diff options
| -rw-r--r-- | kernel/kernel/filesystem/ext2/error.cpp | 3 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/error.hpp | 3 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/filesystem.cpp | 402 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/filesystem.hpp | 93 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/filesystem.tests.cpp | 6 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/inode.cpp | 4 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/inode.tests.cpp | 9 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/linked_directory_entry.hpp | 3 | ||||
| -rw-r--r-- | libs/kstd/kstd/span.hpp | 57 |
9 files changed, 389 insertions, 191 deletions
diff --git a/kernel/kernel/filesystem/ext2/error.cpp b/kernel/kernel/filesystem/ext2/error.cpp index ffe49460..73921379 100644 --- a/kernel/kernel/filesystem/ext2/error.cpp +++ b/kernel/kernel/filesystem/ext2/error.cpp @@ -42,6 +42,8 @@ namespace kernel::filesystem::ext2 return "not enough inodes"; case ext2_errc::unsupported_features_present: return "unsupported features present"; + case ext2_errc::name_too_long: + return "name too long"; default: return "unknown ext2 error"; }; @@ -54,6 +56,7 @@ namespace kernel::filesystem::ext2 { case ext2_errc::invalid_magic_number: case ext2_errc::invalid_root_inode: + case ext2_errc::name_too_long: if (condition.category() == kernel::filesystem::vfs_category()) { return condition.value() == static_cast<int>(kernel::filesystem::vfs_errc::invalid_filesystem); diff --git a/kernel/kernel/filesystem/ext2/error.hpp b/kernel/kernel/filesystem/ext2/error.hpp index 59df1669..edd0d017 100644 --- a/kernel/kernel/filesystem/ext2/error.hpp +++ b/kernel/kernel/filesystem/ext2/error.hpp @@ -21,7 +21,8 @@ namespace kernel::filesystem::ext2 failed_to_read_block_group_descriptors, not_enough_free_blocks, not_enough_inodes, - unsupported_features_present + unsupported_features_present, + name_too_long, }; [[nodiscard]] auto ext2_category() noexcept -> kstd::error_category const &; diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 92945eb2..45d4efeb 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -16,6 +16,7 @@ #include <kstd/cstring.hpp> #include <kstd/memory.hpp> #include <kstd/result.hpp> +#include <kstd/span.hpp> #include <kstd/system_error.hpp> #include <kstd/units.hpp> #include <kstd/vector.hpp> @@ -34,43 +35,53 @@ using namespace kstd::units_literals; namespace kernel::filesystem::ext2 { + //! Check that this driver class is stateless. + static_assert(sizeof(kernel::filesystem::filesystem) == sizeof(kernel::filesystem::ext2::filesystem)); + namespace { - [[nodiscard]] auto bitmap_is_set(kstd::vector<uint8_t> const & bitmap, size_t index) -> bool + //! Check if the given bit has the bit at the given index set. + //! + //! @warning This function will panic if the bit index is out-of-bounds for the given bitmap. + //! + //! @param bitmap The bitmap to check. + //! @param index The index of the bit to check. + [[nodiscard]] auto bitmap_is_set(kstd::vector<std::byte> const & bitmap, size_t index) -> bool { auto const byte_index = index / 8; auto const bit_index = index % 8; - return (bitmap.at(byte_index) & static_cast<uint8_t>(1u << bit_index)) != 0; + return (bitmap.at(byte_index) & static_cast<std::byte>(1u << bit_index)) != std::byte{}; } - auto bitmap_set(kstd::vector<uint8_t> & bitmap, size_t index) -> void + //! Set the bit at the given index in the given bitmap. + //! + //! @warning This function will panic if the bit index is out-of-bounds for the given bitmap. + //! + //! @param bitmap The bitmap to manipulate. + //! @param index The index of the bit to set. + auto bitmap_set(kstd::vector<std::byte> & bitmap, size_t index) -> void { auto const byte_index = index / 8; auto const bit_index = index % 8; - bitmap.at(byte_index) |= static_cast<uint8_t>(1u << bit_index); + bitmap.at(byte_index) |= static_cast<std::byte>(1u << bit_index); } - auto supported_incompatible_features = incompatible_features::file_types_in_directory_entries; - auto supported_ro_compatible_features = read_only_compatible_features::sparse_superblock_copies | // - read_only_compatible_features::large_file_support; - } // namespace + //! The set of "incompatible" Extended Filesystem features supported by this driver. + constexpr auto supported_incompatible_features = incompatible_features::file_types_in_directory_entries; - static_assert(sizeof(kernel::filesystem::filesystem) == sizeof(kernel::filesystem::ext2::filesystem)); + //! The set of "read-only" Extended Filesystem features supported by this driver. + constexpr auto supported_ro_compatible_features = read_only_compatible_features::sparse_superblock_copies | // + read_only_compatible_features::large_file_support; - auto filesystem::indirect_levels(mount_state const & state) const -> std::array<indirect_level, 3> - { - return { - {{constants::singly_indirect_block_index, block_numbers_per_singly_indirect_block(state)}, - {constants::doubly_indirect_block_index, block_numbers_per_doubly_indirect_block(state)}, - {constants::triply_indirect_block_index, block_numbers_per_triply_indirect_block(state)}} - }; - } + //! A set of permissions allowing all access to everyone. + constexpr auto no_access_restrictions = std::uint16_t{0x01FF}; + } // namespace auto filesystem::probe(inode_ptr const & backing_inode) const -> kstd::result<std::uint32_t> { auto superblock = ext2::superblock{}; - auto bytes = std::as_writable_bytes(std::span{&superblock, 1}); - auto read_result = backing_inode->read(bytes, constants::superblock_offset); + auto const bytes = kstd::raw_bytes(superblock); + auto const read_result = backing_inode->read(bytes, constants::superblock_offset); if (!read_result) { @@ -93,12 +104,12 @@ namespace kernel::filesystem::ext2 auto filesystem::mount(inode_ptr const & backing_inode) -> kstd::result<mount_result> { - auto mount_state = kstd::make_shared<struct mount_state>(); + auto const mount_state = kstd::make_shared<struct mount_state>(); auto & [superblock, block_group_descriptors, backing] = *mount_state; backing = backing_inode; - auto superblock_bytes = std::as_writable_bytes(std::span{&superblock, 1}); + auto const superblock_bytes = kstd::raw_bytes(superblock); if (auto read_result = backing->read(superblock_bytes, constants::superblock_offset); !read_result) { return kstd::failure(read_result.error()); @@ -113,7 +124,7 @@ namespace kernel::filesystem::ext2 auto const num_block_groups = (superblock.blocks_count + blocks_per_group - 1) / blocks_per_group; block_group_descriptors = kstd::vector<block_group_descriptor>(num_block_groups); - auto block_group_descriptors_bytes = std::as_writable_bytes(std::span{block_group_descriptors}); + auto const block_group_descriptors_bytes = kstd::raw_bytes(block_group_descriptors); if (auto read_result = backing->read(block_group_descriptors_bytes, block_group_descriptor_table_offset(*mount_state)); !read_result) @@ -121,7 +132,7 @@ namespace kernel::filesystem::ext2 return kstd::failure(read_result.error()); } - auto root = read_inode(constants::root_inode_number, *mount_state); + auto const root = read_inode(constants::root_inode_number, *mount_state); if (!root) { @@ -139,27 +150,27 @@ namespace kernel::filesystem::ext2 auto filesystem::lookup(inode_ptr const & parent, std::string_view name, driver_data_ptr driver_data) const -> kstd::result<inode_ptr> { - if (!parent) + auto const ext2_parent = static_pointer_cast<inode>(parent); + if (!ext2_parent) { return kstd::failure(vfs_errc::invalid_inode); } - if (!parent->is_directory()) + if (!ext2_parent->is_directory()) { return kstd::failure(vfs_errc::not_a_directory); } - auto mount_state = static_pointer_cast<struct mount_state>(driver_data); + auto const mount_state = static_pointer_cast<struct mount_state>(driver_data); if (!mount_state) { - return kstd::failure(vfs_errc::invalid_driver_data); + return kstd::failure(vfs_errc::not_mounted); } - auto * ext2_parent = static_cast<inode *>(parent.get()); auto const & inode_data = ext2_parent->data(); - kstd::vector<uint8_t> buffer(block_size(*mount_state).value); + auto buffer = kstd::vector<std::byte>{block_size(*mount_state).value}; - for (uint32_t i = 0; i < inode_block_count(inode_data, *mount_state); ++i) + for (auto i = 0u; i < block_count(inode_data, *mount_state); ++i) { auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data, *mount_state); if (!global_block_number) @@ -167,24 +178,24 @@ namespace kernel::filesystem::ext2 return kstd::failure(global_block_number.error()); } - if (auto read_result = read_block(*global_block_number, buffer.data(), *mount_state); !read_result) + if (auto result = read_block(*global_block_number, buffer.data(), *mount_state); !result) { - return kstd::failure(read_result.error()); + return kstd::failure(result.error()); } - auto const * entry = reinterpret_cast<linked_directory_entry const *>(buffer.data()); - auto bytes_read = 0_B; + auto entry = reinterpret_cast<linked_directory_entry const *>(buffer.data()); + auto read = 0_B; - while (bytes_read < block_size(*mount_state) && entry->inode != 0) + while (read < block_size(*mount_state) && entry->inode != 0) { - auto const entry_name = std::string_view{entry->name.data(), entry->name_len}; + auto const entry_name = std::string_view{&entry->name_start, entry->name_len}; if (entry_name == name) { return read_inode(entry->inode, *mount_state); } - bytes_read += kstd::bytes{entry->rec_len}; - entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + bytes_read); + read += kstd::bytes{entry->rec_len}; + entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + read); } } @@ -205,19 +216,19 @@ namespace kernel::filesystem::ext2 return kstd::failure(vfs_errc::not_a_directory); } - auto is_device = kapi::filesystem::is_device(type); + auto const is_device = kapi::filesystem::is_device(type); if (is_device && !raw_device) { return kstd::failure(vfs_errc::invalid_argument); } - auto * ext2_parent = static_cast<inode *>(parent.get()); + auto const ext2_parent = static_pointer_cast<inode>(parent); if (!ext2_parent) { return kstd::failure(vfs_errc::invalid_argument); } - auto mount_state = static_pointer_cast<struct mount_state>(driver_data); + auto const mount_state = static_pointer_cast<struct mount_state>(driver_data); if (!mount_state) { return kstd::failure(vfs_errc::invalid_driver_data); @@ -231,65 +242,92 @@ namespace kernel::filesystem::ext2 auto inode_number = maybe_inode_number.value(); - inode_data new_inode_data{}; + auto data = inode_data{}; - uint16_t const full_access_mode = 0x01FF; // TODO use correct access rights - new_inode_data.mode = map_vfs_inode_type_into_inode_mode(type, full_access_mode); - new_inode_data.size = 0; - new_inode_data.dir_acl = 0; - new_inode_data.links_count = 1; - new_inode_data.blocks = 0; - new_inode_data.block.fill(0); + // TODO: use correct access rights + data.mode = map_vfs_inode_type_into_inode_mode(type, no_access_restrictions); + data.size = 0; + data.dir_acl = 0; + data.links_count = 1; + data.blocks = 0; + data.block.fill(0); if (is_device) { - new_inode_data.block[0] = static_cast<std::uint32_t>(raw_device->minor) | // - (static_cast<std::uint32_t>(raw_device->major) << 8); + data.block[0] = static_cast<std::uint32_t>(raw_device->minor) | // + (static_cast<std::uint32_t>(raw_device->major) << 8); } - // TODO preallocate blocks depending on type and m_superblock.prealloc_dir_blocks or m_superblock.prealloc_blocks; + auto created = kstd::make_shared<inode>(inode_number, data); + created->set_owning_mount(ext2_parent->owning_mount().lock()); - if (auto result = add_directory_entry(ext2_parent, name, inode_number, new_inode_data.mode, *mount_state); !result) + // TODO: preallocate blocks depending on type and m_superblock.prealloc_dir_blocks or m_superblock.prealloc_blocks; + if (auto result = add_directory_entry(*ext2_parent, name, *created, data.mode, *mount_state); !result) { return kstd::failure(result.error()); } - auto new_inode = kstd::make_shared<inode>(inode_number, new_inode_data); - new_inode->set_owning_mount(ext2_parent->owning_mount().lock()); - - if (new_inode->is_directory()) + if (created->is_directory()) { // TODO increment used_dirs_count in block group descriptor - if (auto result = init_directory(new_inode, ext2_parent, *mount_state); !result) + if (auto result = init_directory(*created, *ext2_parent, *mount_state); !result) { return kstd::failure(result.error()); } } - if (auto write_result = write_inode(inode_number, new_inode->data(), *mount_state); !write_result) + if (auto result = write_inode(inode_number, created->data(), *mount_state); !result) { - return kstd::failure(write_result.error()); + return kstd::failure(result.error()); } - return new_inode; + return created; + } + + auto filesystem::block_size(mount_state const & state) const -> kstd::bytes + { + return kstd::bytes{constants::base_block_size.value << state.superblock.log_block_size}; + } + + auto filesystem::revision_level(mount_state const & state) const -> uint32_t + { + return state.superblock.rev_level; + } + + auto filesystem::inode_size(mount_state const & state) const -> kstd::bytes + { + return kstd::bytes(revision_level(state) == constants::good_old_revision ? 128 : state.superblock.inode_size); + } + + auto filesystem::block_count(inode_data const & data, mount_state const & state) const -> uint32_t + { + return data.blocks / (2 << state.superblock.log_block_size); } - auto filesystem::add_directory_entry(inode * ext2_parent, std::string_view name, uint32_t child_inode_number, - [[maybe_unused]] uint16_t mode, mount_state & state) -> kstd::result<void> + auto filesystem::indirect_levels(mount_state const & state) const -> std::array<indirect_level, 3> { - auto const last_block_index = inode_block_count(ext2_parent->data(), state) - 1; - auto const global_block = - map_inode_block_index_to_global_block_number(last_block_index, ext2_parent->data(), state); + return { + {{constants::singly_indirect_block_index, block_numbers_per_singly_indirect_block(state)}, + {constants::doubly_indirect_block_index, block_numbers_per_doubly_indirect_block(state)}, + {constants::triply_indirect_block_index, block_numbers_per_triply_indirect_block(state)}} + }; + } + + auto filesystem::add_directory_entry(inode & directory, std::string_view name, inode & child, std::uint16_t mode, + mount_state & state) -> kstd::result<void> + { + auto const last_block_index = block_count(directory.data(), state) - 1; + auto const global_block = map_inode_block_index_to_global_block_number(last_block_index, directory.data(), state); if (!global_block) { return kstd::failure(global_block.error()); } - kstd::vector<uint8_t> buffer(block_size(state).value); - if (auto read_result = read_block(*global_block, buffer.data(), state); !read_result) + auto buffer = kstd::vector<std::byte>{block_size(state).value}; + if (auto result = read_block(*global_block, buffer.data(), state); !result) { - return kstd::failure(read_result.error()); + return kstd::failure(result.error()); } // TODO handle "gaps" in directory entries (e.g., after deletions) in the current implementation, we only @@ -298,6 +336,11 @@ namespace kernel::filesystem::ext2 while (true) { auto const * entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + offset); + auto f = std::string_view{&entry->name_start, entry->name_len}; + if (f.length() == 255) + { + break; + } if (offset + kstd::bytes{entry->rec_len} >= block_size(state)) { break; @@ -315,84 +358,137 @@ namespace kernel::filesystem::ext2 if (kstd::bytes{last_entry->rec_len} - last_entry_actual_len >= needed_rec_len) { last_entry->rec_len = last_entry_actual_len.value; - write_directory_entry_to_buffer(buffer.data(), offset + last_entry_actual_len, child_inode_number, name, - file_type, state); + auto entry_offset = offset + last_entry_actual_len; + auto entry = write_directory_entry_to_buffer(buffer, entry_offset, child, name, file_type); + if (!entry) + { + return kstd::failure(entry.error()); + } + + auto remainder = block_size(state) - entry_offset; + (*entry)->rec_len = remainder.value; + return global_block.and_then([&](auto block_number) { return write_block(block_number, buffer.data(), state); }) .transform([](auto) {}); } else { - if (!ext2_parent->append_blocks(1)) + if (!directory.append_blocks(1)) { return kstd::failure(ext2_errc::not_enough_free_blocks); } - auto const new_block_index = inode_block_count(ext2_parent->data(), state) - 1; + auto const new_block_index = block_count(directory.data(), state) - 1; auto const new_global_block = - map_inode_block_index_to_global_block_number(new_block_index, ext2_parent->data(), state); + map_inode_block_index_to_global_block_number(new_block_index, directory.data(), state); if (!new_global_block) { return kstd::failure(new_global_block.error()); } kstd::libc::memset(buffer.data(), 0, block_size(state).value); - write_directory_entry_to_buffer(buffer.data(), 0_B, child_inode_number, name, file_type, state); - if (auto update_result = new_global_block.and_then( + auto entry = write_directory_entry_to_buffer(buffer, 0_B, child, name, file_type); + if (!entry) + { + return kstd::failure(entry.error()); + } + + auto remainder = block_size(state); + (*entry)->rec_len = remainder.value; + + if (auto result = new_global_block.and_then( [&](auto block_number) { return write_block(block_number, buffer.data(), state); }); - !update_result) + !result) { - return kstd::failure(update_result.error()); + return kstd::failure(result.error()); } } - ext2_parent->data_mutable().size = (inode_block_count(ext2_parent->data(), state) * block_size(state)).value; - return write_inode(ext2_parent->number(), ext2_parent->data(), state); + auto size = block_count(directory.data(), state) * block_size(state); + directory.data_mutable().size = size.value; + return write_inode(directory.number(), directory.data(), state); } - auto filesystem::init_directory(kstd::shared_ptr<kernel::filesystem::ext2::inode> const & new_inode, - kernel::filesystem::ext2::inode * parent_inode, mount_state & state) - -> kstd::result<void> + auto filesystem::init_directory(inode & directory, inode & parent, mount_state & state) -> kstd::result<void> { - if (!new_inode->append_blocks(1)) + if (!directory.append_blocks(1)) { return kstd::failure(ext2_errc::not_enough_free_blocks); } - auto const global_block = map_inode_block_index_to_global_block_number(0, new_inode->data(), state); + auto const global_block = map_inode_block_index_to_global_block_number(0, directory.data(), state); if (!global_block) { return kstd::failure(global_block.error()); } - kstd::vector<uint8_t> buffer(block_size(state).value, 0); - write_directory_entry_to_buffer(buffer.data(), 0_B, new_inode->number(), ".", 2, state); - auto const dot_actual_len = static_cast<kstd::bytes>((8u + 1 + 3u) & ~3u); - auto * entry = reinterpret_cast<linked_directory_entry *>(buffer.data()); - entry->rec_len = dot_actual_len.value; + auto buffer = kstd::vector<std::byte>{block_size(state).value}; + auto dot = write_directory_entry_to_buffer(buffer, 0_B, directory, ".", kapi::filesystem::file_type::directory); - write_directory_entry_to_buffer(buffer.data(), dot_actual_len, parent_inode->number(), "..", 2, state); + if (!dot) + { + return kstd::failure(dot.error()); + } + + auto dot_dot_offset = kstd::bytes{(*dot)->rec_len}; + auto dot_dot = + write_directory_entry_to_buffer(buffer, dot_dot_offset, parent, "..", kapi::filesystem::file_type::directory); + if (!dot_dot) + { + return kstd::failure(dot_dot.error()); + } + auto remainder = block_size(state) - dot_dot_offset; + (*dot_dot)->rec_len = remainder.value; // '..' inside the new dir counts as an extra hard link to the parent - auto & parent_inode_data = parent_inode->data_mutable(); + auto & parent_inode_data = parent.data_mutable(); parent_inode_data.links_count++; - return write_inode(parent_inode->number(), parent_inode_data, state) + return write_inode(parent.number(), parent_inode_data, state) .and_then([&]() { return global_block; }) .and_then([&](auto block_number) { return write_block(block_number, buffer.data(), state); }) .transform([](auto) {}); } - auto filesystem::write_directory_entry_to_buffer(uint8_t * block_buffer, kstd::bytes offset, - uint32_t child_inode_number, std::string_view name, - uint8_t file_type, mount_state & state) -> void + auto filesystem::write_directory_entry_to_buffer(std::span<std::byte> buffer, kstd::bytes offset, inode & target, + std::string_view name, kapi::filesystem::file_type type) + -> kstd::result<linked_directory_entry *> { - auto const name_len = static_cast<uint8_t>(name.size()); - auto * entry = reinterpret_cast<linked_directory_entry *>(block_buffer + offset); - entry->inode = child_inode_number; - entry->rec_len = static_cast<uint16_t>(block_size(state) - offset); - entry->name_len = name_len; - entry->file_type = file_type; - kstd::libc::memcpy(entry->name.data(), name.data(), name_len); + auto const name_length = static_cast<uint8_t>(name.size()); + auto const record_size = (sizeof(linked_directory_entry) - 1 + name_length + 3u) & ~3u; + if (buffer.size() - offset.value < record_size) + { + // TODO: improve error code. + return kstd::failure(vfs_errc::invalid_argument); + } + + auto * entry = reinterpret_cast<linked_directory_entry *>(buffer.data() + offset); + entry->inode = target.number(); + entry->rec_len = record_size; + entry->name_len = name_length; + entry->file_type = [=] { + switch (type) + { + case kapi::filesystem::file_type::regular: + return 1; + case kapi::filesystem::file_type::directory: + return 2; + case kapi::filesystem::file_type::character_device: + return 3; + case kapi::filesystem::file_type::block_device: + return 4; + case kapi::filesystem::file_type::fifo: + return 5; + case kapi::filesystem::file_type::socket: + return 6; + case kapi::filesystem::file_type::symbolic_link: + return 7; + default: + return 0; + } + }(); + kstd::libc::memcpy(&entry->name_start, name.data(), name_length); + return kstd::success(entry); } auto filesystem::read_inode(uint32_t inode_number, mount_state const & state) const @@ -402,8 +498,7 @@ namespace kernel::filesystem::ext2 { auto new_inode_data = inode_data{}; - if (auto read_result = state.backing_inode->read(as_writable_bytes(std::span{&new_inode_data, 1}), *inode_offset); - !read_result) + if (auto read_result = state.backing_inode->read(kstd::raw_bytes(new_inode_data), *inode_offset); !read_result) { return kstd::failure(read_result.error()); } @@ -423,7 +518,7 @@ namespace kernel::filesystem::ext2 { if (auto inode_offset = calculate_inode_offset(inode_number, state)) { - return state.backing_inode->write(as_bytes(std::span{&data, 1}), *inode_offset).transform([](auto) {}); + return state.backing_inode->write(kstd::raw_bytes(data), *inode_offset).transform([](auto) {}); } else { @@ -471,7 +566,7 @@ namespace kernel::filesystem::ext2 continue; } - kstd::vector<uint8_t> block_bitmap(block_size(state).value, 0); + auto block_bitmap = kstd::vector<std::byte>{block_size(state).value}; if (auto read_result = read_block(block_group_descriptor.block_bitmap, block_bitmap.data(), state); !read_result) { return kstd::failure(read_result.error()); @@ -502,7 +597,7 @@ namespace kernel::filesystem::ext2 // TODO update all block group descriptors if (auto write_result = - backing_inode->write(as_bytes(std::span{&block_group_descriptor, 1}), + backing_inode->write(kstd::raw_bytes(block_group_descriptor), block_group_descriptor_table_offset(state) + block_group_descriptor_index * kstd::size_of(block_group_descriptor)); !write_result) @@ -513,7 +608,7 @@ namespace kernel::filesystem::ext2 } // TODO update all superblock copies - if (auto write_result = backing_inode->write(as_bytes(std::span{&superblock, 1}), constants::superblock_offset); + if (auto write_result = backing_inode->write(kstd::raw_bytes(superblock), constants::superblock_offset); !write_result) { return kstd::failure(write_result.error()); @@ -545,7 +640,7 @@ namespace kernel::filesystem::ext2 continue; } - kstd::vector<uint8_t> inode_bitmap(block_size(state).value, 0); + auto inode_bitmap = kstd::vector<std::byte>{block_size(state).value}; if (auto read_result = read_block(block_group_descriptor.inode_bitmap, inode_bitmap.data(), state); !read_result) { return kstd::failure(read_result.error()); @@ -649,8 +744,7 @@ namespace kernel::filesystem::ext2 auto const block_start_offset = block_number * block_size(state); auto const number_start_address = block_start_offset + index * kstd::size_of<uint32_t>(); - if (auto read_result = - state.backing_inode->read(as_writable_bytes(std::span{&block_number_buffer, 1}), number_start_address); + if (auto read_result = state.backing_inode->read(kstd::raw_bytes(block_number_buffer), number_start_address); !read_result) { return kstd::failure(read_result.error()); @@ -704,7 +798,7 @@ namespace kernel::filesystem::ext2 } else { - if (auto write_result = backing_inode->write(as_bytes(std::span{&block_number, 1}), parent_byte_offset); + if (auto write_result = backing_inode->write(kstd::raw_bytes(block_number), parent_byte_offset); !write_result) { return kstd::failure(write_result.error()); @@ -716,7 +810,7 @@ namespace kernel::filesystem::ext2 if (stride == 1) { - if (auto write_result = backing_inode->write(as_bytes(std::span{&global_block_number, 1}), byte_offset); + if (auto write_result = backing_inode->write(kstd::raw_bytes(global_block_number), byte_offset); !write_result) { return kstd::failure(write_result.error()); @@ -740,7 +834,7 @@ namespace kernel::filesystem::ext2 return kstd::success(); } - auto filesystem::read_block(uint32_t block_number, void * buffer, mount_state & state) const + auto filesystem::read_block(uint32_t block_number, void * buffer, mount_state const & state) const -> kstd::result<kstd::bytes> { auto const block_offset = static_cast<size_t>(block_number) * block_size(state); @@ -762,7 +856,7 @@ namespace kernel::filesystem::ext2 { // TODO update all block group descriptors return state.backing_inode - ->write(as_bytes(std::span{&block_group_descriptor, 1}), + ->write(kstd::raw_bytes(block_group_descriptor), block_group_descriptor_table_offset(state) + block_group_descriptor_index * kstd::size_of(block_group_descriptor)) .transform([](auto) {}); @@ -771,42 +865,56 @@ namespace kernel::filesystem::ext2 auto filesystem::write_superblock(mount_state & state) const -> kstd::result<void> { // TODO update all superblock copies - return state.backing_inode->write(as_bytes(std::span{&state.superblock, 1}), constants::superblock_offset) + return state.backing_inode->write(kstd::raw_bytes(state.superblock), constants::superblock_offset) .transform([](auto) {}); } - auto filesystem::map_inode_mode_to_file_type(uint16_t mode) -> uint8_t + auto filesystem::map_inode_mode_to_file_type(uint16_t mode) -> kapi::filesystem::file_type { switch (mode & constants::mode_mask) { case constants::mode_regular: - return 1; + return kapi::filesystem::file_type::regular; case constants::mode_directory: - return 2; + return kapi::filesystem::file_type::directory; + case constants::mode_block_device: + return kapi::filesystem::file_type::block_device; + case constants::mode_character_device: + return kapi::filesystem::file_type::character_device; case constants::mode_symbolic_link: - return 7; + return kapi::filesystem::file_type::symbolic_link; + case constants::mode_fifo: + return kapi::filesystem::file_type::fifo; + case constants::mode_socket: + return kapi::filesystem::file_type::socket; default: - return 0; + return kapi::filesystem::file_type{0}; } } auto filesystem::map_vfs_inode_type_into_inode_mode(kapi::filesystem::file_type type, uint16_t mode) -> uint16_t { - switch (type) - { - case kapi::filesystem::file_type::directory: - return mode | constants::mode_directory; - case kapi::filesystem::file_type::regular: - return mode | constants::mode_regular; - case kapi::filesystem::file_type::block_device: - return mode | constants::mode_block_device; - case kapi::filesystem::file_type::character_device: - return mode | constants::mode_character_device; - case kapi::filesystem::file_type::symbolic_link: - // return mode | constants::mode_symbolic_link; // TODO implement - default: - kapi::system::panic("[EXT2] Not implemented."); - } + return mode | [=] { + switch (type) + { + case kapi::filesystem::file_type::directory: + return constants::mode_directory; + case kapi::filesystem::file_type::regular: + return constants::mode_regular; + case kapi::filesystem::file_type::block_device: + return constants::mode_block_device; + case kapi::filesystem::file_type::character_device: + return constants::mode_character_device; + case kapi::filesystem::file_type::symbolic_link: + return constants::mode_symbolic_link; // TODO implement + case kapi::filesystem::file_type::fifo: + return constants::mode_fifo; + case kapi::filesystem::file_type::socket: + return constants::mode_socket; + default: + kapi::system::panic("[EXT2] Not implemented."); + } + }(); } auto filesystem::block_numbers_per_block(mount_state const & state) const -> size_t @@ -829,26 +937,6 @@ namespace kernel::filesystem::ext2 return block_numbers_per_doubly_indirect_block(state) * block_numbers_per_block(state); } - auto filesystem::block_size(mount_state const & state) const -> kstd::bytes - { - return kstd::bytes{constants::base_block_size.value << state.superblock.log_block_size}; - } - - auto filesystem::revision_level(mount_state const & state) const -> uint32_t - { - return state.superblock.rev_level; - } - - auto filesystem::inode_size(mount_state const & state) const -> kstd::bytes - { - return kstd::bytes(revision_level(state) == constants::good_old_revision ? 128 : state.superblock.inode_size); - } - - auto filesystem::inode_block_count(inode_data const & data, mount_state const & state) const -> uint32_t - { - return data.blocks / (2 << state.superblock.log_block_size); - } - auto filesystem::update_inode_block_count(inode_data & data, uint32_t delta, mount_state & state) -> void { data.blocks += delta * (2 << state.superblock.log_block_size); diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index 4b5604e5..fec4a283 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -3,6 +3,7 @@ #include <kernel/filesystem/ext2/block_group_descriptor.hpp> #include <kernel/filesystem/ext2/inode.hpp> +#include <kernel/filesystem/ext2/linked_directory_entry.hpp> #include <kernel/filesystem/ext2/mount_state.hpp> #include <kernel/filesystem/ext2/superblock.hpp> #include <kernel/filesystem/filesystem.hpp> @@ -21,6 +22,7 @@ #include <cstddef> #include <cstdint> #include <optional> +#include <span> #include <string_view> #include <type_traits> @@ -49,6 +51,9 @@ namespace kernel::filesystem::ext2 constexpr uint16_t inline mode_symbolic_link = 0xA000; constexpr uint16_t inline mode_block_device = 0x6000; constexpr uint16_t inline mode_character_device = 0x2000; + constexpr uint16_t inline mode_fifo = 0x1000; + constexpr uint16_t inline mode_socket = 0xC000; + } // namespace constants //! Ext2 incompatibility features @@ -76,6 +81,9 @@ namespace kernel::filesystem::ext2 //! The Second Extended Filesystem (ext2) struct filesystem final : kernel::filesystem::filesystem { + //! @name Driver Interface Implementation + //! @{ + [[nodiscard]] auto probe(inode_ptr const & backing_inode) const -> kstd::result<std::uint32_t> override; auto mount(inode_ptr const & backing_inode) -> kstd::result<mount_result> override; @@ -88,23 +96,48 @@ namespace kernel::filesystem::ext2 std::optional<kapi::filesystem::device_number> raw_device = std::nullopt) -> kstd::result<inode_ptr> override; + //! @} + + //! @name Filesystem Information Queries + //! @{ + //! Get the size of a block in the filesystem. //! + //! @param state The driver state to operate on. //! @return The size of a block in bytes. [[nodiscard]] auto block_size(mount_state const & state) const -> kstd::bytes; //! Get the revision level of the filesystem. //! + //! @param state The driver state to operate on. //! @return The revision level. [[nodiscard]] auto revision_level(mount_state const & state) const -> uint32_t; + //! Get the size of an inode in the filesystem. + //! + //! @param state The driver state to operate on. + //! @return The size of an inode in bytes. + [[nodiscard]] auto inode_size(mount_state const & state) const -> kstd::bytes; + + //! Get the number of blocks allocated to an inode. + //! + //! @param data The inode data. + //! @param state The driver state to operate on. + //! @return The number of blocks allocated to the inode. + [[nodiscard]] auto block_count(inode_data const & data, mount_state const & state) const -> uint32_t; + + //! @} + + //! @name Block I/O + //! @{ + //! Read a block from the backing inode into the provided buffer. //! //! @param block_number The number of the block to read. //! @param buffer The buffer to read the block data into. //! @param state The state to operate on. //! @return The number of bytes read. - auto read_block(uint32_t block_number, void * buffer, mount_state & state) const -> kstd::result<kstd::bytes>; + auto read_block(uint32_t block_number, void * buffer, mount_state const & state) const -> kstd::result<kstd::bytes>; //! Write a block of data from the provided buffer to the backing inode. //! @@ -114,6 +147,8 @@ namespace kernel::filesystem::ext2 //! @return The number of bytes written. auto write_block(uint32_t block_number, void const * buffer, mount_state & state) -> kstd::result<kstd::bytes>; + //! @} + //! Allocate a specified number of blocks. //! //! @param count The number of blocks to allocate. @@ -121,18 +156,6 @@ namespace kernel::filesystem::ext2 //! @return A vector of the allocated block numbers. auto allocate_blocks(size_t count, mount_state & state) -> kstd::result<kstd::vector<uint32_t>>; - //! Get the size of an inode in the filesystem. - //! - //! @return The size of an inode in bytes. - [[nodiscard]] auto inode_size(mount_state const & state) const -> kstd::bytes; - - //! Get the number of blocks allocated to an inode. - //! - //! @param data The inode data. - //! @param state The state to operate on. - //! @return The number of blocks allocated to the inode. - [[nodiscard]] auto inode_block_count(inode_data const & data, mount_state const & state) const -> uint32_t; - //! Update the number of blocks allocated to an inode. //! //! @param data The inode data. @@ -176,13 +199,45 @@ namespace kernel::filesystem::ext2 [[nodiscard]] auto indirect_levels(mount_state const & state) const -> std::array<indirect_level, 3>; + //! Allocate a single inode in the file system. + //! + //! @param state The driver state to operate on. + //! @return An inode number on success, an error otherwise. auto allocate_inode(mount_state & state) -> kstd::result<uint32_t>; - auto add_directory_entry(inode * ext2_parent, std::string_view name, uint32_t child_inode_number, uint16_t mode, + + //! Add a new directory entry to a given directory. + //! + //! @param directory The directory to add the entry to. + //! @param name The name for the entry. + //! @param child The inode number of the child. + //! @param mode The file mode of the entry. + //! @param state The driver state to operate on. + //! @return Nothing on success, an error code otherwise. + auto add_directory_entry(inode & directory, std::string_view name, inode & child, std::uint16_t mode, mount_state & state) -> kstd::result<void>; - auto init_directory(kstd::shared_ptr<inode> const & new_inode, inode * parent_inode, mount_state & state) - -> kstd::result<void>; - auto write_directory_entry_to_buffer(uint8_t * block_buffer, kstd::bytes offset, uint32_t child_inode_number, - std::string_view name, uint8_t file_type, mount_state & state) -> void; + + //! Initialize a new directory inode. + //! + //! This function initializes an inode to be an empty directory. On success, the inode will be a directory with only + //! two children, "." and "..". + //! + //! @param directory The inode to initialize. + //! @param parent The parent of this newly initialized directory. + //! @param state The driver state to operate on. + //! @return Nothing on success, an error otherwise. + auto init_directory(inode & directory, inode & parent, mount_state & state) -> kstd::result<void>; + + //! Write a new directory entry into the given buffer. + //! + //! @param buffer The buffer to write to. + //! @param offset The offset inside to the buffer. + //! @param target The inode the entry targets. + //! @param name The name for the entry. + //! @param type The filet type of the entry. + //! @return A pointer to the written entry on success, an error otherwise. + auto write_directory_entry_to_buffer(std::span<std::byte> buffer, kstd::bytes offset, inode & target, + std::string_view name, kapi::filesystem::file_type type) + -> kstd::result<linked_directory_entry *>; [[nodiscard]] auto calculate_inode_offset(uint32_t inode_number, mount_state const & state) const -> kstd::result<kstd::bytes>; @@ -203,7 +258,7 @@ namespace kernel::filesystem::ext2 mount_state const & state) const -> kstd::result<void>; [[nodiscard]] auto write_superblock(mount_state & state) const -> kstd::result<void>; - auto map_inode_mode_to_file_type(uint16_t mode) -> uint8_t; + auto map_inode_mode_to_file_type(uint16_t mode) -> kapi::filesystem::file_type; auto map_vfs_inode_type_into_inode_mode(kapi::filesystem::file_type type, uint16_t mode) -> uint16_t; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp index 752dd010..c0df4647 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp @@ -166,12 +166,6 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, std::string_view result{reinterpret_cast<char *>(read_buffer.data()), static_cast<size_t>(*bytes_read)}; REQUIRE(result == "Hello"); } - - THEN("a symbolic link is not implemented yet") - { - REQUIRE_THROWS_AS(fs->create_inode(root, "blub", kapi::filesystem::file_type::symbolic_link, driver_data), - kernel::tests::cpu::halt); - } } } diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp index 8c7d0ec0..a12c65d8 100644 --- a/kernel/kernel/filesystem/ext2/inode.cpp +++ b/kernel/kernel/filesystem/ext2/inode.cpp @@ -136,7 +136,7 @@ namespace kernel::filesystem::ext2 auto block_size = (*filesystem)->block_size(*state); - auto const current_block_count = (*filesystem)->inode_block_count(m_data, *state); + auto const current_block_count = (*filesystem)->block_count(m_data, *state); auto const max_new_inode_size_without_new_blocks = current_block_count * block_size; if (new_inode_size > max_new_inode_size_without_new_blocks) @@ -217,7 +217,7 @@ namespace kernel::filesystem::ext2 for (auto i = 0uz; i < new_blocks->size(); ++i) { - auto const block_index = (*filesystem)->inode_block_count(m_data, *state) + i; + auto const block_index = (*filesystem)->block_count(m_data, *state) + i; auto const global_block_number = new_blocks->at(i); if (auto write_result = diff --git a/kernel/kernel/filesystem/ext2/inode.tests.cpp b/kernel/kernel/filesystem/ext2/inode.tests.cpp index a6a88fc8..0152fd8b 100644 --- a/kernel/kernel/filesystem/ext2/inode.tests.cpp +++ b/kernel/kernel/filesystem/ext2/inode.tests.cpp @@ -16,6 +16,7 @@ #include <kapi/filesystem.hpp> #include <kstd/memory.hpp> +#include <kstd/span.hpp> #include <kstd/units.hpp> #include <kstd/vector.hpp> @@ -355,7 +356,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in auto inode_data = static_cast<kernel::filesystem::ext2::inode *>(file.value().get())->data(); auto const block_size = fs->block_size(*mount_state); - auto const expected_allocated_blocks = 32 * fs->inode_block_count(inode_data, *mount_state); + auto const expected_allocated_blocks = 32 * fs->block_count(inode_data, *mount_state); auto write_buffer = kstd::vector<std::byte>(block_size.value * expected_allocated_blocks, std::byte{'A'}); @@ -369,7 +370,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in auto new_inode_data = static_cast<kernel::filesystem::ext2::inode *>(file.value().get())->data(); REQUIRE(new_inode_data.size == write_buffer.size()); - REQUIRE(fs->inode_block_count(new_inode_data, *mount_state) == expected_allocated_blocks); + REQUIRE(fs->block_count(new_inode_data, *mount_state) == expected_allocated_blocks); } THEN("writing to a an inode of type directory panics") @@ -433,7 +434,7 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" auto inode = kernel::filesystem::ext2::inode{inode_number, inode_data}; inode.set_owning_mount(*mount); - REQUIRE(dev_inode->write(as_bytes(std::span{&inode, 1}), inode_data_offset)); + REQUIRE(dev_inode->write(kstd::raw_bytes(inode), inode_data_offset)); auto const buffer = kstd::vector{std::byte{'H'}, std::byte{'e'}, std::byte{'l'}, std::byte{'l'}, std::byte{'o'}, std::byte{' '}, @@ -443,7 +444,7 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" { auto do_read_inode = [&]() { auto inode_data = kernel::filesystem::ext2::inode_data{}; - REQUIRE(dev_inode->read(as_writable_bytes(std::span{&inode_data, 1}), inode_data_offset)); + REQUIRE(dev_inode->read(kstd::raw_bytes(inode_data), inode_data_offset)); auto read_inode = kernel::filesystem::ext2::inode{inode_number, inode_data}; read_inode.set_owning_mount(*mount); return read_inode; diff --git a/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp index 9c9d162d..08999cb8 100644 --- a/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp +++ b/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -1,7 +1,6 @@ #ifndef TEACHOS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP #define TEACHOS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP -#include <array> #include <cstdint> namespace kernel::filesystem::ext2 @@ -13,7 +12,7 @@ namespace kernel::filesystem::ext2 uint16_t rec_len; uint8_t name_len; uint8_t file_type; - std::array<char, 255> name; // NOLINT(readability-magic-numbers) + char name_start; // NOLINT(readability-magic-numbers) }; } // namespace kernel::filesystem::ext2 diff --git a/libs/kstd/kstd/span.hpp b/libs/kstd/kstd/span.hpp new file mode 100644 index 00000000..6be2eea2 --- /dev/null +++ b/libs/kstd/kstd/span.hpp @@ -0,0 +1,57 @@ +#ifndef KSTD_SPAN_HPP +#define KSTD_SPAN_HPP + +#include <cstddef> +#include <ranges> +#include <span> // IWYU pragma: export + +namespace kstd +{ + + //! Get a read-only span of bytes, that maps to a given object + //! + //! @param object The object to obtain the byte span for. + //! @return A span over the raw bytes of the object representation of the given object. + template<typename ObjectType> + requires(!std::ranges::range<ObjectType>) + constexpr auto raw_bytes(ObjectType const & object) -> std::span<std::byte const, sizeof(ObjectType)> + { + return std::as_bytes(std::span<ObjectType const, 1>{std::addressof(object), 1}); + } + + //! Get a read-write span of bytes, that maps to a given object + //! + //! @param object The object to obtain the byte span for. + //! @return A span over the raw bytes of the object representation of the given object. + template<typename ObjectType> + requires(!std::ranges::range<ObjectType>) + constexpr auto raw_bytes(ObjectType & object) -> std::span<std::byte, sizeof(ObjectType)> + { + return std::as_writable_bytes(std::span<ObjectType, 1>{std::addressof(object), 1}); + } + + //! Get a read-only span of bytes, that maps to a given range object + //! + //! @param range The range of objects to obtain the byte span for. + //! @return A span over the raw bytes of the object representation of the given object. + template<typename ObjectType> + requires(std::ranges::range<ObjectType>) + constexpr auto raw_bytes(ObjectType const & range) -> std::span<std::byte const> + { + return std::as_bytes(std::span{range}); + } + + //! Get a read-write span of bytes, that maps to a given range object + //! + //! @param range The range of objects to obtain the byte span for. + //! @return A span over the raw bytes of the object representation of the given object. + template<typename ObjectType> + requires(std::ranges::range<ObjectType>) + constexpr auto raw_bytes(ObjectType & range) -> std::span<std::byte> + { + return std::as_writable_bytes(std::span{range}); + } + +} // namespace kstd + +#endif
\ No newline at end of file |
