From 3012fa5dfe5dfed5e83baf9b40934ed8e8317627 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 3 Jul 2026 09:32:30 +0200 Subject: kernel: rearrange sources according to p1204 --- kernel/src/filesystem/ext2/filesystem.cpp | 247 ---------------- kernel/src/filesystem/ext2/filesystem.tests.cpp | 139 --------- kernel/src/filesystem/ext2/inode.cpp | 111 ------- kernel/src/filesystem/ext2/inode.tests.cpp | 376 ------------------------ 4 files changed, 873 deletions(-) delete mode 100644 kernel/src/filesystem/ext2/filesystem.cpp delete mode 100644 kernel/src/filesystem/ext2/filesystem.tests.cpp delete mode 100644 kernel/src/filesystem/ext2/inode.cpp delete mode 100644 kernel/src/filesystem/ext2/inode.tests.cpp (limited to 'kernel/src/filesystem/ext2') diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp deleted file mode 100644 index 3180a19a..00000000 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ /dev/null @@ -1,247 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -namespace kernel::filesystem::ext2 -{ - - struct type final : kernel::filesystem::type - { - [[nodiscard]] auto name() const noexcept -> std::string_view override - { - return "ext2"; - } - - [[nodiscard]] auto requires_device() const noexcept -> bool override - { - return true; - } - - [[nodiscard]] auto make_instance() const -> kstd::shared_ptr override - { - return kstd::make_shared(); - } - }; - - [[gnu::used]] - constexpr auto registration = type_registration{}; - - auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result - { - kernel::filesystem::filesystem::mount(backing_inode); - - m_backing_inode->read(&m_superblock, constants::superblock_offset, sizeof(m_superblock)); - - if (m_superblock.magic != constants::magic_number) - { - return operation_result::invalid_magic_number; - } - - auto const blocks_per_group = m_superblock.blocks_per_group; - auto const num_block_groups = (m_superblock.blocks_count + blocks_per_group - 1) / blocks_per_group; - - m_block_group_descriptors = kstd::vector(num_block_groups); - - m_backing_inode->read(m_block_group_descriptors.data(), block_group_descriptor_table_offset(), - num_block_groups * sizeof(block_group_descriptor)); - - m_root_inode = read_inode(constants::root_inode_number); - - if (!m_root_inode || !m_root_inode->is_directory()) - { - return operation_result::invalid_root_inode; - } - return operation_result::success; - } - - auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const - -> kstd::shared_ptr - { - if (!parent || !parent->is_directory()) - { - return nullptr; - } - - auto * ext2_parent = static_cast(parent.get()); - if (!ext2_parent) - { - return nullptr; - } - - auto const & inode_data = ext2_parent->data(); - kstd::vector buffer(block_size()); - - for (uint32_t i = 0; i < inode_block_count(inode_data); ++i) - { - auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data); - auto const block_offset = global_block_number * block_size(); - m_backing_inode->read(buffer.data(), block_offset, block_size()); - - auto const * entry = reinterpret_cast(buffer.data()); - auto bytes_read = 0uz; - - while (bytes_read < block_size() && entry->inode != 0) - { - auto const entry_name = std::string_view{entry->name.data(), entry->name_len}; - if (entry_name == name) - { - return read_inode(entry->inode); - } - - bytes_read += entry->rec_len; - entry = reinterpret_cast(buffer.data() + bytes_read); - } - } - - return nullptr; - } - - auto filesystem::read_inode(uint32_t inode_number) const -> kstd::shared_ptr - { - auto const inodes_per_group = m_superblock.inodes_per_group; - auto const block_group_index = (inode_number - 1) / inodes_per_group; - auto const inode_index_within_group = (inode_number - 1) % inodes_per_group; - - if (block_group_index >= m_block_group_descriptors.size()) - { - return nullptr; - } - - auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index); - auto const inode_table_start_block = block_group_descriptor.inode_table; - auto const inode_table_offset = static_cast(inode_table_start_block) * block_size(); - auto const inode_offset = inode_table_offset + inode_index_within_group * inode_size(); - - auto new_inode_data = inode_data{}; - m_backing_inode->read(&new_inode_data, inode_offset, sizeof(inode_data)); - - return kstd::make_shared(this, new_inode_data); - } - - auto filesystem::indirect_levels() const -> std::array - { - return { - {{constants::singly_indirect_block_index, block_numbers_per_singly_indirect_block()}, - {constants::doubly_indirect_block_index, block_numbers_per_doubly_indirect_block()}, - {constants::triply_indirect_block_index, block_numbers_per_triply_indirect_block()}} - }; - } - - auto filesystem::map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const - -> kstd::ssize_t - { - if (inode_block_index < constants::direct_block_count) - { - return data.block.at(inode_block_index); - } - - inode_block_index -= constants::direct_block_count; - - for (auto const & level : indirect_levels()) - { - if (inode_block_index >= level.capacity) - { - inode_block_index -= level.capacity; - continue; - } - - auto block_number = data.block[level.slot_index]; - if (block_number == 0) - { - return 0; - } - - for (auto stride = level.capacity / block_numbers_per_block();; stride /= block_numbers_per_block()) - { - auto const idx = inode_block_index / stride; - inode_block_index %= stride; - - block_number = read_block_number_at_index(block_number, idx); - if (block_number == 0) - { - return 0; - } - - if (stride == 1) - { - break; - } - } - - return block_number; - } - - return -1; - } - - auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t - { - uint32_t block_number_buffer = 0; - - auto const block_start_offset = block_number * block_size(); - auto const number_start_address = block_start_offset + index * sizeof(uint32_t); - m_backing_inode->read(&block_number_buffer, number_start_address, sizeof(uint32_t)); - - return block_number_buffer; - } - - auto filesystem::block_numbers_per_block() const -> size_t - { - return block_size() / sizeof(uint32_t); - } - - auto filesystem::block_numbers_per_singly_indirect_block() const -> size_t - { - return block_numbers_per_block(); - } - - auto filesystem::block_numbers_per_doubly_indirect_block() const -> size_t - { - return block_numbers_per_singly_indirect_block() * block_numbers_per_block(); - } - - auto filesystem::block_numbers_per_triply_indirect_block() const -> size_t - { - return block_numbers_per_doubly_indirect_block() * block_numbers_per_block(); - } - - auto filesystem::block_size() const -> size_t - { - return constants::base_block_size << m_superblock.log_block_size; - } - - auto filesystem::revision_level() const -> uint32_t - { - return m_superblock.rev_level; - } - - auto filesystem::inode_size() const -> uint16_t - { - return revision_level() == constants::good_old_revision ? 128 : m_superblock.inode_size; - } - - auto filesystem::inode_block_count(inode_data const & data) const -> uint32_t - { - return data.blocks / (2 << m_superblock.log_block_size); - } - - auto filesystem::block_group_descriptor_table_offset() const -> size_t - { - return block_size() == 1024 ? 2 * block_size() : block_size(); - } -} // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.tests.cpp b/kernel/src/filesystem/ext2/filesystem.tests.cpp deleted file mode 100644 index 83410702..00000000 --- a/kernel/src/filesystem/ext2/filesystem.tests.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, - "Ext2 filesystem mount and lookup with real image", "[filesystem][ext2][filesystem][img]") -{ - auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; - - GIVEN("a mounted ext2 filesystem from a real image") - { - REQUIRE(std::filesystem::exists(image_path)); - REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path})); - - auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); - REQUIRE(boot_device != nullptr); - - auto dev_inode = kstd::make_shared(boot_device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - THEN("the root inode is available and is a directory") - { - REQUIRE(fs.root_inode() != nullptr); - REQUIRE(fs.root_inode()->is_directory()); - } - - THEN("lookup resolves known entries from the image") - { - auto information = fs.lookup(fs.root_inode(), "information"); - REQUIRE(information != nullptr); - REQUIRE(information->is_directory()); - - auto info_1 = fs.lookup(information, "info_1.txt"); - REQUIRE(info_1 != nullptr); - REQUIRE(info_1->is_regular()); - } - - THEN("lookup returns null for invalid inputs") - { - REQUIRE(fs.lookup(nullptr, "information") == nullptr); - - auto information = fs.lookup(fs.root_inode(), "information"); - REQUIRE(information != nullptr); - auto info_1 = fs.lookup(information, "info_1.txt"); - REQUIRE(info_1 != nullptr); - - REQUIRE(fs.lookup(info_1, "anything") == nullptr); - REQUIRE(fs.lookup(fs.root_inode(), "does_not_exist") == nullptr); - } - } -} - -SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem]") -{ - auto const block_size = 1024; - GIVEN("a block device that does not contain an ext2 superblock") - { - auto device = kstd::make_shared(0, 0, "mock", block_size, 2 * block_size); - REQUIRE(device != nullptr); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - - THEN("mount fails with invalid_magic_number") - { - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::invalid_magic_number); - } - } -} - -SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesystem][ext2][filesystem]") -{ - auto const block_size = 1024; - - GIVEN("a minimally valid ext2 layout with configured indirect block tables") - { - auto device = kstd::make_shared(0, 0, "mock", block_size, 128 * block_size); - REQUIRE(device != nullptr); - - kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto inode_data = kernel::filesystem::ext2::inode_data{}; - inode_data.block[0] = 7; - - inode_data.block[12] = 30; - kernel::tests::filesystem::ext2::write_u32(*device, 30 * block_size, 31); - - inode_data.block[13] = 40; - kernel::tests::filesystem::ext2::write_u32(*device, 40 * block_size, 41); - kernel::tests::filesystem::ext2::write_u32(*device, 41 * block_size, 42); - - inode_data.block[14] = 50; - kernel::tests::filesystem::ext2::write_u32(*device, 50 * block_size, 51); - kernel::tests::filesystem::ext2::write_u32(*device, 51 * block_size, 52); - kernel::tests::filesystem::ext2::write_u32(*device, 52 * block_size, 53); - - auto const numbers_per_block = static_cast(block_size / sizeof(uint32_t)); - auto const singly_start = static_cast(kernel::filesystem::ext2::constants::direct_block_count); - auto const doubly_start = singly_start + numbers_per_block; - auto const triply_start = doubly_start + numbers_per_block * numbers_per_block; - - THEN("mapping resolves direct, singly, doubly and triply indirect indexes") - { - REQUIRE(fs.map_inode_block_index_to_global_block_number(0, inode_data) == 7); - REQUIRE(fs.map_inode_block_index_to_global_block_number(singly_start, inode_data) == 31); - REQUIRE(fs.map_inode_block_index_to_global_block_number(doubly_start, inode_data) == 42); - REQUIRE(fs.map_inode_block_index_to_global_block_number(triply_start, inode_data) == 53); - } - - THEN("mapping returns zero for out-of-range indexes") - { - auto const beyond_triply = triply_start + numbers_per_block * numbers_per_block * numbers_per_block; - REQUIRE(fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data) == -1); - } - } -} diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp deleted file mode 100644 index 35a32ee4..00000000 --- a/kernel/src/filesystem/ext2/inode.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include - -#include -#include - -#include - -#include - -#include -#include -#include - -namespace kernel::filesystem::ext2 -{ - inode::inode(filesystem const * fs, inode_data const & data) - : m_filesystem(fs) - , m_data(data) - { - if (!m_filesystem) - { - kapi::system::panic("[EXT2] ext2::inode constructed with filesystem null pointer"); - } - } - - auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t - { - auto const max_readable = this->size() - offset; - auto const requested_size = std::min(size, max_readable); - - if (is_symbolic_link() && this->size() <= sizeof(m_data.block)) - { - auto inline_target = reinterpret_cast(m_data.block.data()); - kstd::libc::memcpy(static_cast(buffer), inline_target + offset, requested_size); - return requested_size; - } - - auto block_index = offset / m_filesystem->block_size(); - auto in_block_offset = offset % m_filesystem->block_size(); - - auto bytes_read = 0uz; - - while (bytes_read < requested_size) - { - auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data); - if (block_number == -1) - { - break; - } - - auto const bytes_to_read = std::min(requested_size - bytes_read, m_filesystem->block_size() - in_block_offset); - if (block_number == 0) - { - kstd::libc::memset(static_cast(buffer) + bytes_read, 0, bytes_to_read); - bytes_read += bytes_to_read; - } - else - { - auto const block_start_offset = block_number * m_filesystem->block_size(); - auto const read_offset = block_start_offset + in_block_offset; - - bytes_read += m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, - bytes_to_read); - } - - block_index++; - in_block_offset = 0; // After the first block, we always start at the beginning of the block - } - - return bytes_read; - } - - auto inode::write(void const *, size_t, size_t) -> size_t - { - kapi::system::panic("[EXT2] inode::write is not implemented yet"); - return 0; - } - - [[nodiscard]] auto inode::data() const -> inode_data const & - { - return m_data; - } - - auto inode::is_regular() const -> bool - { - return (m_data.mode & constants::mode_mask) == constants::mode_regular; - } - - auto inode::is_directory() const -> bool - { - return (m_data.mode & constants::mode_mask) == constants::mode_directory; - } - - auto inode::is_symbolic_link() const -> bool - { - return (m_data.mode & constants::mode_mask) == constants::mode_symbolic_link; - } - - auto inode::size() const -> uint64_t - { - uint64_t size = m_data.size; - - if (m_filesystem->revision_level() > constants::good_old_revision && is_regular()) - { - size |= static_cast(m_data.dir_acl) << 32; - } - - return size; - } - -} // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp deleted file mode 100644 index 4aecc04a..00000000 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ /dev/null @@ -1,376 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - -SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]") -{ - GIVEN("an ext2 filesystem") - { - auto fs = kernel::filesystem::ext2::filesystem{}; - auto data = kernel::filesystem::ext2::inode_data{}; - - THEN("the inode is initialized with regular file mode in data and has the kind regular") - { - data.mode = kernel::filesystem::ext2::constants::mode_regular; - auto inode = kernel::filesystem::ext2::inode(&fs, data); - - REQUIRE(inode.is_regular()); - REQUIRE_FALSE(inode.is_directory()); - REQUIRE_FALSE(inode.is_device()); - REQUIRE_FALSE(inode.is_symbolic_link()); - } - - THEN("the inode is initialized with directory mode in data and has the kind directory") - { - data.mode = kernel::filesystem::ext2::constants::mode_directory; - auto inode = kernel::filesystem::ext2::inode(&fs, data); - - REQUIRE_FALSE(inode.is_regular()); - REQUIRE(inode.is_directory()); - REQUIRE_FALSE(inode.is_device()); - REQUIRE_FALSE(inode.is_symbolic_link()); - } - - THEN("the inode is initialized with symbolic link mode in data and has the kind symbolic link") - { - data.mode = kernel::filesystem::ext2::constants::mode_symbolic_link; - auto inode = kernel::filesystem::ext2::inode(&fs, data); - - REQUIRE_FALSE(inode.is_regular()); - REQUIRE_FALSE(inode.is_directory()); - REQUIRE_FALSE(inode.is_device()); - REQUIRE(inode.is_symbolic_link()); - } - - THEN("the inode is initialized with zero mode in data and has no specific kind") - { - data.mode = 0; - auto inode = kernel::filesystem::ext2::inode(&fs, data); - - REQUIRE_FALSE(inode.is_regular()); - REQUIRE_FALSE(inode.is_directory()); - REQUIRE_FALSE(inode.is_device()); - REQUIRE_FALSE(inode.is_symbolic_link()); - } - } - - GIVEN("no filesystem (null pointer)") - { - THEN("constructing an inode with a null filesystem pointer panics") - { - REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode(nullptr, {}), kernel::tests::cpu::halt); - } - } -} - -SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 inode reads from real image", - "[filesystem][ext2][inode][img]") -{ - auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; - - GIVEN("a mounted ext2 filesystem and a regular file inode") - { - REQUIRE(std::filesystem::exists(image_path)); - REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path})); - - auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); - REQUIRE(boot_device != nullptr); - - auto dev_inode = kstd::make_shared(boot_device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto information = fs.lookup(fs.root_inode(), "information"); - REQUIRE(information != nullptr); - auto file = fs.lookup(information, "info_1.txt"); - REQUIRE(file != nullptr); - REQUIRE(file->is_regular()); - - THEN("reading from offset zero returns expected file prefix") - { - auto buffer = kstd::vector(6); - auto const bytes_read = file->read(buffer.data(), 0, buffer.size()); - - REQUIRE(bytes_read == 6); - - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; - REQUIRE(text == "info_1"); - } - - THEN("reading with an offset returns the expected byte") - { - auto buffer = kstd::vector(1); - auto const bytes_read = file->read(buffer.data(), 5, buffer.size()); - - REQUIRE(bytes_read == 1); - REQUIRE(static_cast(buffer[0]) == '1'); - } - } -} - -SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesystem][ext2][inode]") -{ - auto const block_size = 1024uz; - GIVEN("an ext2 inode with only direct mapped data blocks") - { - auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); - REQUIRE(device != nullptr); - kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto data = kernel::filesystem::ext2::inode_data{}; - data.block[0] = 30; - data.block[1] = 0; - data.block[2] = 31; - data.size = block_size * 3; - - kernel::tests::filesystem::ext2::write_bytes(*device, 30 * block_size, "Hello", 5); - kernel::tests::filesystem::ext2::write_bytes(*device, 31 * block_size, "World!", 6); - - auto inode = kernel::filesystem::ext2::inode{&fs, data}; - - auto buffer = kstd::vector(data.size, std::byte{0xAB}); - - THEN("correct number of bytes are read and holes are returned as zeros") - { - auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); - REQUIRE(bytes_read == data.size); - - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; - REQUIRE(text.substr(0, 5) == "Hello"); - REQUIRE(std::ranges::all_of(text.substr(5, block_size - 5), [](char c) { return c == '\0'; })); - REQUIRE(text.substr(2 * block_size, 6) == "World!"); - REQUIRE(std::ranges::all_of(text.substr(2 * block_size + 6, 3 * block_size), [](char c) { return c == '\0'; })); - } - } - - GIVEN("an ext2 indode with file holes in singly indirect blocks") - { - auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); - REQUIRE(device != nullptr); - kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto data = kernel::filesystem::ext2::inode_data{}; - data.block[0] = 30; - data.block[12] = 31; - data.size = block_size * 15; - - kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size, 50); - kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size + 4, 0); - kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size + 8, 51); - - kernel::tests::filesystem::ext2::write_bytes(*device, 30 * block_size, "Hello", 5); - kernel::tests::filesystem::ext2::write_bytes(*device, 50 * block_size, "Blub", 4); - kernel::tests::filesystem::ext2::write_bytes(*device, 51 * block_size, "World!", 6); - - auto inode = kernel::filesystem::ext2::inode{&fs, data}; - - auto buffer = kstd::vector(data.size, std::byte{0xAB}); - - THEN("correct number of bytes are read and holes are returned as zeros") - { - auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); - REQUIRE(bytes_read == data.size); - - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; - REQUIRE(text.substr(0, 5) == "Hello"); - REQUIRE(std::ranges::all_of(text.substr(5, 12 * block_size - 5), [](char c) { return c == '\0'; })); - REQUIRE(text.substr(12 * block_size, 4) == "Blub"); - REQUIRE( - std::ranges::all_of(text.substr(12 * block_size + 4, 2 * block_size - 4), [](char c) { return c == '\0'; })); - REQUIRE(text.substr(14 * block_size, 6) == "World!"); - REQUIRE( - std::ranges::all_of(text.substr(14 * block_size + 6, 1 * block_size - 6), [](char c) { return c == '\0'; })); - } - } - - GIVEN("an ext2 inode with zero singly indirect block pointer") - { - auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); - REQUIRE(device != nullptr); - kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto data = kernel::filesystem::ext2::inode_data{}; - data.block[12] = 0; - data.size = block_size * 15; - - auto inode = kernel::filesystem::ext2::inode{&fs, data}; - - auto buffer = kstd::vector(block_size * 15, std::byte{0xAB}); - - THEN("all direct blocks are zero when singly indirect block pointer is zero") - { - auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); - REQUIRE(bytes_read == buffer.size()); - REQUIRE(std::ranges::all_of(buffer, [](std::byte c) { return c == std::byte{0x00}; })); - } - } -} - -SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") -{ - auto const block_size = 1024uz; - GIVEN("an ext2 inode with two direct blocks and a block size of 1024 bytes") - { - auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); - REQUIRE(device != nullptr); - kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto inode_data = kernel::filesystem::ext2::inode_data{}; - inode_data.size = block_size * 2; - inode_data.block[0] = 20; - kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6); - inode_data.block[1] = 21; - kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size, "World!", 6); - auto inode = kernel::filesystem::ext2::inode{&fs, inode_data}; - - auto buffer = kstd::vector(12, std::byte{0x00}); - - THEN("reading across the block boundary returns the combined content") - { - auto const bytes_read = inode.read(buffer.data(), block_size - 6, buffer.size()); - REQUIRE(bytes_read == 12); - - auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; - REQUIRE(text == "Hello World!"); - } - } -} - -SCENARIO("Ext2 inode write is not implemented", "[filesystem][ext2][inode]") -{ - GIVEN("an ext2 inode") - { - auto fs = kernel::filesystem::ext2::filesystem{}; - auto inode = kernel::filesystem::ext2::inode{&fs, kernel::filesystem::ext2::inode_data{}}; - - THEN("writing to the inode panics") - { - auto buffer = kstd::vector(32, std::byte{0x00}); - REQUIRE_THROWS_AS(inode.write(buffer.data(), 0, buffer.size()), kernel::tests::cpu::halt); - } - } -} - -SCENARIO("Ext2 inode get_size() correctly returns size depending on revision level", "[filesystem][ext2][inode]") -{ - auto const block_size = 1024uz; - - auto superblock = kernel::filesystem::ext2::superblock{}; - superblock.magic = kernel::filesystem::ext2::constants::magic_number; - superblock.log_block_size = 0; - superblock.blocks_count = 64; - superblock.blocks_per_group = 64; - superblock.inodes_per_group = 32; - superblock.inode_size = 128; - - GIVEN("an ext2 inode with good old revision and inode_data.size = 256, inode_data.dir_acl = 32") - { - superblock.rev_level = 0; - - auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); - REQUIRE(device != nullptr); - kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device, superblock); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto data = kernel::filesystem::ext2::inode_data{}; - data.size = 256; - data.dir_acl = 32; - - THEN("the inode size is 256 if mode = regular") - { - data.mode = kernel::filesystem::ext2::constants::mode_regular; - - auto inode = kernel::filesystem::ext2::inode{&fs, data}; - - REQUIRE(inode.size() == 256); - } - - THEN("the inode size is 256 if mode = directory") - { - data.mode = kernel::filesystem::ext2::constants::mode_directory; - - auto inode = kernel::filesystem::ext2::inode{&fs, data}; - - REQUIRE(inode.size() == 256); - } - } - - GIVEN("an ext2 inode with good dynamic revision and inode_data.size = 256, inode_data.dir_acl = 32") - { - superblock.rev_level = 1; - - auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); - REQUIRE(device != nullptr); - kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device, superblock); - - auto dev_inode = kstd::make_shared(device); - - auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - - auto data = kernel::filesystem::ext2::inode_data{}; - data.size = 256; - data.dir_acl = 32; - - THEN("the inode size is 256 if mode = regular") - { - data.mode = kernel::filesystem::ext2::constants::mode_regular; - - auto inode = kernel::filesystem::ext2::inode{&fs, data}; - - REQUIRE(inode.size() == 0x0000'0020'0000'0100); - } - - THEN("the inode size is 256 if mode = directory") - { - data.mode = kernel::filesystem::ext2::constants::mode_directory; - - auto inode = kernel::filesystem::ext2::inode{&fs, data}; - - REQUIRE(inode.size() == 256); - } - } -} \ No newline at end of file -- cgit v1.2.3