diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-07-03 09:32:30 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-07-03 09:32:30 +0200 |
| commit | 3012fa5dfe5dfed5e83baf9b40934ed8e8317627 (patch) | |
| tree | 00d1c60a3d5d1cdfa55e0ff1691cd3362b6064ee /kernel/src/filesystem | |
| parent | 5d8a58d538515ffc57ddf82ba40ba763990a8696 (diff) | |
| download | kernel-3012fa5dfe5dfed5e83baf9b40934ed8e8317627.tar.xz kernel-3012fa5dfe5dfed5e83baf9b40934ed8e8317627.zip | |
kernel: rearrange sources according to p1204
Diffstat (limited to 'kernel/src/filesystem')
31 files changed, 0 insertions, 3630 deletions
diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp deleted file mode 100644 index 01de276c..00000000 --- a/kernel/src/filesystem/dentry.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include <kernel/filesystem/dentry.hpp> - -#include <kernel/filesystem/inode.hpp> - -#include <kapi/system.hpp> - -#include <kstd/memory> -#include <kstd/string> - -#include <algorithm> -#include <cstdint> -#include <string_view> - -namespace kernel::filesystem -{ - dentry::dentry(kstd::shared_ptr<dentry> const & parent, kstd::shared_ptr<inode> const & inode, std::string_view name) - : m_name(name) - , m_parent(parent) - , m_inode(inode) - , m_flags(0) - { - if (!m_inode) - { - kapi::system::panic("[FILESYSTEM] dentry constructed with null inode."); - } - if (m_name.empty()) - { - kapi::system::panic("[FILESYSTEM] dentry constructed with empty name."); - } - } - - auto dentry::get_inode() const -> kstd::shared_ptr<inode> const & - { - return m_inode; - } - - auto dentry::parent() const -> kstd::shared_ptr<dentry> - { - return m_parent.lock(); - } - - auto dentry::name() const -> std::string_view - { - return m_name; - } - - auto dentry::absolute_path() const -> kstd::string - { - kstd::string path = m_name; - - auto parent = this->parent(); - while (parent) - { - auto parent_name = parent->m_name; - if (parent_name == "/") - { - path = "/" + path; - } - else - { - path = parent_name + "/" + path; - } - - parent = parent->parent(); - } - - return path; - } - - auto dentry::add_child(kstd::shared_ptr<dentry> const & child) -> void - { - m_children.push_back(child); - } - - auto dentry::find_child(std::string_view name) const -> kstd::shared_ptr<dentry> - { - auto it = std::ranges::find_if(m_children, [&](auto const & child) { return child->m_name == name; }); - return (it != m_children.end()) ? *it : nullptr; - } - - auto dentry::set_flag(dentry_flags flag) -> void - { - m_flags |= static_cast<uint32_t>(flag); - } - - auto dentry::unset_flag(dentry_flags flag) -> void - { - m_flags &= ~static_cast<uint32_t>(flag); - } - - auto dentry::has_flag(dentry_flags flag) const -> bool - { - return (m_flags & static_cast<uint32_t>(flag)) != 0; - } -} // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp deleted file mode 100644 index b7690f57..00000000 --- a/kernel/src/filesystem/dentry.tests.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include <kernel/filesystem/dentry.hpp> - -#include <kernel/test_support/cpu.hpp> -#include <kernel/test_support/filesystem/inode.hpp> - -#include <kstd/memory> -#include <kstd/print> - -#include <catch2/catch_test_macros.hpp> - -SCENARIO("Dentry construction", "[filesystem][dentry]") -{ - GIVEN("A parent dentry and inode") - { - auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto parent_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "parent"); - - WHEN("constructing a dentry") - { - auto child_dentry = kernel::filesystem::dentry{parent_dentry, inode, "child"}; - - THEN("the dentry has the correct parent, inode, and name") - { - REQUIRE(child_dentry.parent() == parent_dentry); - REQUIRE(child_dentry.get_inode() == inode); - REQUIRE(child_dentry.name() == "child"); - } - - THEN("no flag is set") - { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - } - - WHEN("constructing a dentry with an empty name") - { - THEN("the dentry has the correct parent and inode, and an empty name") - { - REQUIRE_THROWS_AS((kernel::filesystem::dentry{parent_dentry, inode, ""}), kernel::tests::cpu::halt); - } - } - - WHEN("constructing a dentry with a null parent") - { - auto child_dentry = kernel::filesystem::dentry{nullptr, inode, "child"}; - - THEN("the dentry has a null parent, the correct inode, and the correct name") - { - REQUIRE(child_dentry.parent() == nullptr); - REQUIRE(child_dentry.get_inode() == inode); - REQUIRE(child_dentry.name() == "child"); - } - - THEN("no flag is set") - { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - } - - WHEN("constructing a dentry with a null inode") - { - THEN("the system panics") - { - REQUIRE_THROWS_AS((kernel::filesystem::dentry{parent_dentry, nullptr, "child"}), kernel::tests::cpu::halt); - } - } - } -} - -SCENARIO("Dentry child logic", "[filesystem][dentry]") -{ - GIVEN("A parent dentry and inode") - { - auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto parent_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "parent"); - - WHEN("adding child dentries") - { - auto child1 = kstd::make_shared<kernel::filesystem::dentry>(parent_dentry, inode, "child1"); - auto child2 = kstd::make_shared<kernel::filesystem::dentry>(parent_dentry, inode, "child2"); - parent_dentry->add_child(child1); - parent_dentry->add_child(child2); - - THEN("the children can be found by name") - { - REQUIRE(parent_dentry->find_child("child1") == child1); - REQUIRE(parent_dentry->find_child("child2") == child2); - } - - THEN("finding a non-existent child returns null") - { - REQUIRE(parent_dentry->find_child("nonexistent") == nullptr); - } - } - } -} - -SCENARIO("Dentry Flag logic", "[filesystem][dentry]") -{ - GIVEN("A dentry") - { - auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto dentry = kernel::filesystem::dentry{nullptr, inode, "test"}; - - WHEN("setting a flag") - { - dentry.set_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point); - - THEN("the flag is set") - { - REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - } - - WHEN("unsetting a flag") - { - dentry.set_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point); - dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point); - - THEN("the flag is unset") - { - REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - } - } -} - -SCENARIO("Dentry path resolution", "[filesystem][dentry]") -{ - GIVEN("A dentry with a parent hierarchy") - { - auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/"); - - auto home_inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto home_dentry = kstd::make_shared<kernel::filesystem::dentry>(root_dentry, home_inode, "home"); - root_dentry->add_child(home_dentry); - - auto user_inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto user_dentry = kstd::make_shared<kernel::filesystem::dentry>(home_dentry, user_inode, "user"); - home_dentry->add_child(user_dentry); - - THEN("the full path is constructed correctly") - { - REQUIRE(root_dentry->absolute_path() == "/"); - REQUIRE(home_dentry->absolute_path() == "/home"); - REQUIRE(user_dentry->absolute_path() == "/home/user"); - } - } -}
\ No newline at end of file diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp deleted file mode 100644 index ce887ff3..00000000 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include <kernel/filesystem/devfs/filesystem.hpp> - -#include "kernel/filesystem/filesystem.hpp" -#include <kernel/devices/storage/management.hpp> -#include <kernel/filesystem/devfs/inode.hpp> -#include <kernel/filesystem/device_inode.hpp> -#include <kernel/filesystem/inode.hpp> -#include <kernel/filesystem/type.hpp> - -#include <kapi/devices/device.hpp> - -#include <kstd/memory> - -#include <algorithm> -#include <string_view> - -namespace kernel::filesystem::devfs -{ - struct type final : kernel::filesystem::type - { - [[nodiscard]] auto name() const noexcept -> std::string_view override - { - return "devfs"; - } - - [[nodiscard]] auto requires_device() const noexcept -> bool override - { - return false; - } - - [[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 &) -> operation_result - { - m_root_inode = kstd::make_shared<inode>(); - build_device_inode_table(); - - 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; - } - - if (parent.get() != m_root_inode.get()) - { - return nullptr; - } - - auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { - if (auto device_inode_ptr = static_cast<device_inode *>(dev_node.get())) - { - return device_inode_ptr->device()->name() == name; - } - return false; - }); - return (it != m_inodes.end()) ? *it : nullptr; - } - - auto filesystem::build_device_inode_table() -> void - { - m_inodes.clear(); - - auto storage_mgmt = devices::storage::management::get(); - std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto const & controller) { - std::ranges::for_each(controller->all_devices(), - [&](auto const & device) { m_inodes.push_back(kstd::make_shared<device_inode>(device)); }); - }); - } -} // namespace kernel::filesystem::devfs
\ No newline at end of file diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp deleted file mode 100644 index 36cb4117..00000000 --- a/kernel/src/filesystem/devfs/filesystem.tests.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include <kernel/filesystem/devfs/filesystem.hpp> - -#include <kernel/filesystem/filesystem.hpp> -#include <kernel/test_support/filesystem/storage_boot_module_fixture.hpp> - -#include <catch2/catch_test_macros.hpp> - -SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, - "Devfs filesystem lookup uses storage management devices", "[filesystem][devfs][filesystem]") -{ - GIVEN("a boot module registry with one module") - { - setup_modules(1); - - auto fs = kernel::filesystem::devfs::filesystem{}; - auto result = fs.mount(nullptr); - - THEN("mount succeeds") - { - REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); - REQUIRE(fs.root_inode() != nullptr); - } - - THEN("lookup on root finds ram0 device inode") - { - auto inode = fs.lookup(fs.root_inode(), "ram0"); - REQUIRE(inode != nullptr); - REQUIRE(inode->is_device()); - } - - THEN("lookup of an unknown device returns null") - { - auto inode = fs.lookup(fs.root_inode(), "ram99"); - REQUIRE(inode == nullptr); - } - - THEN("lookup with wrong parent returns null") - { - auto other_fs = kernel::filesystem::devfs::filesystem{}; - other_fs.mount(nullptr); - - auto inode = fs.lookup(other_fs.root_inode(), "ram0"); - REQUIRE(inode == nullptr); - } - - THEN("lookup with a non-directory parent returns null") - { - auto non_directory_inode = fs.lookup(fs.root_inode(), "ram0"); - REQUIRE(non_directory_inode != nullptr); - REQUIRE_FALSE(non_directory_inode->is_directory()); - - auto result = fs.lookup(non_directory_inode, "anything"); - REQUIRE(result == nullptr); - } - } - - GIVEN("a boot module registry with three modules") - { - setup_modules(3, 2048); - - auto fs = kernel::filesystem::devfs::filesystem{}; - auto result = fs.mount(nullptr); - REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); - - THEN("lookup finds all generated RAM devices") - { - REQUIRE(fs.lookup(fs.root_inode(), "ram0") != nullptr); - REQUIRE(fs.lookup(fs.root_inode(), "ram16") != nullptr); - REQUIRE(fs.lookup(fs.root_inode(), "ram32") != nullptr); - } - } -} diff --git a/kernel/src/filesystem/devfs/inode.cpp b/kernel/src/filesystem/devfs/inode.cpp deleted file mode 100644 index 7bbfbbed..00000000 --- a/kernel/src/filesystem/devfs/inode.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include <kernel/filesystem/devfs/inode.hpp> - -#include <cstddef> - -namespace kernel::filesystem::devfs -{ - auto inode::read(void *, size_t, size_t) const -> size_t - { - return 0; - } - - auto inode::write(void const *, size_t, size_t) -> size_t - { - return 0; - } - - auto inode::is_directory() const -> bool - { - return true; - } -} // namespace kernel::filesystem::devfs
\ No newline at end of file diff --git a/kernel/src/filesystem/devfs/inode.tests.cpp b/kernel/src/filesystem/devfs/inode.tests.cpp deleted file mode 100644 index ae26e742..00000000 --- a/kernel/src/filesystem/devfs/inode.tests.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include <kernel/filesystem/devfs/inode.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -#include <cstdint> - -SCENARIO("Devfs inode creation", "[filesystem][devfs][inode]") -{ - GIVEN("a devfs inode") - { - auto inode = kernel::filesystem::devfs::inode{}; - - THEN("the inode has the correct kind") - { - REQUIRE(inode.is_directory()); - REQUIRE_FALSE(inode.is_device()); - REQUIRE_FALSE(inode.is_regular()); - REQUIRE_FALSE(inode.is_symbolic_link()); - } - } -} - -SCENARIO("Devfs inode read/write", "[filesystem][devfs][inode]") -{ - GIVEN("a devfs inode") - { - auto inode = kernel::filesystem::devfs::inode{}; - - WHEN("attempting to read from the devfs inode") - { - kstd::vector<uint8_t> buffer(512); - auto bytes_read = inode.read(buffer.data(), 0, buffer.size()); - - THEN("no bytes are read") - { - REQUIRE(bytes_read == 0); - } - } - - WHEN("attempting to write to the devfs inode") - { - kstd::vector<uint8_t> buffer(512); - auto bytes_written = inode.write(buffer.data(), 0, buffer.size()); - - THEN("no bytes are written") - { - REQUIRE(bytes_written == 0); - } - } - } -} diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp deleted file mode 100644 index 81a784cd..00000000 --- a/kernel/src/filesystem/device_inode.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include <kernel/filesystem/device_inode.hpp> - -#include <kernel/devices/block_device_utils.hpp> - -#include <kapi/devices/device.hpp> -#include <kapi/system.hpp> - -#include <kstd/memory> - -#include <cstddef> - -namespace kernel::filesystem -{ - device_inode::device_inode(kstd::shared_ptr<kapi::devices::device> const & device) - : m_device(device) - { - if (!device) - { - kapi::system::panic("[FILESYSTEM] device_inode constructed with null device."); - } - } - - auto device_inode::read(void * buffer, size_t offset, size_t size) const -> size_t - { - if (m_device->is_block_device()) - { - return devices::block_device_utils::read(m_device, buffer, offset, size); - } - else - { - kapi::system::panic("[FILESYSTEM] device_file::read called on non-block device."); - } - } - - auto device_inode::write(void const * buffer, size_t offset, size_t size) -> size_t - { - if (m_device->is_block_device()) - { - return devices::block_device_utils::write(m_device, buffer, offset, size); - } - else - { - kapi::system::panic("[FILESYSTEM] device_file::write called on non-block device."); - } - } - - auto device_inode::device() const -> kstd::shared_ptr<kapi::devices::device> const & - { - return m_device; - } - - auto device_inode::is_device() const -> bool - { - return true; - } - -} // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.tests.cpp b/kernel/src/filesystem/device_inode.tests.cpp deleted file mode 100644 index 025a22a2..00000000 --- a/kernel/src/filesystem/device_inode.tests.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include <kernel/filesystem/device_inode.hpp> - -#include <kernel/test_support/cpu.hpp> -#include <kernel/test_support/devices/block_device.hpp> -#include <kernel/test_support/devices/character_device.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -#include <cstddef> -#include <cstdint> - -SCENARIO("Device inode construction", "[filesystem][device_inode]") -{ - GIVEN("a block device") - { - auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "test_block_device", 512, 3 * 512); - - WHEN("constructing a device inode with the block device") - { - auto inode = kernel::filesystem::device_inode{device}; - - THEN("the device inode has the correct device") - { - REQUIRE(inode.device() == device); - } - - THEN("the device inode has the correct kind") - { - REQUIRE(inode.is_device()); - REQUIRE_FALSE(inode.is_directory()); - REQUIRE_FALSE(inode.is_regular()); - REQUIRE_FALSE(inode.is_symbolic_link()); - } - } - - WHEN("constructing a device inode with a null device") - { - THEN("the constructor panics") - { - REQUIRE_THROWS_AS((kernel::filesystem::device_inode{nullptr}), kernel::tests::cpu::halt); - } - } - } -} - -SCENARIO("Device inode read/write", "[filesystem][device_inode]") -{ - GIVEN("a block device and a device inode for that device") - { - auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "test_block_device", 512, 3 * 512); - auto inode = kernel::filesystem::device_inode{device}; - - WHEN("writing to the device inode") - { - kstd::vector<uint8_t> write_buffer(1024); - for (size_t i = 0; i < write_buffer.size(); ++i) - { - write_buffer[i] = static_cast<uint8_t>(i % 256); - } - - auto bytes_written = inode.write(write_buffer.data(), 256, write_buffer.size()); - - THEN("the correct number of bytes is written") - { - REQUIRE(bytes_written == 1024); - } - - THEN("the data written matches the data read back from the device inode") - { - kstd::vector<uint8_t> read_buffer(1024); - auto bytes_read = inode.read(read_buffer.data(), 256, read_buffer.size()); - - REQUIRE(bytes_read == write_buffer.size()); - REQUIRE(read_buffer == write_buffer); - } - } - } -} - -SCENARIO("Device inode read/write with a non-block device", "[filesystem][device_inode]") -{ - GIVEN("a non-block device and a device inode for that device") - { - auto device = kstd::make_shared<kernel::tests::devices::character_device>(0, 0, "test_character_device"); - auto inode = kernel::filesystem::device_inode{device}; - - WHEN("reading from the device inode") - { - kstd::vector<uint8_t> read_buffer(512); - THEN("the system panics") - { - REQUIRE_THROWS_AS(inode.read(read_buffer.data(), 0, read_buffer.size()), kernel::tests::cpu::halt); - } - } - - WHEN("writing to the device inode") - { - kstd::vector<uint8_t> write_buffer(512); - THEN("the system panics") - { - REQUIRE_THROWS_AS(inode.write(write_buffer.data(), 0, write_buffer.size()), kernel::tests::cpu::halt); - } - } - } -} 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 diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp deleted file mode 100644 index 24d0e229..00000000 --- a/kernel/src/filesystem/filesystem.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include <kernel/filesystem/filesystem.hpp> - -#include <kernel/filesystem/ext2/filesystem.hpp> -#include <kernel/filesystem/inode.hpp> - -#include <kapi/system.hpp> - -#include <kstd/memory> - -#include <array> - -namespace kernel::filesystem -{ - namespace - { - constexpr auto static filesystem_factories = std::array{ - []() { return kstd::make_shared<ext2::filesystem>(); }, - }; - } // namespace - - auto filesystem::probe_and_mount(kstd::shared_ptr<inode> const & backing_inode) -> kstd::shared_ptr<filesystem> - { - if (!backing_inode) - { - kapi::system::panic("[FILESYSTEM] cannot mount filesystem: backing inode is null."); - } - - for (auto & factory : filesystem_factories) - { - auto fs = factory(); - if (fs->mount(backing_inode) == operation_result::success) - { - return fs; - } - } - - return nullptr; - } - - auto filesystem::mount(kstd::shared_ptr<inode> const & backing_inode) -> operation_result - { - if (!backing_inode) - { - kapi::system::panic("[FILESYSTEM] cannot mount filesystem: backing inode is null."); - } - - m_backing_inode = backing_inode; - return operation_result::success; - } - - auto filesystem::root_inode() const -> kstd::shared_ptr<inode> const & - { - return m_root_inode; - } - - auto filesystem::backing_inode() const -> kstd::shared_ptr<inode> const & - { - return m_backing_inode; - } -} // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp deleted file mode 100644 index c1889179..00000000 --- a/kernel/src/filesystem/inode.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <kernel/filesystem/inode.hpp> - -namespace kernel::filesystem -{ - auto inode::is_directory() const -> bool - { - return false; - } - - auto inode::is_regular() const -> bool - { - return false; - } - - auto inode::is_device() const -> bool - { - return false; - } - - auto inode::is_symbolic_link() const -> bool - { - return false; - } -} // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp deleted file mode 100644 index ead7479d..00000000 --- a/kernel/src/filesystem/mount.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include <kernel/filesystem/mount.hpp> - -#include <kernel/filesystem/dentry.hpp> -#include <kernel/filesystem/filesystem.hpp> - -#include <kapi/system.hpp> - -#include <kstd/memory> -#include <kstd/string> - -#include <cstddef> -#include <string_view> - -namespace kernel::filesystem -{ - mount::mount(kstd::shared_ptr<dentry> const & mount_dentry, kstd::shared_ptr<dentry> const & root_dentry, - kstd::shared_ptr<filesystem> const & fs, kstd::shared_ptr<mount> const & parent_mount, - kstd::shared_ptr<mount> const & source_mount) - : m_mount_dentry(mount_dentry) - , m_root_dentry(root_dentry) - , m_filesystem(fs) - , m_parent_mount(parent_mount) - , m_source_mount(source_mount) - , m_ref_count(0) - { - if (!m_filesystem) - { - kapi::system::panic("[FILESYSTEM] mount initialized with null filesystem."); - } - } - - auto mount::mount_dentry() const -> kstd::shared_ptr<dentry> const & - { - return m_mount_dentry; - } - - auto mount::get_filesystem() const -> kstd::shared_ptr<filesystem> const & - { - return m_filesystem; - } - - auto mount::root_dentry() const -> kstd::shared_ptr<dentry> const & - { - return m_root_dentry; - } - - auto mount::mount_path() const -> kstd::string - { - if (m_mount_dentry) - { - return m_mount_dentry->absolute_path(); - } - return "/"; - } - - auto mount::parent_mount() const -> kstd::shared_ptr<mount> const & - { - return m_parent_mount; - } - - auto mount::source_mount() const -> kstd::shared_ptr<mount> - { - return m_source_mount.lock(); - } - - auto mount::increment_ref_count() -> void - { - m_ref_count += 1; - } - - auto mount::decrement_ref_count() -> void - { - if (m_ref_count == 0) - { - kapi::system::panic("[FILESYSTEM] decrement_ref_count() was called but ref_count is 0"); - } - - m_ref_count -= 1; - } - - auto mount::is_ready_to_unmount() const -> bool - { - return m_ref_count == 0; - } - - auto mount::ref_count() const -> size_t - { - return m_ref_count; - } -} // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp deleted file mode 100644 index c9ff82e9..00000000 --- a/kernel/src/filesystem/mount.tests.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include <kernel/filesystem/mount.hpp> - -#include <kernel/filesystem/dentry.hpp> -#include <kernel/test_support/cpu.hpp> -#include <kernel/test_support/filesystem/filesystem.hpp> -#include <kernel/test_support/filesystem/inode.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -#include <stdexcept> - -SCENARIO("Mount construction", "[filesystem][mount]") -{ - GIVEN("a filesystem and a root dentry") - { - auto fs = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/"); - - WHEN("constructing a mount with the filesystem and root dentry") - { - auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr}; - - THEN("the mount has the correct filesystem, root dentry, mount dentry, and mount path") - { - REQUIRE(mount.get_filesystem() == fs); - REQUIRE(mount.root_dentry() == root_dentry); - REQUIRE(mount.mount_dentry() == root_dentry); - REQUIRE(mount.mount_path() == "/"); - REQUIRE(mount.is_ready_to_unmount()); - } - - THEN("the mount has no parent mount and no source mount") - { - REQUIRE(mount.parent_mount() == nullptr); - REQUIRE(mount.source_mount() == nullptr); - } - } - - WHEN("constructing a mount with a null filesystem") - { - THEN("the constructor panics") - { - REQUIRE_THROWS_AS((kernel::filesystem::mount{root_dentry, root_dentry, nullptr, nullptr, nullptr}), - kernel::tests::cpu::halt); - } - } - } -} - -SCENARIO("Mount reference counting", "[filesystem][mount]") -{ - GIVEN("a filesystem and a root dentry") - { - auto fs = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/"); - - THEN("reference count can be incremented and decremented, the mount is ready to unmount when the reference " - "count == 0") - { - auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr}; - - mount.increment_ref_count(); - REQUIRE(mount.ref_count() == 1); - REQUIRE_FALSE(mount.is_ready_to_unmount()); - - mount.increment_ref_count(); - REQUIRE(mount.ref_count() == 2); - REQUIRE_FALSE(mount.is_ready_to_unmount()); - - mount.decrement_ref_count(); - REQUIRE(mount.ref_count() == 1); - REQUIRE_FALSE(mount.is_ready_to_unmount()); - - mount.decrement_ref_count(); - REQUIRE(mount.ref_count() == 0); - REQUIRE(mount.is_ready_to_unmount()); - } - - THEN("decrementing reference count when it is already zero does not decrement it below zero") - { - auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr}; - - REQUIRE_THROWS_AS(mount.decrement_ref_count(), std::runtime_error); - REQUIRE(mount.ref_count() == 0); - REQUIRE(mount.is_ready_to_unmount()); - } - } -}
\ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp deleted file mode 100644 index e4baac7a..00000000 --- a/kernel/src/filesystem/mount_table.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include <kernel/filesystem/mount_table.hpp> - -#include <kernel/filesystem/dentry.hpp> -#include <kernel/filesystem/mount.hpp> - -#include <kstd/memory> -#include <kstd/vector> - -#include <algorithm> -#include <ranges> -#include <string_view> - -namespace kernel::filesystem -{ - auto mount_table::has_child_mounts(kstd::shared_ptr<mount> const & parent_mount) const -> bool - { - return std::ranges::any_of(m_mounts, - [&parent_mount](auto const & mount) { return mount->parent_mount() == parent_mount; }); - } - - auto mount_table::add_mount(kstd::shared_ptr<mount> const & mount) -> void - { - m_mounts.push_back(mount); - - if (auto source_mount = mount->source_mount()) - { - source_mount->increment_ref_count(); - } - - if (auto mount_dentry = mount->mount_dentry()) - { - mount_dentry->set_flag(dentry::dentry_flags::is_mount_point); - } - } - - auto mount_table::remove_mount(std::string_view path) -> operation_result - { - auto mount_it = find_mount_iterator(path); - if (mount_it == m_mounts.end()) - { - return operation_result::mount_not_found; - } - - auto const & mount = *mount_it; - if (!mount->is_ready_to_unmount()) - { - return operation_result::cannot_be_unmounted; - } - if (has_child_mounts(mount)) - { - return operation_result::has_child_mounts; - } - - if (auto source_mount = mount->source_mount()) - { - source_mount->decrement_ref_count(); - } - - if (auto mount_dentry = mount->mount_dentry()) - { - mount_dentry->unset_flag(dentry::dentry_flags::is_mount_point); - } - - m_mounts.erase(mount_it); - return operation_result::removed; - } - - auto mount_table::find_mount(std::string_view path) const -> kstd::shared_ptr<mount> - { - auto mount_it = find_mount_iterator(path); - return (mount_it != m_mounts.end()) ? *mount_it : nullptr; - } - - auto mount_table::find_mount_iterator(std::string_view path) const - -> kstd::vector<kstd::shared_ptr<mount>>::const_iterator - { - return std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->mount_path() == path; }).begin(); - } -} // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp deleted file mode 100644 index 19b47b23..00000000 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include <kernel/filesystem/mount_table.hpp> - -#include <kernel/filesystem/dentry.hpp> -#include <kernel/filesystem/mount.hpp> -#include <kernel/test_support/filesystem/filesystem.hpp> -#include <kernel/test_support/filesystem/inode.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -#include <string_view> - -SCENARIO("Mount table construction", "[filesystem][mount_table]") -{ - GIVEN("an empty mount table") - { - kernel::filesystem::mount_table table; - - THEN("removing any mount returns mount_not_found") - { - REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::mount_not_found); - REQUIRE(table.remove_mount("/any/path") == kernel::filesystem::mount_table::operation_result::mount_not_found); - } - } -} - -SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem][mount_table]") -{ - GIVEN("a mount table and some mounts") - { - kernel::filesystem::mount_table table; - - auto fs1 = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_dentry1 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr); - - auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/mnt"); - auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, nullptr, nullptr); - - table.add_mount(mount1); - table.add_mount(mount2); - - THEN("dentry flags are set correctly for mounted dentries") - { - REQUIRE(mount_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - REQUIRE(mount_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - - THEN("finding mounts by exact valid path returns the correct mount") - { - REQUIRE(table.find_mount("/") == mount1); - REQUIRE(table.find_mount("/mnt") == mount2); - } - - THEN("finding mounts by exact invalid path returns null") - { - REQUIRE(table.find_mount("/nonexistent") == nullptr); - REQUIRE(table.find_mount("/mnt/file") == nullptr); - } - - THEN("removing a mount that has no child mounts succeeds") - { - REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - - THEN("removing a mount that does not exist returns mount_not_found") - { - REQUIRE(table.remove_mount("/nonexistent") == kernel::filesystem::mount_table::operation_result::mount_not_found); - } - } - - GIVEN("multiple mounts with the same path") - { - kernel::filesystem::mount_table table; - - auto fs1 = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_dentry1 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr); - - auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, nullptr, nullptr); - - table.add_mount(mount1); - table.add_mount(mount2); - - THEN("finding mounts by exact valid path returns the correct mount") - { - REQUIRE(table.find_mount("/") == mount2); - } - - THEN("removing the topmost mount with the same path succeeds") - { - REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - } - - GIVEN("a mount with child mounts") - { - kernel::filesystem::mount_table table; - - auto fs1 = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_dentry1 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr); - - auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>( - mount_dentry1, kstd::make_shared<kernel::tests::filesystem::inode>(), "mnt"); - auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, mount1, nullptr); - - auto fs3 = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_dentry3 = kstd::make_shared<kernel::filesystem::dentry>( - nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/"); - auto mount_dentry3 = kstd::make_shared<kernel::filesystem::dentry>( - mount_dentry2, kstd::make_shared<kernel::tests::filesystem::inode>(), "submnt"); - auto mount3 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry3, root_dentry3, fs3, mount2, nullptr); - - table.add_mount(mount1); - table.add_mount(mount2); - table.add_mount(mount3); - - THEN("removing a mount with child mounts returns has_child_mounts") - { - REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::has_child_mounts); - REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::has_child_mounts); - } - - THEN("removing a leaf mount succeeds") - { - REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE_FALSE(root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); - } - } -} - -SCENARIO("Mount reference counting", "[filesystem][mount_table]") -{ - kernel::filesystem::mount_table table; - - GIVEN("a filesystem and a root dentry") - { - auto fs = kstd::make_shared<kernel::tests::filesystem::filesystem>(); - auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/"); - - auto source_mount = kstd::make_shared<kernel::filesystem::mount>(root_dentry, root_dentry, fs, nullptr, nullptr); - auto mount = kstd::make_shared<kernel::filesystem::mount>(root_dentry, root_dentry, fs, nullptr, source_mount); - - THEN("reference count of source mount is incremented when a mount is added to the mount table and decremented when " - "the mount is removed") - { - REQUIRE(source_mount->ref_count() == 0); - - table.add_mount(mount); - REQUIRE(source_mount->ref_count() == 1); - - REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(source_mount->ref_count() == 0); - } - } -} diff --git a/kernel/src/filesystem/open_file_descriptor.cpp b/kernel/src/filesystem/open_file_descriptor.cpp deleted file mode 100644 index a5567bf4..00000000 --- a/kernel/src/filesystem/open_file_descriptor.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include <kernel/filesystem/open_file_descriptor.hpp> - -#include <kernel/filesystem/dentry.hpp> - -#include <kstd/memory> -#include <kstd/os/error.hpp> - -#include <cstddef> - -namespace kernel::filesystem -{ - open_file_descriptor::open_file_descriptor(kstd::shared_ptr<dentry> const & dentry) - : m_dentry(dentry) - , m_offset(0) - { - if (!dentry) - { - kstd::os::panic("[FILESYSTEM] open_file_descriptor constructed with null dentry."); - } - } - - auto open_file_descriptor::read(void * buffer, size_t size) -> size_t - { - auto read_bytes = m_dentry->get_inode()->read(buffer, m_offset, size); - m_offset += read_bytes; - return read_bytes; - } - - auto open_file_descriptor::write(void const * buffer, size_t size) -> size_t - { - auto written_bytes = m_dentry->get_inode()->write(buffer, m_offset, size); - m_offset += written_bytes; - return written_bytes; - } - - auto open_file_descriptor::offset() const -> size_t - { - return m_offset; - } - - auto open_file_descriptor::get_dentry() const -> kstd::shared_ptr<dentry> const & - { - return m_dentry; - } - -} // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp deleted file mode 100644 index 8c24cf0e..00000000 --- a/kernel/src/filesystem/open_file_descriptor.tests.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include <kernel/filesystem/open_file_descriptor.hpp> - -#include <kernel/filesystem/dentry.hpp> -#include <kernel/filesystem/inode.hpp> -#include <kernel/filesystem/vfs.hpp> -#include <kernel/test_support/filesystem/inode.hpp> -#include <kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -#include <cstddef> -#include <filesystem> -#include <string_view> - -SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor]") -{ - GIVEN("a dentry and an open file descriptor for that dentry") - { - auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); - auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry}; - - THEN("the initial offset is zero") - { - REQUIRE(file_descriptor.offset() == 0); - } - } -} - -SCENARIO("Open file descriptor read/write offset management", "[filesystem][open_file_descriptor]") -{ - GIVEN("a dentry that tracks read/write calls and an open file descriptor for that dentry") - { - auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); - auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry}; - - THEN("the offset is updated correctly after reads") - { - REQUIRE(file_descriptor.read(nullptr, 100) == 100); - REQUIRE(file_descriptor.offset() == 100); - REQUIRE(file_descriptor.read(nullptr, 50) == 50); - REQUIRE(file_descriptor.offset() == 150); - } - - THEN("the offset is updated correctly after writes") - { - REQUIRE(file_descriptor.write(nullptr, 200) == 200); - REQUIRE(file_descriptor.offset() == 200); - REQUIRE(file_descriptor.write(nullptr, 25) == 25); - REQUIRE(file_descriptor.offset() == 225); - } - - THEN("reads and writes both update the same offset") - { - REQUIRE(file_descriptor.read(nullptr, 10) == 10); - REQUIRE(file_descriptor.offset() == 10); - REQUIRE(file_descriptor.write(nullptr, 20) == 20); - REQUIRE(file_descriptor.offset() == 30); - REQUIRE(file_descriptor.read(nullptr, 5) == 5); - REQUIRE(file_descriptor.offset() == 35); - REQUIRE(file_descriptor.write(nullptr, 15) == 15); - REQUIRE(file_descriptor.offset() == 50); - } - } -} - -SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Open file descriptor read with real image", - "[filesystem][open_file_descriptor][img]") -{ - auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; - - GIVEN("an open file descriptor for a file in a real image") - { - REQUIRE(std::filesystem::exists(image_path)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path})); - - auto & vfs = kernel::filesystem::vfs::get(); - auto dentry = vfs.open("/information/info_1.txt"); - REQUIRE(dentry != nullptr); - auto ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); - - THEN("the file can be read and the offset is updated") - { - kstd::vector<std::byte> buffer(32); - auto bytes_read = ofd->read(buffer.data(), buffer.size()); - REQUIRE(bytes_read == 7); - REQUIRE(ofd->offset() == 7); - - std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)}; - REQUIRE(buffer_as_str == "info_1\n"); - } - - THEN("the file can be read multiple times") - { - kstd::vector<std::byte> buffer(4); - auto bytes_read_1 = ofd->read(buffer.data(), buffer.size() / 2); - REQUIRE(bytes_read_1 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size() / 2); - - auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, buffer.size() / 2); - REQUIRE(bytes_read_2 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size()); - - std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), bytes_read_1 + bytes_read_2}; - REQUIRE(buffer_as_str == "info"); - } - } -} diff --git a/kernel/src/filesystem/open_file_table.cpp b/kernel/src/filesystem/open_file_table.cpp deleted file mode 100644 index 2afe3aa4..00000000 --- a/kernel/src/filesystem/open_file_table.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include <kernel/filesystem/open_file_table.hpp> - -#include <kernel/filesystem/open_file_descriptor.hpp> - -#include <kapi/system.hpp> - -#include <kstd/memory> -#include <kstd/unikstd.h> - -#include <algorithm> -#include <cstddef> -#include <optional> - -namespace -{ - constinit auto static global_open_file_table = std::optional<kernel::filesystem::open_file_table>{}; -} // namespace - -namespace kernel::filesystem -{ - auto open_file_table::init() -> void - { - if (global_open_file_table) - { - kapi::system::panic("[FILESYSTEM] Open file table has already been initialized."); - } - - global_open_file_table.emplace(open_file_table{}); - } - - auto open_file_table::get() -> open_file_table & - { - if (!global_open_file_table) - { - kapi::system::panic("[FILESYSTEM] Open file table has not been initialized."); - } - - return *global_open_file_table; - } - - auto open_file_table::add_file(kstd::shared_ptr<open_file_descriptor> const & file_descriptor) -> kstd::ssize_t - { - if (!file_descriptor) - { - return -1; - } - - auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); - if (it != m_open_files.end()) - { - *it = file_descriptor; - return it - m_open_files.begin(); - } - - m_open_files.push_back(file_descriptor); - return m_open_files.size() - 1; - } - - auto open_file_table::file(size_t fd) const -> kstd::shared_ptr<open_file_descriptor> - { - if (fd >= m_open_files.size()) - { - return nullptr; - } - - return m_open_files.at(fd); - } - - auto open_file_table::remove_file(size_t fd) -> kstd::ssize_t - { - if (fd >= m_open_files.size()) - { - return -1; - } - - m_open_files.at(fd) = nullptr; - return 0; - } -} // namespace kernel::filesystem - -namespace kernel::tests::filesystem::open_file_table -{ - auto deinit() -> void - { - global_open_file_table.reset(); - } -} // namespace kernel::tests::filesystem::open_file_table diff --git a/kernel/src/filesystem/open_file_table.tests.cpp b/kernel/src/filesystem/open_file_table.tests.cpp deleted file mode 100644 index 3e91111d..00000000 --- a/kernel/src/filesystem/open_file_table.tests.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include <kernel/filesystem/open_file_table.hpp> - -#include <kernel/filesystem/dentry.hpp> -#include <kernel/filesystem/open_file_descriptor.hpp> -#include <kernel/test_support/filesystem/inode.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") -{ - GIVEN("a open file table and an open file descriptor") - { - auto & table = kernel::filesystem::open_file_table::get(); - auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); - - auto file_descriptor_1 = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); - auto file_descriptor_2 = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); - - WHEN("adding the open file descriptor to the open file table") - { - auto fd_1 = table.add_file(file_descriptor_1); - auto fd_2 = table.add_file(file_descriptor_2); - auto fd_3 = table.add_file(file_descriptor_2); - - THEN("a valid file descriptor is returned") - { - REQUIRE(fd_1 == 0); - REQUIRE(fd_2 == 1); - REQUIRE(fd_3 == 2); - } - - THEN("the file descriptor can be retrieved using the returned file descriptor") - { - auto retrieved_descriptor = table.file(fd_1); - REQUIRE(retrieved_descriptor == file_descriptor_1); - } - } - } - - GIVEN("a invalid open file descriptor") - { - auto & table = kernel::filesystem::open_file_table::get(); - - THEN("adding a null file descriptor returns an error code") - { - auto fd = table.add_file(nullptr); - REQUIRE(fd == -1); - } - - THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns a null pointer") - { - auto retrieved_descriptor = table.file(1000); - REQUIRE(retrieved_descriptor == nullptr); - } - } -} - -SCENARIO("Open file table remove file", "[filesystem][open_file_table]") -{ - GIVEN("a open file table with an open file descriptor") - { - auto & table = kernel::filesystem::open_file_table::get(); - auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); - auto file_descriptor = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); - auto fd = table.add_file(file_descriptor); - - WHEN("removing the file descriptor using the file descriptor") - { - table.remove_file(fd); - - THEN("the file descriptor can no longer be retrieved using the file descriptor") - { - auto retrieved_descriptor = table.file(fd); - REQUIRE(retrieved_descriptor == nullptr); - } - } - - WHEN("removing a file descriptor the other file descriptor keep the same index") - { - auto fd2 = table.add_file(file_descriptor); - table.remove_file(fd); - - THEN("the second file descriptor can still be retrieved using its file descriptor") - { - auto retrieved_descriptor = table.file(fd2); - REQUIRE(retrieved_descriptor == file_descriptor); - } - } - } - - GIVEN("an invalid file descriptor") - { - auto & table = kernel::filesystem::open_file_table::get(); - - THEN("removing a file with an out-of-bounds file descriptor does nothing") - { - REQUIRE_NOTHROW(table.remove_file(1000)); - } - } -}
\ No newline at end of file diff --git a/kernel/src/filesystem/path.tests.cpp b/kernel/src/filesystem/path.tests.cpp deleted file mode 100644 index 3c18b5c2..00000000 --- a/kernel/src/filesystem/path.tests.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include <kernel/filesystem/path.hpp> - -#include <catch2/catch_test_macros.hpp> - -#include <algorithm> -#include <string> -#include <string_view> -#include <vector> - -SCENARIO("path utilities", "[filesystem][path]") -{ - GIVEN("valid and invalid paths") - { - THEN("valid absolute paths are recognized as valid") - { - REQUIRE(kernel::filesystem::path::is_valid_path("/valid/absolute/path")); - REQUIRE(kernel::filesystem::path::is_valid_path("/")); - } - - THEN("valid relative paths are recognized as valid") - { - REQUIRE(kernel::filesystem::path::is_valid_path("valid/../relative/.././path")); - REQUIRE(kernel::filesystem::path::is_valid_path("valid/relative/path")); - REQUIRE(kernel::filesystem::path::is_valid_path("file.txt")); - } - - THEN("invalid paths are recognized as invalid") - { - REQUIRE_FALSE(kernel::filesystem::path::is_valid_path("")); - REQUIRE_FALSE(kernel::filesystem::path::is_valid_path(std::string(4096, 'a'))); - } - - THEN("valid absolute paths are recognized as absolute") - { - REQUIRE(kernel::filesystem::path::is_valid_absolute_path("/valid/absolute/path")); - REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("valid/relative/path")); - } - - THEN("invalid paths are not recognized as absolute") - { - REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("")); - REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path(std::string(4096, 'a'))); - REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("invalid/absolute/path")); - } - - THEN("valid relative paths are recognized as relative") - { - REQUIRE(kernel::filesystem::path::is_valid_relative_path("valid/relative/path")); - REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("/valid/absolute/path")); - } - - THEN("invalid paths are not recognized as relative") - { - REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("")); - REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path(std::string(4096, 'a'))); - REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("/invalid/absolute/path")); - } - } - - GIVEN("a valid path") - { - THEN("it can be split into components") - { - auto components = kernel::filesystem::path::split("/a/b///c/d.txt"); - std::vector<std::string_view> expected = {"a", "b", "c", "d.txt"}; - REQUIRE(std::ranges::equal(components, expected)); - } - } -}
\ No newline at end of file diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp deleted file mode 100644 index 7fe5c1ea..00000000 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include <kernel/filesystem/rootfs/filesystem.hpp> - -#include <kernel/filesystem/filesystem.hpp> -#include <kernel/filesystem/inode.hpp> -#include <kernel/filesystem/rootfs/inode.hpp> -#include <kernel/filesystem/type.hpp> - -#include <kstd/memory> - -#include <string_view> - -namespace kernel::filesystem::rootfs -{ - - struct type final : kernel::filesystem::type - { - [[nodiscard]] auto name() const noexcept -> std::string_view override - { - return "rootfs"; - } - - [[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 &) -> operation_result - { - m_root_inode = kstd::make_shared<inode>(); - return operation_result::success; - } - - auto filesystem::lookup(kstd::shared_ptr<kernel::filesystem::inode> const &, std::string_view) const - -> kstd::shared_ptr<kernel::filesystem::inode> - { - return nullptr; - } -} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/filesystem.tests.cpp b/kernel/src/filesystem/rootfs/filesystem.tests.cpp deleted file mode 100644 index ae320e95..00000000 --- a/kernel/src/filesystem/rootfs/filesystem.tests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include <kernel/filesystem/rootfs/filesystem.hpp> - -#include <kernel/filesystem/filesystem.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -SCENARIO("Rootfs filesystem mount and lookup", "[filesystem][rootfs][filesystem]") -{ - GIVEN("a mounted rootfs filesystem") - { - auto fs = kernel::filesystem::rootfs::filesystem{}; - auto result = fs.mount(nullptr); - - THEN("the filesystem can be mounted successfully") - { - REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); - REQUIRE(fs.root_inode() != nullptr); - } - - THEN("looking up a non-existent directory returns null") - { - auto non_existent_inode_1 = fs.lookup(fs.root_inode(), ""); - REQUIRE(non_existent_inode_1 == nullptr); - auto non_existent_inode_2 = fs.lookup(fs.root_inode(), "nonexistent"); - REQUIRE(non_existent_inode_2 == nullptr); - } - - THEN("looking up with a null parent inode returns null") - { - auto result = fs.lookup(nullptr, "dev"); - REQUIRE(result == nullptr); - } - } -} diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp deleted file mode 100644 index f64fb87c..00000000 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include <kernel/filesystem/inode.hpp> - -#include <kernel/filesystem/rootfs/inode.hpp> - -#include <cstddef> - -namespace kernel::filesystem::rootfs -{ - auto inode::read(void *, size_t, size_t) const -> size_t - { - return 0; - } - - auto inode::write(void const *, size_t, size_t) -> size_t - { - return 0; - } - - auto inode::is_directory() const -> bool - { - return true; - } -} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp deleted file mode 100644 index f4b634fa..00000000 --- a/kernel/src/filesystem/rootfs/inode.tests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include <kernel/filesystem/rootfs/inode.hpp> - -#include <kstd/memory> -#include <kstd/print> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -SCENARIO("Rootfs inode read/write", "[filesystem][rootfs][inode]") -{ - GIVEN("a rootfs inode") - { - auto inode = kernel::filesystem::rootfs::inode{}; - - WHEN("reading from the inode") - { - kstd::vector<char> buffer(10); - auto bytes_read = inode.read(buffer.data(), 0, buffer.size()); - - THEN("no bytes are read") - { - REQUIRE(bytes_read == 0); - } - } - - WHEN("writing to the inode") - { - kstd::vector<char> buffer(10, 'x'); - auto bytes_written = inode.write(buffer.data(), 0, buffer.size()); - - THEN("no bytes are written") - { - REQUIRE(bytes_written == 0); - } - } - } -} diff --git a/kernel/src/filesystem/type_registry.cpp b/kernel/src/filesystem/type_registry.cpp deleted file mode 100644 index 74b12090..00000000 --- a/kernel/src/filesystem/type_registry.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include <kernel/filesystem/type_registry.hpp> - -#include <kapi/system.hpp> - -#include <kstd/memory> -#include <kstd/print> - -#include <algorithm> -#include <cstddef> -#include <optional> -#include <ranges> -#include <span> - -namespace kernel::filesystem -{ - - extern "C" - { - // We need to suppress clang-tidy linting warnings here, since these symbols are generated by the linker and we - // cannot choose their names, unless we wanted to extend the linker script needlessly. - // NOLINTBEGIN(readability-identifier-naming) - extern type_registry::pointer const __start_fs_types; - extern type_registry::pointer const __stop_fs_types; - // NOLINTEND(readability-identifier-naming) - } - - namespace - { - auto constinit instance = std::optional<type_registry>{}; - } - - auto type_registry::init() -> void - { - if (instance) - { - kapi::system::panic("[FILESYSTEM] tried to initialize type registry more than once!"); - } - - instance.emplace(); - - auto type_descriptors = std::span{&__start_fs_types, &__stop_fs_types} | // - std::views::filter([](auto p) { return p != nullptr; }); - - std::ranges::for_each(type_descriptors, [](auto descriptor) { - kstd::println("[FILESYSTEM] registering '{}'", descriptor->name()); - instance->add(descriptor); - }); - } - - auto type_registry::get() -> type_registry & - { - if (!instance) - { - kapi::system::panic("[FILESYSTEM] type registry has not been initialized!"); - } - - return *instance; - } - - auto type_registry::add(pointer descriptor) -> bool - { - auto result = m_descriptors.emplace(descriptor->name(), descriptor); - return result.second; - } - - auto type_registry::all() const noexcept -> std::span<pointer const> - { - return {m_descriptors.values().begin(), m_descriptors.values().end()}; - } - - auto type_registry::size() const noexcept -> std::size_t - { - return m_descriptors.size(); - } - -} // namespace kernel::filesystem diff --git a/kernel/src/filesystem/type_registry.tests.cpp b/kernel/src/filesystem/type_registry.tests.cpp deleted file mode 100644 index 8382579f..00000000 --- a/kernel/src/filesystem/type_registry.tests.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include <kernel/filesystem/type_registry.hpp> - -#include <kernel/filesystem/filesystem.hpp> -#include <kernel/filesystem/type.hpp> - -#include <kstd/memory> - -#include <catch2/catch_test_macros.hpp> - -#include <iterator> -#include <string_view> - -struct test_type final : kernel::filesystem::type -{ - [[nodiscard]] auto name() const noexcept -> std::string_view override - { - return "bht_testfs"; - } - - [[nodiscard]] auto requires_device() const noexcept -> bool override - { - return false; - } - - [[nodiscard]] auto make_instance() const -> kstd::shared_ptr<kernel::filesystem::filesystem> override - { - return nullptr; - } -}; - -SCENARIO("Filesystem type registry initialization and construction", "[filesystem]") -{ - GIVEN("A default constructed type_registry") - { - auto instance = kernel::filesystem::type_registry{}; - - WHEN("getting the span of filesystem descriptors") - { - auto descriptors = instance.all(); - - THEN("the span is empty") - { - REQUIRE(descriptors.empty()); - } - } - } -} - -SCENARIO("Filesystem type registry modifiers", "[filesystem]") -{ - GIVEN("A default constructed type_registry") - { - auto instance = kernel::filesystem::type_registry{}; - - WHEN("adding a type descriptor") - { - auto descriptor = test_type{}; - - instance.add(kstd::make_observer(&descriptor)); - - THEN("the size of the registry is one") - { - REQUIRE(instance.size() == 1); - } - - THEN("the span is not empty") - { - REQUIRE_FALSE(instance.all().empty()); - } - - THEN("the span's size is equal to the registry size") - { - REQUIRE(std::size(instance.all()) == std::size(instance)); - } - } - } -} diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp deleted file mode 100644 index e5dff8c4..00000000 --- a/kernel/src/filesystem/vfs.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#include <kernel/filesystem/vfs.hpp> - -#include <kernel/filesystem/constants.hpp> -#include <kernel/filesystem/dentry.hpp> -#include <kernel/filesystem/devfs/filesystem.hpp> -#include <kernel/filesystem/filesystem.hpp> -#include <kernel/filesystem/mount.hpp> -#include <kernel/filesystem/mount_table.hpp> -#include <kernel/filesystem/path.hpp> -#include <kernel/filesystem/rootfs/filesystem.hpp> - -#include <kapi/system.hpp> - -#include <kstd/memory> -#include <kstd/vector> - -#include <algorithm> -#include <cstdint> -#include <optional> -#include <ranges> -#include <string_view> -#include <utility> - -namespace -{ - constinit auto static active_vfs = std::optional<kernel::filesystem::vfs>{}; -} // namespace - -namespace kernel::filesystem -{ - auto vfs::init() -> void - { - if (active_vfs) - { - kapi::system::panic("[FILESYSTEM] vfs has already been initialized."); - } - - active_vfs.emplace(); - } - - vfs::vfs() - { - // mount rootfs at / - auto root_fs = kstd::make_shared<rootfs::filesystem>(); - root_fs->mount(nullptr); - - auto root_fs_root_dentry = kstd::make_shared<dentry>(nullptr, root_fs->root_inode(), "/"); - auto root_mount = kstd::make_shared<mount>(nullptr, root_fs_root_dentry, root_fs, nullptr, nullptr); - m_mount_table.add_mount(root_mount); - - // mount devfs at /dev (inside rootfs, temporary, will be shadowed) - auto device_fs = kstd::make_shared<devfs::filesystem>(); - device_fs->mount(nullptr); - graft_persistent_device_fs(device_fs); - - // mount boot fs at / (shadows rootfs), re-graft devfs - auto [boot_device_dentry, boot_device_mount_context] = resolve_path_internal("/dev/ram0"); - if (boot_device_dentry && boot_device_mount_context) - { - if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) - { - if (auto root_dentry = resolve_path("/")) - { - do_mount_internal(root_dentry, root_mount, boot_root_fs, boot_device_mount_context); - graft_persistent_device_fs(device_fs); - } - } - } - } - - auto vfs::get() -> vfs & - { - if (!active_vfs) - { - kapi::system::panic("[FILESYSTEM] vfs has not been initialized."); - } - - return *active_vfs; - } - - auto vfs::open(std::string_view path) -> kstd::shared_ptr<dentry> - { - auto [dentry, mount] = resolve_path_internal(path); - if (!dentry || !mount) - { - return nullptr; - } - mount->increment_ref_count(); - return dentry; - } - - auto vfs::close(std::string_view path) -> operation_result - { - if (auto mount = find_mount(path)) - { - mount->decrement_ref_count(); - return operation_result::success; - } - return operation_result::invalid_path; - } - - auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result - { - if (!path::is_valid_path(source) || !path::is_valid_path(target)) - { - return operation_result::invalid_path; - } - - auto [mount_point_dentry, mount_context] = resolve_path_internal(target); - if (mount_point_dentry && mount_context) - { - auto [source_dentry, source_mount_context] = resolve_path_internal(source); - if (source_dentry && source_mount_context) - { - if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) - { - do_mount_internal(mount_point_dentry, mount_context, fs, source_mount_context); - return operation_result::success; - } - return operation_result::invalid_filesystem; - } - return operation_result::non_existent_path; - } - return operation_result::mount_point_not_found; - } - - auto vfs::unmount(std::string_view path) -> operation_result - { - if (!path::is_valid_path(path)) - { - return operation_result::invalid_path; - } - - auto remove_result = m_mount_table.remove_mount(path); - if (remove_result == mount_table::operation_result::removed) - { - return operation_result::success; - } - else if (remove_result == mount_table::operation_result::mount_not_found) - { - return operation_result::mount_point_not_found; - } - - return operation_result::unmount_failed; - } - - auto vfs::do_mount_internal(kstd::shared_ptr<dentry> const & mount_point_dentry, - kstd::shared_ptr<mount> const & parent_mount, kstd::shared_ptr<filesystem> const & fs, - kstd::shared_ptr<mount> const & source_mount) -> void - { - auto new_fs_root = - kstd::make_shared<dentry>(mount_point_dentry->parent(), fs->root_inode(), mount_point_dentry->name()); - auto new_mount = kstd::make_shared<mount>(mount_point_dentry, new_fs_root, fs, parent_mount, source_mount); - m_mount_table.add_mount(new_mount); - } - - auto vfs::graft_persistent_device_fs(kstd::shared_ptr<devfs::filesystem> const & device_fs) -> void - { - auto [root_mount_point_dentry, root_mount] = resolve_path_internal("/"); - if (root_mount_point_dentry && root_mount) - { - auto dev_dentry = root_mount_point_dentry->find_child("dev"); - if (!dev_dentry) - { - dev_dentry = kstd::make_shared<dentry>(root_mount_point_dentry, device_fs->root_inode(), "dev"); - root_mount_point_dentry->add_child(dev_dentry); - } - - do_mount_internal(dev_dentry, root_mount, device_fs); - } - } - - auto vfs::resolve_path_internal(std::string_view path) const - -> std::pair<kstd::shared_ptr<dentry>, kstd::shared_ptr<mount>> - { - if (!path::is_valid_absolute_path(path)) - { - return {nullptr, nullptr}; - } - - auto current_mount = m_mount_table.find_mount("/"); - if (!current_mount) - { - kapi::system::panic("[FILESYSTEM] no root mount found."); - } - - auto current_dentry = current_mount->root_dentry(); - - auto path_parts = path::split(path); - kstd::vector path_parts_vector(path_parts.begin(), path_parts.end()); - std::ranges::reverse(path_parts_vector); - - auto symlink_counter = 0uz; - - while (!path_parts_vector.empty()) - { - auto part = path_parts_vector.back(); - path_parts_vector.pop_back(); - - if (part == ".") - { - continue; - } - - if (part == "..") - { - auto parent_dentry = current_dentry->parent(); - - if (current_dentry == current_mount->root_dentry()) - { - if (current_mount->mount_path() == "/") - { - continue; - } - - if (auto parent_mount = current_mount->parent_mount()) - { - current_mount = parent_mount; - current_dentry = parent_dentry; - } - } - - current_dentry = parent_dentry; - continue; - } - - auto next_dentry = current_dentry->find_child(part); - if (!next_dentry) - { - auto current_fs = current_mount->get_filesystem(); - auto found_inode = current_fs->lookup(current_dentry->get_inode(), part); - if (!found_inode) - { - return {nullptr, nullptr}; - } - - next_dentry = kstd::make_shared<dentry>(current_dentry, found_inode, part); - current_dentry->add_child(next_dentry); - } - else if (next_dentry->has_flag(dentry::dentry_flags::is_mount_point)) - { - current_mount = m_mount_table.find_mount(next_dentry->absolute_path()); - if (!current_mount) - { - kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); - } - - next_dentry = current_mount->root_dentry(); - } - - if (next_dentry->get_inode()->is_symbolic_link()) - { - if (symlink_counter++ > constants::symloop_max) - { - return {nullptr, nullptr}; - } - - kstd::vector<uint8_t> buffer(constants::symlink_max_path_length); - auto const bytes_read = next_dentry->get_inode()->read(buffer.data(), 0, buffer.size()); - auto const symbolic_link_path = std::string_view{reinterpret_cast<char const *>(buffer.data()), bytes_read}; - - auto symbolic_link_parts = path::split(symbolic_link_path); - kstd::vector symbolic_link_parts_vector(symbolic_link_parts.begin(), symbolic_link_parts.end()); - std::ranges::reverse(symbolic_link_parts_vector); - - path_parts_vector.insert_range(path_parts_vector.end(), symbolic_link_parts_vector); - - if (path::is_valid_absolute_path(symbolic_link_path)) - { - current_mount = m_mount_table.find_mount("/"); - current_dentry = current_mount->root_dentry(); - } - continue; - } - - current_dentry = next_dentry; - } - return {current_dentry, current_mount}; - } - - auto vfs::resolve_path(std::string_view path) const -> kstd::shared_ptr<dentry> - { - return resolve_path_internal(path).first; - } - - auto vfs::find_mount(std::string_view path) const -> kstd::shared_ptr<mount> - { - return resolve_path_internal(path).second; - } - -} // namespace kernel::filesystem - -namespace kernel::tests::filesystem::vfs -{ - auto deinit() -> void - { - active_vfs.reset(); - } -} // namespace kernel::tests::filesystem::vfs diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp deleted file mode 100644 index f1d0df0d..00000000 --- a/kernel/src/filesystem/vfs.tests.cpp +++ /dev/null @@ -1,568 +0,0 @@ -#include <kernel/filesystem/vfs.hpp> - -#include <kernel/filesystem/open_file_descriptor.hpp> -#include <kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp> - -#include <kstd/memory> -#include <kstd/vector> - -#include <catch2/catch_test_macros.hpp> - -#include <cstddef> -#include <filesystem> -#include <stdexcept> -#include <string_view> - -SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with dummy modules", - "[filesystem][vfs]") -{ - GIVEN("an initialized boot module registry with multiple modules") - { - REQUIRE_NOTHROW(setup_modules_and_init_vfs(5)); - - THEN("vfs initializes and provides /dev mount") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto dev = vfs.open("/dev"); - - REQUIRE(dev != nullptr); - } - - THEN("vfs initializes root filesystem with boot device if boot module is present") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto root_file = vfs.open("/"); - - REQUIRE(root_file != nullptr); - } - } -} - -SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with file backed image", - "[filesystem][vfs][img]") -{ - auto const image_path_1 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; - auto const image_path_2 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_2KB_fs.img"; - auto const image_path_3 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_4KB_fs.img"; - - GIVEN("a real image file") - { - REQUIRE(std::filesystem::exists(image_path_1)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path_1})); - - THEN("vfs initializes and provides expected mount points") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto root = vfs.open("/"); - auto dev = vfs.open("/dev"); - auto information = vfs.open("/information/info_1.txt"); - - REQUIRE(root != nullptr); - REQUIRE(dev != nullptr); - REQUIRE(information != nullptr); - } - } - - GIVEN("a real image file containing a /dev directory") - { - REQUIRE(std::filesystem::exists(image_path_2)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_2"}, {image_path_2})); - - THEN("vfs hides the image's /dev behind the devfs mount") - { - auto & vfs = kernel::filesystem::vfs::get(); - - auto image_1 = vfs.open("/dev/image_1.txt"); - REQUIRE(image_1 == nullptr); - - auto dev = vfs.open("/dev/ram0"); - REQUIRE(dev != nullptr); - } - } - - GIVEN("three real image files") - { - REQUIRE(std::filesystem::exists(image_path_1)); - REQUIRE(std::filesystem::exists(image_path_2)); - REQUIRE(std::filesystem::exists(image_path_3)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2", "test_img_module_3"}, - {image_path_1, image_path_2, image_path_3})); - - auto & vfs = kernel::filesystem::vfs::get(); - - THEN("vfs initializes first module as root") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto info1 = vfs.open("/information/info_1.txt"); - auto info2 = vfs.open("/information/info_2.txt"); - - REQUIRE(info1 != nullptr); - REQUIRE(info2 != nullptr); - } - - THEN("second image can be mounted, data retrieved and unmounted again") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - - auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(mounted_monkey_1 != nullptr); - REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); - auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(unmounted_monkey_1 == nullptr); - - auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - } - - THEN("third image can be mounted in a mounted file system, unmount only if no child mount exists") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/dev/ram32", "/information/monkey_house/infrastructure") == - kernel::filesystem::vfs::operation_result::success); - - auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - auto mounted_fish1 = vfs.open("/information/monkey_house/infrastructure/enclosures/aquarium/tank_1/fish_1.txt"); - - REQUIRE(mounted_monkey_1 != nullptr); - REQUIRE(mounted_fish1 != nullptr); - - REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(mounted_fish1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); - REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == - kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); - } - - THEN("image can be mounted, unmount only if no files are open") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - - auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(mounted_monkey_1 != nullptr); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); - - REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); - } - - THEN("file with invalid path or not opened file cannot be closed") - { - REQUIRE(vfs.close("invalid_path") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE_THROWS_AS(vfs.close("/information/info_1.txt"), std::runtime_error); - } - - THEN("file cannot be closed twice") - { - auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - - REQUIRE(vfs.close(info_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE_THROWS_AS(vfs.close(info_1->absolute_path()), std::runtime_error); - } - - THEN("images can be stacked mounted and correct file system is unmounted again") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/dev/ram32", "/information") == kernel::filesystem::vfs::operation_result::success); - - auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); - REQUIRE(mounted_tickets != nullptr); - - REQUIRE(vfs.close(mounted_tickets->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); - mounted_tickets = vfs.open("/information/entrance/tickets.txt"); - REQUIRE(mounted_tickets == nullptr); - - auto mounted_monkey = vfs.open("/information/monkey_house/monkey_1.txt"); - REQUIRE(mounted_monkey != nullptr); - } - - THEN("image can be mounted on / file opened and unmounted again") - { - auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - - REQUIRE(vfs.do_mount("/dev/ram16", "/") == kernel::filesystem::vfs::operation_result::success); - - info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); - - auto water = vfs.open("/monkey_house/infrastructure/water.txt"); - REQUIRE(water != nullptr); - - REQUIRE(vfs.close(water->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); - - info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - } - - THEN("image can be mounted on / just the boot root has /dev") - { - auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - - REQUIRE(vfs.do_mount("/dev/ram16", "/") == kernel::filesystem::vfs::operation_result::success); - - info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); - - auto water = vfs.open("/monkey_house/infrastructure/water.txt"); - REQUIRE(water != nullptr); - - REQUIRE(vfs.close(water->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - auto dev_ram_16 = vfs.open("/dev/ram16"); - REQUIRE(dev_ram_16 == nullptr); - - REQUIRE(vfs.do_mount("/dev/ram32", "/") == kernel::filesystem::vfs::operation_result::non_existent_path); - - REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); - - auto dev_ram_32 = vfs.open("/dev/ram32"); - REQUIRE(dev_ram_32 != nullptr); - } - - THEN("boot root can be unmounted and remounted again but /dev is not re-grafted") - { - auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - - REQUIRE(vfs.close(info_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); - - info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); - - REQUIRE(vfs.do_mount("/dev/ram0", "/") == kernel::filesystem::vfs::operation_result::success); - - info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - - auto dev_ram_0 = vfs.open("/dev/ram0"); - REQUIRE(dev_ram_0 == nullptr); - } - - THEN("mount with null file system fails") - { - REQUIRE(vfs.do_mount("/closed.txt", "/information") == - kernel::filesystem::vfs::operation_result::invalid_filesystem); - } - - THEN("mount with invalid path fails") - { - REQUIRE(vfs.do_mount("/dev/ram16", "") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("/dev/ram16", "information") == - kernel::filesystem::vfs::operation_result::mount_point_not_found); - } - - THEN("mount with non-existent source path fails") - { - REQUIRE(vfs.do_mount("/dev/nonexistent", "/information") == - kernel::filesystem::vfs::operation_result::non_existent_path); - } - - THEN("mount with non-existent mount point fails") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent") == - kernel::filesystem::vfs::operation_result::mount_point_not_found); - } - - THEN("unmount with invalid path fails") - { - REQUIRE(vfs.unmount("") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.unmount("information") == kernel::filesystem::vfs::operation_result::mount_point_not_found); - } - - THEN("unmounting non-existent mount point returns expected error code") - { - REQUIRE(vfs.unmount("/information/nonexistent") == - kernel::filesystem::vfs::operation_result::mount_point_not_found); - } - - THEN("a file can be access if . in the path") - { - auto info_1 = vfs.open("/information/./info_1.txt"); - REQUIRE(info_1 != nullptr); - } - - THEN("a file can be accessed within the same mount if path contains .. ") - { - auto info_1 = vfs.open("/archiv/../information/info_1.txt"); - REQUIRE(info_1 != nullptr); - - auto img = vfs.open("/archiv/../information/../archiv/2024.img"); - REQUIRE(img != nullptr); - } - - THEN("a file can be accessed over multiple mounts if path contains .. or . ") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - - auto img = vfs.open("/information/monkey_house/caretaker/../../../../../../archiv/2024.img"); - REQUIRE(img != nullptr); - - auto dev_32 = vfs.open("/information/monkey_house/caretaker/../../../dev/ram32"); - REQUIRE(dev_32 != nullptr); - - auto water = vfs.open("/information/./monkey_house/infrastructure/water.txt"); - REQUIRE(water != nullptr); - } - - THEN("a file can be accessed over multiple mounts (device and file) if path contains .. ") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information/monkey_house/infrastructure") == - kernel::filesystem::vfs::operation_result::success); - - auto pig_1 = vfs.open("/information/monkey_house/infrastructure/stable/pig_1.txt"); - REQUIRE(pig_1 != nullptr); - - auto isabelle = - vfs.open("/information/monkey_house/infrastructure/stable/../../../monkey_house/caretaker/isabelle.txt"); - REQUIRE(isabelle != nullptr); - - auto closed = vfs.open("/information/monkey_house/infrastructure/stable/../../../../closed.txt"); - REQUIRE(closed != nullptr); - } - } - - GIVEN("A real image file containing as filesystem formatted files") - { - REQUIRE(std::filesystem::exists(image_path_1)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); - - THEN("the file-filesystem in the image can be mounted, files can be read and unmounted again") - { - auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); - - auto info_1 = vfs.open("/information/info_1.txt"); - REQUIRE(info_1 == nullptr); - - auto dentry = vfs.open("/information/sheep_1.txt"); - REQUIRE(dentry != nullptr); - auto sheep_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); - - kstd::vector<std::byte> buffer(7); - auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); - std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), bytes_read}; - REQUIRE(buffer_as_str == "sheep_1"); - - REQUIRE(vfs.close(dentry->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); - auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(unmounted_sheep_1 == nullptr); - } - - THEN("the file-filesystem in the image can be mounted and in this filesystem can another file-filesystem be " - "mounted, files can be read and unmounted again") - { - auto & vfs = kernel::filesystem::vfs::get(); - REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/archiv/2025.img", "/information/stable") == - kernel::filesystem::vfs::operation_result::success); - - auto sheep_1 = vfs.open("/information/sheep_1.txt"); - auto goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); - REQUIRE(sheep_1 != nullptr); - REQUIRE(goat_1 != nullptr); - - auto sheep_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(sheep_1); - auto goat_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(goat_1); - - kstd::vector<std::byte> sheep_buffer(7); - auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); - std::string_view buffer_as_str{reinterpret_cast<char *>(sheep_buffer.data()), bytes_read}; - REQUIRE(buffer_as_str == "sheep_1"); - - kstd::vector<std::byte> goat_buffer(6); - bytes_read = goat_1_ofd->read(goat_buffer.data(), goat_buffer.size()); - buffer_as_str = std::string_view{reinterpret_cast<char *>(goat_buffer.data()), bytes_read}; - REQUIRE(buffer_as_str == "goat_1"); - - REQUIRE(vfs.close(sheep_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(goat_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); - - REQUIRE(vfs.unmount("/information/stable") == kernel::filesystem::vfs::operation_result::success); - auto unmounted_goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); - REQUIRE(unmounted_goat_1 == nullptr); - - auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(still_mounted_sheep_1 != nullptr); - - REQUIRE(vfs.close(still_mounted_sheep_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); - auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(unmounted_sheep_1 == nullptr); - } - } - - GIVEN("two real image files where the second contains as filesystem formatted files") - { - REQUIRE(std::filesystem::exists(image_path_1)); - REQUIRE(std::filesystem::exists(image_path_3)); - REQUIRE_NOTHROW( - setup_modules_from_img_and_init_vfs({"test_img_module_3", "test_img_module_1"}, {image_path_3, image_path_1})); - - auto & vfs = kernel::filesystem::vfs::get(); - - THEN("cannot unmount a filesystem if files are mounted") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/entrance") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/enclosures") == - kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::unmount_failed); - REQUIRE(vfs.unmount("/enclosures") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); - } - - THEN("can mount filesystem onto the directory that contains it") - { - REQUIRE(vfs.do_mount("/dev/ram16", "/entrance") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/entrance") == - kernel::filesystem::vfs::operation_result::success); - - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); - } - } - - GIVEN("A real image files, containing symbolic links") - { - REQUIRE(std::filesystem::exists(image_path_1)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); - - THEN("file can be opened through absolute symbolic link") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto info_1 = vfs.open("/symlinks/info_1_absolute"); - REQUIRE(info_1 != nullptr); - } - - THEN("file can be opened through relative symbolic link") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto info_1 = vfs.open("/symlinks/info_1_relative"); - REQUIRE(info_1 != nullptr); - } - - THEN("file can be opened through symbolic link pointing absolute to the directory containing the file") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto info_1 = vfs.open("/symlinks/information_directory_absolute/info_1.txt"); - REQUIRE(info_1 != nullptr); - } - - THEN("file can be opened through symbolic link pointing relative to the directory containing the file") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto info_1 = vfs.open("/symlinks/information_directory_relative/info_1.txt"); - REQUIRE(info_1 != nullptr); - } - - THEN("symbolic link with path traversing back to the root") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto info_1 = vfs.open("/symlinks/traverse_back_5_times/information/info_1.txt"); - REQUIRE(info_1 != nullptr); - } - - THEN("symbolic link containing an invalid absolute path is handled correctly") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto invalid_symlink = vfs.open("/symlinks/invalid_absolute"); - REQUIRE(invalid_symlink == nullptr); - } - - THEN("symbolic link containing an invalid relative path is handled correctly") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto invalid_symlink = vfs.open("/symlinks/invalid_relative"); - REQUIRE(invalid_symlink == nullptr); - } - - THEN("circular symbolic links are detected and handled correctly") - { - auto & vfs = kernel::filesystem::vfs::get(); - auto circular_symlink = vfs.open("/symlinks/symloop_a"); - REQUIRE(circular_symlink == nullptr); - } - } - - GIVEN("A real image file containing as filesystem formatted files and this filesystem contains a symbolic link") - { - REQUIRE(std::filesystem::exists(image_path_1)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); - - auto & vfs = kernel::filesystem::vfs::get(); - vfs.do_mount("/archiv/2024.img", "/information"); - - THEN("file can be opened through symbolic link pointing to the parent filesystem") - { - auto closed_file = vfs.open("/information/symlinks/traverse_back_twice/closed.txt"); - REQUIRE(closed_file != nullptr); - } - } - - GIVEN("Two real images, one containing a symbolic link leaving and reentering filesystem again") - { - REQUIRE(std::filesystem::exists(image_path_1)); - REQUIRE(std::filesystem::exists(image_path_2)); - REQUIRE_NOTHROW( - setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2"}, {image_path_1, image_path_2})); - - auto & vfs = kernel::filesystem::vfs::get(); - vfs.do_mount("/dev/ram16", "/information"); - - THEN("file can be opened through symbolic link pointing to the parent filesystem and back into the mounted " - "filesystem again") - { - auto monkey_1 = vfs.open("/information/symlinks/leave_and_reenter_mount/monkey_1.txt"); - REQUIRE(monkey_1 != nullptr); - } - } - - GIVEN("One real image containing a very long symbolic link") - { - REQUIRE(std::filesystem::exists(image_path_3)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_3"}, {image_path_3})); - - auto & vfs = kernel::filesystem::vfs::get(); - - THEN("file can be opened through symbolic link with a long path") - { - auto fish_30 = vfs.open("/symlinks/very_long_symlink"); - REQUIRE(fish_30 != nullptr); - } - } - - GIVEN("One real image containing a valid symbolic link chain") - { - REQUIRE(std::filesystem::exists(image_path_3)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_3"}, {image_path_3})); - - auto & vfs = kernel::filesystem::vfs::get(); - - THEN("file can be opened through symbolic link chain") - { - auto map = vfs.open("/symlinks/symlink_chain_1/map.txt"); - REQUIRE(map != nullptr); - } - } -} |
