aboutsummaryrefslogtreecommitdiff
path: root/kernel/src/filesystem/ext2
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-07-03 09:32:30 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-07-03 09:32:30 +0200
commit3012fa5dfe5dfed5e83baf9b40934ed8e8317627 (patch)
tree00d1c60a3d5d1cdfa55e0ff1691cd3362b6064ee /kernel/src/filesystem/ext2
parent5d8a58d538515ffc57ddf82ba40ba763990a8696 (diff)
downloadkernel-3012fa5dfe5dfed5e83baf9b40934ed8e8317627.tar.xz
kernel-3012fa5dfe5dfed5e83baf9b40934ed8e8317627.zip
kernel: rearrange sources according to p1204
Diffstat (limited to 'kernel/src/filesystem/ext2')
-rw-r--r--kernel/src/filesystem/ext2/filesystem.cpp247
-rw-r--r--kernel/src/filesystem/ext2/filesystem.tests.cpp139
-rw-r--r--kernel/src/filesystem/ext2/inode.cpp111
-rw-r--r--kernel/src/filesystem/ext2/inode.tests.cpp376
4 files changed, 0 insertions, 873 deletions
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 <kernel/filesystem/ext2/filesystem.hpp>
-
-#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/superblock.hpp>
-#include <kernel/filesystem/filesystem.hpp>
-#include <kernel/filesystem/inode.hpp>
-#include <kernel/filesystem/type.hpp>
-
-#include <kstd/memory>
-#include <kstd/unikstd.h>
-#include <kstd/vector>
-
-#include <array>
-#include <cstddef>
-#include <cstdint>
-#include <string_view>
-
-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<kernel::filesystem::filesystem> override
- {
- return kstd::make_shared<filesystem>();
- }
- };
-
- [[gnu::used]]
- constexpr auto registration = type_registration<type>{};
-
- auto filesystem::mount(kstd::shared_ptr<kernel::filesystem::inode> 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<block_group_descriptor>(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<kernel::filesystem::inode> const & parent, std::string_view name) const
- -> kstd::shared_ptr<kernel::filesystem::inode>
- {
- if (!parent || !parent->is_directory())
- {
- return nullptr;
- }
-
- auto * ext2_parent = static_cast<inode *>(parent.get());
- if (!ext2_parent)
- {
- return nullptr;
- }
-
- auto const & inode_data = ext2_parent->data();
- kstd::vector<uint8_t> 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<linked_directory_entry const *>(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<linked_directory_entry const *>(buffer.data() + bytes_read);
- }
- }
-
- return nullptr;
- }
-
- auto filesystem::read_inode(uint32_t inode_number) const -> kstd::shared_ptr<inode>
- {
- 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<size_t>(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<inode>(this, new_inode_data);
- }
-
- auto filesystem::indirect_levels() const -> std::array<indirect_level, 3>
- {
- 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 <kernel/filesystem/ext2/filesystem.hpp>
-
-#include <kernel/devices/storage/management.hpp>
-#include <kernel/filesystem/device_inode.hpp>
-#include <kernel/filesystem/ext2/inode.hpp>
-#include <kernel/filesystem/filesystem.hpp>
-#include <kernel/test_support/devices/block_device.hpp>
-#include <kernel/test_support/filesystem/ext2.hpp>
-#include <kernel/test_support/filesystem/storage_boot_module_fixture.hpp>
-
-#include <kstd/memory>
-#include <kstd/vector>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <array>
-#include <cstdint>
-#include <filesystem>
-
-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<kernel::filesystem::device_inode>(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<kernel::tests::devices::block_device>(0, 0, "mock", block_size, 2 * block_size);
- REQUIRE(device != nullptr);
-
- auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(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<kernel::tests::devices::block_device>(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<kernel::filesystem::device_inode>(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<uint32_t>(block_size / sizeof(uint32_t));
- auto const singly_start = static_cast<uint32_t>(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 <kernel/filesystem/ext2/inode.hpp>
-
-#include <kernel/filesystem/ext2/filesystem.hpp>
-#include <kernel/filesystem/inode.hpp>
-
-#include <kapi/system.hpp>
-
-#include <kstd/cstring>
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-
-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<uint8_t const *>(m_data.block.data());
- kstd::libc::memcpy(static_cast<uint8_t *>(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<uint8_t *>(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<uint8_t *>(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<uint64_t>(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 <kernel/filesystem/ext2/inode.hpp>
-
-#include <kernel/devices/storage/management.hpp>
-#include <kernel/filesystem/device_inode.hpp>
-#include <kernel/filesystem/ext2/filesystem.hpp>
-#include <kernel/filesystem/ext2/superblock.hpp>
-#include <kernel/filesystem/filesystem.hpp>
-#include <kernel/test_support/cpu.hpp>
-#include <kernel/test_support/devices/block_device.hpp>
-#include <kernel/test_support/filesystem/ext2.hpp>
-#include <kernel/test_support/filesystem/storage_boot_module_fixture.hpp>
-
-#include <kstd/memory>
-#include <kstd/vector>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <algorithm>
-#include <cstddef>
-#include <filesystem>
-#include <string_view>
-
-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<kernel::filesystem::device_inode>(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<std::byte>(6);
- auto const bytes_read = file->read(buffer.data(), 0, buffer.size());
-
- REQUIRE(bytes_read == 6);
-
- auto const text = std::string_view{reinterpret_cast<char const *>(buffer.data()), bytes_read};
- REQUIRE(text == "info_1");
- }
-
- THEN("reading with an offset returns the expected byte")
- {
- auto buffer = kstd::vector<std::byte>(1);
- auto const bytes_read = file->read(buffer.data(), 5, buffer.size());
-
- REQUIRE(bytes_read == 1);
- REQUIRE(static_cast<char>(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<kernel::tests::devices::block_device>(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<kernel::filesystem::device_inode>(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<std::byte>(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<char const *>(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<kernel::tests::devices::block_device>(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<kernel::filesystem::device_inode>(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<std::byte>(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<char const *>(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<kernel::tests::devices::block_device>(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<kernel::filesystem::device_inode>(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<std::byte>(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<kernel::tests::devices::block_device>(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<kernel::filesystem::device_inode>(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<std::byte>(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<char const *>(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<std::byte>(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<kernel::tests::devices::block_device>(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<kernel::filesystem::device_inode>(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<kernel::tests::devices::block_device>(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<kernel::filesystem::device_inode>(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