diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/kernel/filesystem/ext2/directory_iterator.cpp | 14 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/directory_iterator.hpp | 5 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp | 9 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/filesystem.cpp | 84 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/filesystem.hpp | 29 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/inode.cpp | 79 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/mount_state.hpp | 3 |
7 files changed, 146 insertions, 77 deletions
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.cpp b/kernel/kernel/filesystem/ext2/directory_iterator.cpp index 8475cdd9..3e614f26 100644 --- a/kernel/kernel/filesystem/ext2/directory_iterator.cpp +++ b/kernel/kernel/filesystem/ext2/directory_iterator.cpp @@ -1,6 +1,8 @@ #include <kernel/filesystem/ext2/directory_iterator.hpp> +#include <kernel/filesystem/ext2/filesystem.hpp> #include <kernel/filesystem/ext2/inode.hpp> +#include <kernel/filesystem/ext2/mount_state.hpp> #include <kapi/system.hpp> @@ -10,8 +12,10 @@ namespace kernel::filesystem::ext2 { - directory_iterator::directory_iterator(inode const & inode) - : m_inode(&inode) + directory_iterator::directory_iterator(inode const & inode, filesystem const & filesystem, mount_state & state) + : m_inode{&inode} + , m_filesystem{&filesystem} + , m_state{&state} , m_buffer{sizeof(value_type)} { if (!inode.is_directory()) @@ -33,7 +37,7 @@ namespace kernel::filesystem::ext2 auto directory_iterator::operator++() -> directory_iterator & { - if (!m_inode || m_file_offset >= m_inode->size()) + if (!m_inode || m_file_offset >= m_filesystem->data_size(*m_inode, *m_state)) { m_inode = nullptr; return *this; @@ -60,7 +64,7 @@ namespace kernel::filesystem::ext2 auto directory_iterator::read() -> void { - if (auto result = m_inode->read(m_buffer, m_file_offset); !result) + if (auto result = m_filesystem->do_read(*m_inode, m_buffer, m_file_offset, *m_state); !result) { kapi::system::panic("[FS:ext2] failed to read directory entry", result.error()); } @@ -73,7 +77,7 @@ namespace kernel::filesystem::ext2 { m_buffer.resize(m_buffer.size() + remainder); - if (auto result = m_inode->read(m_buffer, m_file_offset); !result) + if (auto result = m_filesystem->do_read(*m_inode, m_buffer, m_file_offset, *m_state); !result) { kapi::system::panic("[FS:ext2] failed to read directory entry", result.error()); } diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.hpp b/kernel/kernel/filesystem/ext2/directory_iterator.hpp index 92dd2b51..4e2fd9d8 100644 --- a/kernel/kernel/filesystem/ext2/directory_iterator.hpp +++ b/kernel/kernel/filesystem/ext2/directory_iterator.hpp @@ -3,6 +3,7 @@ #include <kernel/filesystem/ext2/inode.hpp> #include <kernel/filesystem/ext2/linked_directory_entry.hpp> +#include <kernel/filesystem/ext2/mount_state.hpp> #include <kstd/memory.hpp> #include <kstd/units.hpp> @@ -23,7 +24,7 @@ namespace kernel::filesystem::ext2 using reference = linked_directory_entry const &; constexpr directory_iterator() = default; - explicit directory_iterator(inode const & inode); + directory_iterator(inode const & inode, filesystem const & filesystem, mount_state & state); auto operator*() const -> reference; @@ -39,6 +40,8 @@ namespace kernel::filesystem::ext2 auto read() -> void; kstd::observer_ptr<inode const> m_inode{}; + kstd::observer_ptr<filesystem const> m_filesystem{}; + kstd::observer_ptr<mount_state> m_state{}; kstd::bytes m_file_offset{}; kstd::vector<std::byte> m_buffer{}; }; diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp b/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp index 20ef7009..2d817e0b 100644 --- a/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp +++ b/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp @@ -4,6 +4,7 @@ #include <kernel/filesystem/device_inode.hpp> #include <kernel/filesystem/ext2/filesystem.hpp> #include <kernel/filesystem/ext2/inode.hpp> +#include <kernel/filesystem/ext2/mount_state.hpp> #include <kernel/filesystem/mount.hpp> #include <kernel/test_support/filesystem/storage_boot_module_fixture.hpp> @@ -37,12 +38,14 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, auto root = (*mount)->root_dentry()->inode(); auto const & root_ext2_inode = static_cast<kernel::filesystem::ext2::inode const &>(*root); + auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>((*mount)->driver_data()); + WHEN("iterating from begin to the default-constructed end sentinel") { auto names = std::vector<std::string>{}; auto guard = 0; - for (auto it = kernel::filesystem::ext2::directory_iterator{root_ext2_inode}; + for (auto it = kernel::filesystem::ext2::directory_iterator{root_ext2_inode, *fs, *mount_state}; it != kernel::filesystem::ext2::directory_iterator{}; ++it) { REQUIRE(guard++ < 64); // fails loudly on a non-terminating loop rather than hanging the suite @@ -82,7 +85,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, auto root = (*mount)->root_dentry()->inode(); auto const & root_ext2_inode = static_cast<kernel::filesystem::ext2::inode const &>(*root); - auto original = kernel::filesystem::ext2::directory_iterator{root_ext2_inode}; + auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>((*mount)->driver_data()); + + auto original = kernel::filesystem::ext2::directory_iterator{root_ext2_inode, *fs, *mount_state}; auto const first_name = std::string{&original->name_start, original->name_len}; WHEN("the iterator is copied, then only the original is advanced") diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 49b78d0c..bf977c46 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -17,12 +17,14 @@ #include <kstd/cstring.hpp> #include <kstd/memory.hpp> +#include <kstd/mutex.hpp> #include <kstd/result.hpp> #include <kstd/span.hpp> #include <kstd/system_error.hpp> #include <kstd/units.hpp> #include <kstd/vector.hpp> +#include <algorithm> #include <array> #include <cstddef> #include <cstdint> @@ -143,7 +145,7 @@ namespace kernel::filesystem::ext2 auto filesystem::mount(inode_ptr const & backing_inode) -> kstd::result<mount_result> { auto const mount_state = kstd::make_shared<struct mount_state>(); - auto & [superblock, block_group_descriptors, backing] = *mount_state; + auto & [superblock, block_group_descriptors, backing, _] = *mount_state; backing = backing_inode; @@ -205,7 +207,9 @@ namespace kernel::filesystem::ext2 return kstd::failure(vfs_errc::not_mounted); } - for (auto it = directory_iterator{*directory}; it != directory_iterator{}; ++it) + auto guard = kstd::lock_guard{mount_state->lock}; + + for (auto it = directory_iterator{*directory, *this, *mount_state}; it != directory_iterator{}; ++it) { if (it->inode == 0) { @@ -253,6 +257,7 @@ namespace kernel::filesystem::ext2 return kstd::failure(vfs_errc::invalid_driver_data); } + auto guard = kstd::lock_guard{mount_state->lock}; auto batch = write_batch{*this, *mount_state}; auto maybe_inode_number = allocate_inode(*mount_state, batch); @@ -295,7 +300,7 @@ namespace kernel::filesystem::ext2 { return kstd::failure(result.error()); } - created->set_size(block_size(*mount_state)); + set_data_size(*created, block_size(*mount_state), *mount_state); } batch.mark_inode(*created); @@ -579,7 +584,7 @@ namespace kernel::filesystem::ext2 auto filesystem::allocate_blocks(size_t count, mount_state & state, write_batch & batch) -> kstd::result<kstd::vector<uint32_t>> { - auto & [superblock, block_group_descriptors, backing_inode] = state; + auto & [superblock, block_group_descriptors, backing_inode, _] = state; if (block_group_descriptors.empty() || count > superblock.free_blocks_count) { @@ -649,9 +654,78 @@ namespace kernel::filesystem::ext2 return allocated_blocks; } + auto filesystem::data_size(inode const & inode, mount_state const & state) const -> kstd::bytes + { + uint64_t size = inode.data().size; + + if (revision_level(state) > constants::good_old_revision && inode.is_regular()) + { + size |= static_cast<uint64_t>(inode.data().dir_acl) << 32; + } + + return kstd::bytes{size}; + } + + auto filesystem::set_data_size(inode & inode, kstd::bytes new_size, mount_state const & state) const -> void + { + if (revision_level(state) > constants::good_old_revision && inode.is_regular()) + { + inode.data().dir_acl = static_cast<uint32_t>(new_size.value >> 32); + } + inode.data().size = static_cast<uint32_t>(new_size.value); + } + + auto filesystem::do_read(inode const & inode, std::span<std::byte> buffer, kstd::bytes offset, + mount_state const & state) const -> kstd::result<kstd::bytes> + { + auto const max_readable = data_size(inode, state) - offset; + auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable); + + auto const block_size = this->block_size(state); + auto block_index = offset / block_size; + auto in_block_offset = offset % block_size; + + auto bytes_read = 0_B; + + while (bytes_read < requested_size) + { + auto const block_number = inode_block_number(block_index, inode, state); + if (!block_number) + { + break; + } + + auto const bytes_to_read = std::min(requested_size - bytes_read, block_size - in_block_offset); + if (block_number == 0) + { + kstd::libc::memset(buffer.data() + bytes_read, 0, bytes_to_read.value); + bytes_read += bytes_to_read; + } + else + { + auto const block_start_offset = block_number.value() * block_size; + auto const read_offset = block_start_offset + in_block_offset; + + auto const read_result = + state.backing_inode->read(buffer.subspan(bytes_read.value, bytes_to_read.value), read_offset); + if (!read_result) + { + return kstd::failure(read_result.error()); + } + + bytes_read += read_result.value(); + } + + block_index++; + in_block_offset = 0_B; + } + + return bytes_read; + } + auto filesystem::allocate_inode(mount_state & state, write_batch & batch) -> kstd::result<uint32_t> { - auto & [superblock, block_group_descriptors, _] = state; + auto & [superblock, block_group_descriptors, _, __] = state; if (block_group_descriptors.empty() || superblock.free_inodes_count == 0) { diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index 1bce8d71..6d5720a2 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACHOS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP #include <kernel/filesystem/ext2/block_group_descriptor.hpp> +#include <kernel/filesystem/ext2/directory_iterator.hpp> #include <kernel/filesystem/ext2/inode.hpp> #include <kernel/filesystem/ext2/linked_directory_entry.hpp> #include <kernel/filesystem/ext2/mount_state.hpp> @@ -195,7 +196,33 @@ namespace kernel::filesystem::ext2 write_batch & batch) -> kstd::result<void>; private: - friend struct write_batch; + friend write_batch; + friend inode; + friend directory_iterator; + + //! Perform the actual read of the requested data. + //! + //! @param inode The inode whose data to read. + //! @param buffer The buffer to read into. + //! @param offset The offset inside the inode's data to start at. + //! @param state The driver state to operate on. + //! @return The number of bytes read on success, an error otherwise. + [[nodiscard]] auto do_read(inode const & inode, std::span<std::byte> buffer, kstd::bytes offset, + mount_state const & state) const -> kstd::result<kstd::bytes>; + + //! Determine the size of an inode's data on disk. + //! + //! @param inode The inode whose data size to determine. + //! @param state The driver state to operate on. + //! @return The size of the data referenced by the inode. + [[nodiscard]] auto data_size(inode const & inode, mount_state const & state) const -> kstd::bytes; + + //! Set the size of an inode's data on disk. + //! + //! @param inode The inode whose data size to set. + //! @param new_size The size to set. + //! @param state The driver state to operate on. + auto set_data_size(inode & inode, kstd::bytes new_size, mount_state const & state) const -> void; //! Allocate a single inode in the file system. //! diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp index 56174638..075d6c5f 100644 --- a/kernel/kernel/filesystem/ext2/inode.cpp +++ b/kernel/kernel/filesystem/ext2/inode.cpp @@ -13,6 +13,7 @@ #include <kstd/cstring.hpp> #include <kstd/memory.hpp> +#include <kstd/mutex.hpp> #include <kstd/result.hpp> #include <kstd/units.hpp> @@ -33,11 +34,10 @@ namespace kernel::filesystem::ext2 auto inode::read(std::span<std::byte> buffer, kstd::bytes offset) const -> kstd::result<kstd::bytes> { - auto const max_readable = this->size() - offset; - auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable); - if (is_symbolic_link() && this->size() <= kstd::bytes{sizeof(m_data.block)}) { + auto const max_readable = this->size() - offset; + auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable); auto inline_target = reinterpret_cast<uint8_t const *>(m_data.block.data()); kstd::libc::memcpy(buffer.data(), inline_target + offset, requested_size.value); return requested_size; @@ -61,46 +61,8 @@ namespace kernel::filesystem::ext2 return kstd::failure(vfs_errc::not_mounted); } - auto block_size = (*filesystem)->block_size(**state); - auto block_index = offset / block_size; - auto in_block_offset = offset % block_size; - - auto bytes_read = 0_B; - - while (bytes_read < requested_size) - { - auto const block_number = (*filesystem)->inode_block_number(block_index, *this, **state); - if (!block_number) - { - break; - } - - auto const bytes_to_read = std::min(requested_size - bytes_read, block_size - in_block_offset); - if (block_number == 0) - { - kstd::libc::memset(buffer.data() + bytes_read, 0, bytes_to_read.value); - bytes_read += bytes_to_read; - } - else - { - auto const block_start_offset = block_number.value() * block_size; - auto const read_offset = block_start_offset + in_block_offset; - - auto const read_result = - backing_inode->read(buffer.subspan(bytes_read.value, bytes_to_read.value), read_offset); - if (!read_result) - { - return kstd::failure(read_result.error()); - } - - bytes_read += read_result.value(); - } - - block_index++; - in_block_offset = 0_B; // After the first block, we always start at the beginning of the block - } - - return bytes_read; + auto guard = kstd::lock_guard{(*state)->lock}; + return (*filesystem)->do_read(*this, buffer, offset, **state); } auto inode::write(std::span<std::byte const> buffer, kstd::bytes offset) -> kstd::result<kstd::bytes> @@ -113,8 +75,6 @@ namespace kernel::filesystem::ext2 // TODO check maximum file size of filesystem // TODO handle sparse files - auto const new_inode_size = std::max(this->size(), offset + kstd::bytes{buffer.size()}); - auto filesystem = get_filesystem(); if (!filesystem) { @@ -133,13 +93,15 @@ namespace kernel::filesystem::ext2 return kstd::failure(vfs_errc::not_mounted); } - auto block_size = (*filesystem)->block_size(**state); + auto guard = kstd::lock_guard{(*state)->lock}; + auto batch = write_batch{**filesystem, **state}; + auto const new_inode_size = std::max((*filesystem)->data_size(*this, **state), offset + kstd::bytes{buffer.size()}); + + auto block_size = (*filesystem)->block_size(**state); auto const current_block_count = (*filesystem)->block_count(*this, **state); auto const max_new_inode_size_without_new_blocks = current_block_count * block_size; - auto batch = write_batch{**filesystem, **state}; - if (new_inode_size > max_new_inode_size_without_new_blocks) { auto const additional_blocks_needed = @@ -183,7 +145,8 @@ namespace kernel::filesystem::ext2 in_block_offset = 0_B; } - set_size(std::max(this->size(), offset + bytes_written)); + (*filesystem) + ->set_data_size(*this, std::max((*filesystem)->data_size(*this, **state), offset + bytes_written), **state); batch.mark_inode(*this); return bytes_written; } @@ -297,12 +260,8 @@ namespace kernel::filesystem::ext2 } auto state = static_pointer_cast<mount_state>(*driver_data); - - if ((*filesystem)->revision_level(*state) > constants::good_old_revision && is_regular()) - { - m_data.dir_acl = static_cast<uint32_t>(new_size.value >> 32); - } - m_data.size = static_cast<uint32_t>(new_size.value); + auto guard = kstd::lock_guard{state->lock}; + return (*filesystem)->set_data_size(*this, new_size, *state); } [[nodiscard]] auto inode::data() -> inode_data & @@ -317,8 +276,6 @@ namespace kernel::filesystem::ext2 auto inode::size() const -> kstd::bytes { - uint64_t size = m_data.size; - auto filesystem = get_filesystem(); if (!filesystem) { @@ -331,12 +288,8 @@ namespace kernel::filesystem::ext2 return 0_B; } - if ((*filesystem)->revision_level(**state) > constants::good_old_revision && is_regular()) - { - size |= static_cast<uint64_t>(m_data.dir_acl) << 32; - } - - return kstd::bytes{size}; + auto guard = kstd::lock_guard{(*state)->lock}; + return (*filesystem)->data_size(*this, **state); } [[nodiscard]] auto inode::number() const -> uint32_t diff --git a/kernel/kernel/filesystem/ext2/mount_state.hpp b/kernel/kernel/filesystem/ext2/mount_state.hpp index e8ae8fd7..ba705382 100644 --- a/kernel/kernel/filesystem/ext2/mount_state.hpp +++ b/kernel/kernel/filesystem/ext2/mount_state.hpp @@ -5,6 +5,8 @@ #include <kernel/filesystem/ext2/superblock.hpp> #include <kernel/filesystem/inode.hpp> +#include <kapi/tracked_mutex.hpp> + #include <kstd/memory.hpp> #include <kstd/vector.hpp> @@ -16,6 +18,7 @@ namespace kernel::filesystem::ext2 struct superblock superblock{}; kstd::vector<block_group_descriptor> block_group_descriptors{}; kstd::shared_ptr<kernel::filesystem::inode> backing_inode{}; + mutable kapi::tracked_mutex lock{}; }; } // namespace kernel::filesystem::ext2 |
