From 72b40ecf33fb0ef2d4232b80560642296c79399c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 09:49:17 +0200 Subject: automatically detect the mounted file system type by trial-and-error --- kernel/include/kernel/filesystem/filesystem.hpp | 3 ++- kernel/src/filesystem/filesystem.cpp | 29 +++++++++++++++++++++---- kernel/src/filesystem/vfs.cpp | 5 +---- kernel/src/main.cpp | 4 ++-- 4 files changed, 30 insertions(+), 11 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 1d86178..05f96dc 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/inode.hpp" #include @@ -15,7 +16,7 @@ namespace kernel::filesystem { virtual ~filesystem() = default; - virtual auto mount(kstd::shared_ptr const & device) -> int; + auto static mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 0ac9cf8..e0c760f 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,20 +1,41 @@ #include "kernel/filesystem/filesystem.hpp" #include "kapi/devices/device.hpp" +#include "kapi/system.hpp" + +#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include + namespace kernel::filesystem { - auto filesystem::mount(kstd::shared_ptr const & device) -> int + namespace + { + constexpr auto static filesystem_factories = std::array{ + []() { return kstd::make_shared(); }, + }; + } // namespace + + auto filesystem::mount(kstd::shared_ptr const & device) -> kstd::shared_ptr { if (!device) { - return -1; // TODO BA-FS26 panic or errorcode? + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: device is null."); } - m_device = device; - return 0; + + for (auto & factory : filesystem_factories) + { + auto fs = factory(); + if (fs->mount(device) == 0) + { + return fs; + } + } + + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: no suitable filesystem found on device."); } auto filesystem::root_inode() const -> kstd::shared_ptr const & diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 06214d2..66aa91e 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -5,7 +5,6 @@ #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/filesystem.hpp" -#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -46,9 +45,7 @@ namespace kernel::filesystem auto storage_mgmt = devices::storage::management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { - // TODO BA-FS26 detect fs type from boot device and load corresponding fs, for now just assume ext2 - auto boot_root_fs = kstd::make_shared(); - boot_root_fs->mount(boot_device); + auto boot_root_fs = kernel::filesystem::filesystem::mount(boot_device); do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index b920674..382f12d 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -130,9 +130,9 @@ auto test_file_lookup() -> void kstd::os::panic("test code failed"); } - auto new_filesystem = kstd::make_shared(); auto device = storage_mgmt.device_by_major_minor(1, 16); - new_filesystem->mount(device); + auto new_filesystem = kernel::filesystem::filesystem::mount(device); + if (vfs.do_mount("/a/b", new_filesystem) != 0) { kstd::os::panic("test code failed"); -- cgit v1.2.3 From 93bca4c2a7c1852fc89df6965c835a7dbbdd6512 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 09:50:14 +0200 Subject: read ext2 superblock and check the magic number --- .../include/kernel/filesystem/ext2/filesystem.hpp | 11 +++- kernel/src/filesystem/ext2/filesystem.cpp | 66 ++++++++++++---------- 2 files changed, 47 insertions(+), 30 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 078da31..176d83c 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -2,20 +2,29 @@ #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + +#include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include #include namespace kernel::filesystem::ext2 { struct filesystem : kernel::filesystem::filesystem { - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> int; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + + private: + auto get_block_size() -> size_t; + auto get_inode_size() -> size_t; + + superblock m_superblock; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index eb9edc4..a3425b5 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -1,10 +1,10 @@ #include "kernel/filesystem/ext2/filesystem.hpp" -#include "kernel/devices/block_device_utils.hpp" #include "kapi/devices/device.hpp" + +#include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" -#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -17,37 +17,35 @@ namespace kernel::filesystem::ext2 { namespace { - // constexpr size_t SUPERBLOCK_OFFSET = 1024; - // constexpr uint16_t EXT2_MAGIC = 0xEF53; - - // // Mode bits - // constexpr uint16_t S_IFMT = 0xF000; - // constexpr uint16_t S_IFREG = 0x8000; - // constexpr uint16_t S_IFDIR = 0x4000; - - // auto S_ISREG(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFREG; - // } - // auto S_ISDIR(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFDIR; - // } - - // auto get_block_size(superblock const & superblock) -> size_t - // { - // return 1024U << superblock.log_block_size; - // } - - // auto get_inode_size(superblock const & superblock) -> size_t - // { - // return superblock.rev_level == 0 ? 128 : superblock.inode_size; - // } + constexpr size_t SUPERBLOCK_OFFSET = 1024; + constexpr uint16_t EXT2_MAGIC = 0xEF53; + + // Mode bits + constexpr uint16_t S_IFMT = 0xF000; + constexpr uint16_t S_IFREG = 0x8000; + constexpr uint16_t S_IFDIR = 0x4000; + + auto S_ISREG(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFREG; + } + auto S_ISDIR(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFDIR; + } } // namespace auto filesystem::mount(kstd::shared_ptr const & device) -> int { - kernel::filesystem::filesystem::mount(device); // TODO BA-FS26 error handling? + m_device = device; + + kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); + + if (m_superblock.magic != EXT2_MAGIC) + { + return -1; + } + // TODO BA-FS26 load proper root inode from ext2 metadata // m_root_inode = inode{inode_kind::directory}; @@ -69,4 +67,14 @@ namespace kernel::filesystem::ext2 return kstd::make_shared(); } + + auto filesystem::get_block_size() -> size_t + { + return 1024U << m_superblock.log_block_size; + } + + auto filesystem::get_inode_size() -> size_t + { + return m_superblock.rev_level == 0 ? 128 : m_superblock.inode_size; + } } // namespace kernel::filesystem::ext2 -- cgit v1.2.3 From 1dcf253fdf8169a3b2b71bfac68f2f25951af1a8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 10:04:04 +0200 Subject: fix build, refactoring --- .../include/kernel/filesystem/devfs/filesystem.hpp | 1 + .../include/kernel/filesystem/ext2/filesystem.hpp | 2 +- kernel/include/kernel/filesystem/filesystem.hpp | 4 +++- .../kernel/filesystem/rootfs/filesystem.hpp | 1 + kernel/src/filesystem/ext2/filesystem.cpp | 27 +++++++++++----------- kernel/src/filesystem/filesystem.cpp | 9 +++++++- kernel/src/filesystem/rootfs/filesystem.cpp | 1 + kernel/src/filesystem/vfs.cpp | 2 +- kernel/src/main.cpp | 4 ++-- 9 files changed, 32 insertions(+), 19 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 29ae388..3edeabb 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 176d83c..f6cd17f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -16,7 +16,7 @@ namespace kernel::filesystem::ext2 { struct filesystem : kernel::filesystem::filesystem { - auto mount(kstd::shared_ptr const & device) -> int; + auto mount(kstd::shared_ptr const & device) -> int override; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 05f96dc..1c45377 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -16,7 +16,9 @@ namespace kernel::filesystem { virtual ~filesystem() = default; - auto static mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; + auto static probe_and_mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; + + virtual auto mount(kstd::shared_ptr const & device) -> int; virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index 5632d86..7931d87 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index a3425b5..0ebb243 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -5,6 +5,7 @@ #include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" +#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -21,23 +22,23 @@ namespace kernel::filesystem::ext2 constexpr uint16_t EXT2_MAGIC = 0xEF53; // Mode bits - constexpr uint16_t S_IFMT = 0xF000; - constexpr uint16_t S_IFREG = 0x8000; - constexpr uint16_t S_IFDIR = 0x4000; - - auto S_ISREG(uint16_t mode) -> bool - { - return (mode & S_IFMT) == S_IFREG; - } - auto S_ISDIR(uint16_t mode) -> bool - { - return (mode & S_IFMT) == S_IFDIR; - } + // constexpr uint16_t S_IFMT = 0xF000; + // constexpr uint16_t S_IFREG = 0x8000; + // constexpr uint16_t S_IFDIR = 0x4000; + + // auto S_ISREG(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFREG; + // } + // auto S_ISDIR(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFDIR; + // } } // namespace auto filesystem::mount(kstd::shared_ptr const & device) -> int { - m_device = device; + kernel::filesystem::filesystem::mount(device); kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index e0c760f..a06eb80 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -19,7 +19,8 @@ namespace kernel::filesystem }; } // namespace - auto filesystem::mount(kstd::shared_ptr const & device) -> kstd::shared_ptr + auto filesystem::probe_and_mount(kstd::shared_ptr const & device) + -> kstd::shared_ptr { if (!device) { @@ -38,6 +39,12 @@ namespace kernel::filesystem kapi::system::panic("[FILESYSTEM] cannot mount filesystem: no suitable filesystem found on device."); } + auto filesystem::mount(kstd::shared_ptr const & device) -> int + { + m_device = device; + return 0; + } + auto filesystem::root_inode() const -> kstd::shared_ptr const & { return m_root_inode; diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index 37bf588..a7c746e 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/rootfs/filesystem.hpp" #include "kapi/devices/device.hpp" + #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/rootfs/inode.hpp" diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 66aa91e..d611fc9 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -45,7 +45,7 @@ namespace kernel::filesystem auto storage_mgmt = devices::storage::management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { - auto boot_root_fs = kernel::filesystem::filesystem::mount(boot_device); + auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device); do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 382f12d..8df6767 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -8,8 +8,8 @@ #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/vfs.hpp" #include "kernel/memory.hpp" @@ -131,7 +131,7 @@ auto test_file_lookup() -> void } auto device = storage_mgmt.device_by_major_minor(1, 16); - auto new_filesystem = kernel::filesystem::filesystem::mount(device); + auto new_filesystem = kernel::filesystem::filesystem::probe_and_mount(device); if (vfs.do_mount("/a/b", new_filesystem) != 0) { -- cgit v1.2.3 From 16b854e991bb791694268d09eb696c719cdff42f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 11:09:13 +0200 Subject: read block_group_descriptors --- kernel/include/kernel/filesystem/ext2/filesystem.hpp | 3 +++ kernel/src/filesystem/ext2/filesystem.cpp | 13 +++++++++++++ 2 files changed, 16 insertions(+) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index f6cd17f..59b9cba 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -3,11 +3,13 @@ #include "kapi/devices/device.hpp" +#include "kernel/filesystem/ext2/block_group_descriptor.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include #include #include @@ -25,6 +27,7 @@ namespace kernel::filesystem::ext2 auto get_inode_size() -> size_t; superblock m_superblock; + kstd::vector m_block_group_descriptors; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 0ebb243..96c1589 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -3,12 +3,14 @@ #include "kapi/devices/device.hpp" #include "kernel/devices/block_device_utils.hpp" +#include "kernel/filesystem/ext2/block_group_descriptor.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include #include #include @@ -47,6 +49,17 @@ namespace kernel::filesystem::ext2 return -1; } + auto const block_size = get_block_size(); + auto const blocks_per_group = m_superblock.blocks_per_group; + auto const num_block_groups = (m_superblock.blocks_count + blocks_per_group - 1) / blocks_per_group; + + m_block_group_descriptors = kstd::vector(num_block_groups); + + auto const block_group_descriptor_table_offset = block_size == 1024 ? 2 * block_size : block_size; + kernel::devices::block_device_utils::read(m_device, m_block_group_descriptors.data(), + block_group_descriptor_table_offset, + num_block_groups * sizeof(block_group_descriptor)); + // TODO BA-FS26 load proper root inode from ext2 metadata // m_root_inode = inode{inode_kind::directory}; -- cgit v1.2.3 From 91db623189133cb14693ae60ee54ac293cec3b54 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 11:33:14 +0200 Subject: remove todos --- kernel/include/kernel/filesystem/ext2/superblock.hpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/superblock.hpp b/kernel/include/kernel/filesystem/ext2/superblock.hpp index 8600b4c..e7e15f2 100644 --- a/kernel/include/kernel/filesystem/ext2/superblock.hpp +++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp @@ -41,11 +41,8 @@ namespace kernel::filesystem::ext2 uint32_t feature_compat; uint32_t feature_incompat; uint32_t feature_ro_compat; - // uint8_t uuid[16]; // TODO BA-FS26 really correct? std::array uuid; - // uint8_t volume_name[16]; // TODO BA-FS26 really correct? std::array volume_name; - // uint8_t last_mounted[64]; // TODO BA-FS26 really correct? std::array last_mounted; uint32_t algorithm_usage_bitmap; @@ -55,14 +52,12 @@ namespace kernel::filesystem::ext2 uint16_t padding1; // Journaling Support - // uint8_t journal_uuid[16]; // TODO BA-FS26 really correct? std::array journal_uuid; uint32_t journal_inum; uint32_t journal_dev; uint32_t last_orphan; // Directory Indexing Support - // uint32_t hash_seed[4]; // TODO BA-FS26 really correct? std::array hash_seed; uint8_t def_hash_version; std::array padding2; -- cgit v1.2.3 From baf63039d5430c0b3b1e6235b561c12f60e97f49 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 15:03:06 +0200 Subject: implement read_inode --- .../filesystem/ext2/block_group_descriptor.hpp | 2 +- .../include/kernel/filesystem/ext2/filesystem.hpp | 4 +++ kernel/include/kernel/filesystem/ext2/inode.hpp | 19 +++++++----- .../filesystem/ext2/linked_directory_entry.hpp | 2 +- .../include/kernel/filesystem/ext2/superblock.hpp | 2 +- kernel/src/filesystem/ext2/filesystem.cpp | 35 +++++++++++++++++----- 6 files changed, 45 insertions(+), 19 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp index a23c045..2ff91d9 100644 --- a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp +++ b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp @@ -6,7 +6,7 @@ namespace kernel::filesystem::ext2 { - struct block_group_descriptor + struct [[gnu::packed]] block_group_descriptor { uint32_t block_bitmap; uint32_t inode_bitmap; diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 59b9cba..ccee172 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -4,6 +4,7 @@ #include "kapi/devices/device.hpp" #include "kernel/filesystem/ext2/block_group_descriptor.hpp" +#include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -12,6 +13,7 @@ #include #include +#include #include namespace kernel::filesystem::ext2 @@ -23,6 +25,8 @@ namespace kernel::filesystem::ext2 -> kstd::shared_ptr override; private: + auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; + auto get_block_size() -> size_t; auto get_inode_size() -> size_t; diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 2c27c17..4284e6f 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -11,13 +11,8 @@ namespace kernel::filesystem::ext2 { - struct inode : kernel::filesystem::inode + struct [[gnu::packed]] inode_data { - inode(); - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - uint16_t mode; uint16_t uid; uint32_t size; @@ -30,15 +25,23 @@ namespace kernel::filesystem::ext2 uint32_t blocks; uint32_t flags; uint32_t osd1; - // uint32_t block[15]; // TODO BA-FS26 really correct? std::array block; // NOLINT(readability-magic-numbers) uint32_t generation; uint32_t file_acl; uint32_t dir_acl; uint32_t faddr; - // uint8_t osd2[12]; // TODO BA-FS26 really correct? std::array osd2; // NOLINT(readability-magic-numbers) }; + + struct inode : kernel::filesystem::inode + { + inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + inode_data m_data{}; + }; } // namespace kernel::filesystem::ext2 #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp index 8dd42a1..f44255a 100644 --- a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -6,7 +6,7 @@ namespace kernel::filesystem::ext2 { - struct linked_directory_entry + struct [[gnu::packed]] linked_directory_entry { uint32_t inode; uint16_t rec_len; diff --git a/kernel/include/kernel/filesystem/ext2/superblock.hpp b/kernel/include/kernel/filesystem/ext2/superblock.hpp index e7e15f2..8e57ae7 100644 --- a/kernel/include/kernel/filesystem/ext2/superblock.hpp +++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp @@ -6,7 +6,7 @@ namespace kernel::filesystem::ext2 { - struct superblock + struct [[gnu::packed]] superblock { uint32_t inodes_count; uint32_t blocks_count; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 96c1589..d650e97 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -21,7 +21,9 @@ namespace kernel::filesystem::ext2 namespace { constexpr size_t SUPERBLOCK_OFFSET = 1024; - constexpr uint16_t EXT2_MAGIC = 0xEF53; + constexpr uint16_t MAGIC_NUMBER = 0xEF53; + + constexpr uint32_t ROOT_INODE_NUMBER = 2; // Mode bits // constexpr uint16_t S_IFMT = 0xF000; @@ -44,7 +46,7 @@ namespace kernel::filesystem::ext2 kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); - if (m_superblock.magic != EXT2_MAGIC) + if (m_superblock.magic != MAGIC_NUMBER) { return -1; } @@ -60,12 +62,7 @@ namespace kernel::filesystem::ext2 block_group_descriptor_table_offset, num_block_groups * sizeof(block_group_descriptor)); - // TODO BA-FS26 load proper root inode from ext2 metadata - // m_root_inode = inode{inode_kind::directory}; - - // TODO BA-FS26 implement - m_root_inode = kstd::make_shared(); - // devices::block_device_utils::read(device, nullptr, 0, 0); // TODO BA-FS26 just for testing + m_root_inode = read_inode(ROOT_INODE_NUMBER); return 0; } @@ -82,6 +79,28 @@ namespace kernel::filesystem::ext2 return kstd::make_shared(); } + auto filesystem::read_inode(uint32_t inode_number) -> kstd::shared_ptr + { + auto const block_size = get_block_size(); + auto const inodes_per_group = m_superblock.inodes_per_group; + auto const block_group_index = (inode_number - 1) / inodes_per_group; + auto const inode_index_within_group = (inode_number - 1) % inodes_per_group; + + if (block_group_index >= m_block_group_descriptors.size()) + { + return nullptr; + } + + auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index); + auto const inode_table_start_block = block_group_descriptor.inode_table; + auto const inode_table_offset = static_cast(inode_table_start_block) * block_size; + auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); + + auto new_inode = kstd::make_shared(); + kernel::devices::block_device_utils::read(m_device, &new_inode->m_data, inode_offset, sizeof(inode_data)); + return new_inode; + } + auto filesystem::get_block_size() -> size_t { return 1024U << m_superblock.log_block_size; -- cgit v1.2.3 From 794de4a7f8dbea164d857ae9e4525536f338518d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 16:06:40 +0200 Subject: temporary implementation of inode kind --- kernel/include/kernel/filesystem/inode.hpp | 3 ++- kernel/src/filesystem/ext2/filesystem.cpp | 41 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 13 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index d97b5ab..59207df 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -25,7 +25,8 @@ namespace kernel::filesystem [[nodiscard]] auto is_regular() const -> bool; [[nodiscard]] auto is_device() const -> bool; - private: + // TODO BA-FS26 improve inode_kind really needed? + public: inode_kind m_kind{inode_kind::regular}; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index d650e97..31dc6f2 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -26,18 +26,18 @@ namespace kernel::filesystem::ext2 constexpr uint32_t ROOT_INODE_NUMBER = 2; // Mode bits - // constexpr uint16_t S_IFMT = 0xF000; - // constexpr uint16_t S_IFREG = 0x8000; - // constexpr uint16_t S_IFDIR = 0x4000; - - // auto S_ISREG(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFREG; - // } - // auto S_ISDIR(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFDIR; - // } + constexpr uint16_t S_IFMT = 0xF000; + constexpr uint16_t S_IFREG = 0x8000; + constexpr uint16_t S_IFDIR = 0x4000; + + auto S_ISREG(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFREG; + } + auto S_ISDIR(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFDIR; + } } // namespace auto filesystem::mount(kstd::shared_ptr const & device) -> int @@ -63,6 +63,7 @@ namespace kernel::filesystem::ext2 num_block_groups * sizeof(block_group_descriptor)); m_root_inode = read_inode(ROOT_INODE_NUMBER); + // TODO BA-FS26 check if root inode is valid and is a directory ?? return 0; } @@ -98,6 +99,22 @@ namespace kernel::filesystem::ext2 auto new_inode = kstd::make_shared(); kernel::devices::block_device_utils::read(m_device, &new_inode->m_data, inode_offset, sizeof(inode_data)); + + // TODO BA-FS26 improve inode_kind really needed? or just map it to the mode bits? + if (S_ISREG(new_inode->m_data.mode)) + { + new_inode->m_kind = inode::inode_kind::regular; + } + else if (S_ISDIR(new_inode->m_data.mode)) + { + new_inode->m_kind = inode::inode_kind::directory; + } + else + { + // TODO BA-FS26 really correct?? + return nullptr; + } + return new_inode; } -- cgit v1.2.3 From 82c8a4c16e3af3d62d7211e741b051da900de79c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 10:16:36 +0200 Subject: first lookup draft --- kernel/src/filesystem/ext2/filesystem.cpp | 48 ++++++++++++++++++++++++++++++- kernel/src/main.cpp | 2 ++ 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 31dc6f2..82fcba9 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -5,11 +5,13 @@ #include "kernel/devices/block_device_utils.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 +#include #include #include @@ -24,6 +26,7 @@ namespace kernel::filesystem::ext2 constexpr uint16_t MAGIC_NUMBER = 0xEF53; constexpr uint32_t ROOT_INODE_NUMBER = 2; + constexpr size_t DIRECT_BLOCK_COUNT = 12; // Mode bits constexpr uint16_t S_IFMT = 0xF000; @@ -67,9 +70,52 @@ namespace kernel::filesystem::ext2 return 0; } - auto filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view name) + auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr { + if (!parent || !parent->is_directory()) + { + return nullptr; + } + + auto * ext2_parent = static_cast(parent.get()); + if (!ext2_parent) + { + return nullptr; + } + + auto const block_size = get_block_size(); + auto const & inode_data = ext2_parent->m_data; + kstd::vector buffer(block_size); + + for (size_t i = 0; i < DIRECT_BLOCK_COUNT && inode_data.block[i] != 0; ++i) + { + kernel::devices::block_device_utils::read(m_device, buffer.data(), inode_data.block[i] * block_size, block_size); + + size_t offset = 0; + while (offset < block_size) + { + auto * entry = reinterpret_cast(buffer.data() + offset); + if (entry->inode == 0) + break; + + // Stop if the directory entry is malformed to avoid overruns/loops. + // if (entry->rec_len < sizeof(linked_directory_entry) || offset + entry->rec_len > block_size) + // { + // break; + // } + + std::string_view entry_name(reinterpret_cast(entry->name.data()), entry->name_len); + if (entry_name == name) + { + return read_inode(entry->inode); + } + + offset += entry->rec_len; + } + } + + return nullptr; // TODO BA-FS26 implement ext2 directory traversal and inode loading if (name == "dev") { diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 8df6767..258c28b 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -117,6 +117,8 @@ auto test_file_lookup() -> void auto vfs = kernel::filesystem::vfs::get(); auto storage_mgmt = kernel::devices::storage::management::get(); + auto ofd_hello = vfs.open("/hello.txt"); + auto ofd1 = vfs.open("/a/b/c"); auto ofd2 = vfs.open("/dev/ram0"); auto ofd3 = vfs.open("/a/d/e"); -- cgit v1.2.3 From 15ea1551e54c36ebac26f21dc156636e326298c6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 14:29:32 +0200 Subject: fix linked_directory_entry struct --- kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp index f44255a..76eb6f5 100644 --- a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -12,8 +12,7 @@ namespace kernel::filesystem::ext2 uint16_t rec_len; uint8_t name_len; uint8_t file_type; - uint8_t pad; - std::array name; // NOLINT(readability-magic-numbers) + std::array name; // NOLINT(readability-magic-numbers) }; } // namespace kernel::filesystem::ext2 -- cgit v1.2.3 From 7e6137b2725d5cf2b16f55678dcfb99091f03fe9 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 14:30:20 +0200 Subject: implement map_inode_block_index_to_global_block_number and lookup --- .../include/kernel/filesystem/ext2/filesystem.hpp | 2 + kernel/src/filesystem/ext2/filesystem.cpp | 120 ++++++++++++++++----- 2 files changed, 98 insertions(+), 24 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index ccee172..dea059f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -26,9 +26,11 @@ namespace kernel::filesystem::ext2 private: auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; + auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; auto get_block_size() -> size_t; auto get_inode_size() -> size_t; + auto get_inode_block_count(inode_data const & data) -> uint32_t; superblock m_superblock; kstd::vector m_block_group_descriptors; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 82fcba9..a84414f 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -11,6 +11,7 @@ #include "kernel/filesystem/inode.hpp" #include +#include #include #include @@ -26,7 +27,11 @@ namespace kernel::filesystem::ext2 constexpr uint16_t MAGIC_NUMBER = 0xEF53; constexpr uint32_t ROOT_INODE_NUMBER = 2; + constexpr size_t DIRECT_BLOCK_COUNT = 12; + constexpr size_t INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; + constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = INDIRECT_BLOCK_INDEX + 1; + constexpr size_t TRIPLY_INDIRECT_BLOCK_INDEX = DOUBLY_INDIRECT_BLOCK_INDEX + 1; // Mode bits constexpr uint16_t S_IFMT = 0xF000; @@ -37,6 +42,7 @@ namespace kernel::filesystem::ext2 { return (mode & S_IFMT) == S_IFREG; } + auto S_ISDIR(uint16_t mode) -> bool { return (mode & S_IFMT) == S_IFDIR; @@ -88,42 +94,29 @@ namespace kernel::filesystem::ext2 auto const & inode_data = ext2_parent->m_data; kstd::vector buffer(block_size); - for (size_t i = 0; i < DIRECT_BLOCK_COUNT && inode_data.block[i] != 0; ++i) + for (uint32_t i = 0; i < get_inode_block_count(inode_data); ++i) { - kernel::devices::block_device_utils::read(m_device, buffer.data(), inode_data.block[i] * block_size, block_size); - - size_t offset = 0; - while (offset < block_size) - { - auto * entry = reinterpret_cast(buffer.data() + offset); - if (entry->inode == 0) - break; + 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; + kernel::devices::block_device_utils::read(m_device, buffer.data(), block_offset, block_size); - // Stop if the directory entry is malformed to avoid overruns/loops. - // if (entry->rec_len < sizeof(linked_directory_entry) || offset + entry->rec_len > block_size) - // { - // break; - // } + auto const * entry = reinterpret_cast(buffer.data()); + auto bytes_read = 0uz; - std::string_view entry_name(reinterpret_cast(entry->name.data()), entry->name_len); + 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); } - offset += entry->rec_len; + bytes_read += entry->rec_len; + entry = reinterpret_cast(buffer.data() + bytes_read); } } return nullptr; - // TODO BA-FS26 implement ext2 directory traversal and inode loading - if (name == "dev") - { - // TODO BA-FS26 just for testing - return nullptr; - } - - return kstd::make_shared(); } auto filesystem::read_inode(uint32_t inode_number) -> kstd::shared_ptr @@ -164,6 +157,80 @@ namespace kernel::filesystem::ext2 return new_inode; } + auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t + { + if (inode_block_index < DIRECT_BLOCK_COUNT) + { + return data.block.at(inode_block_index); + } + inode_block_index -= DIRECT_BLOCK_COUNT; + + auto const block_size = get_block_size(); + auto const numbers_per_block = block_size / sizeof(uint32_t); + uint32_t block_number_buffer = 0; + + auto const number_of_singly_indirect_blocks = numbers_per_block; + if (inode_block_index < number_of_singly_indirect_blocks) + { + auto const indirect_block_start_offset = data.block.at(INDIRECT_BLOCK_INDEX) * block_size; + auto const direct_block_number_index = inode_block_index; + auto const direct_block_number_offset = + indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, + sizeof(uint32_t)); + return block_number_buffer; + } + inode_block_index -= number_of_singly_indirect_blocks; + + auto const number_of_doubly_indirect_blocks = numbers_per_block * numbers_per_block; + if (inode_block_index < number_of_doubly_indirect_blocks) + { + auto const doubly_indirect_block_start_offset = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX) * block_size; + auto const indirect_block_number_index = inode_block_index / numbers_per_block; + auto const indirect_block_number_offset = + doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, + sizeof(uint32_t)); + + auto const indirect_block_start_offset = block_number_buffer * block_size; + auto const direct_block_number_index = inode_block_index % numbers_per_block; + auto const direct_block_number_offset = + indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, + sizeof(uint32_t)); + return block_number_buffer; + } + inode_block_index -= number_of_doubly_indirect_blocks; + + auto const number_of_triply_indirect_blocks = numbers_per_block * numbers_per_block * numbers_per_block; + if (inode_block_index < number_of_triply_indirect_blocks) + { + auto const triply_indirect_block_start_offset = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX) * block_size; + auto const doubly_indirect_block_number_index = inode_block_index / (numbers_per_block * numbers_per_block); + auto const doubly_indirect_block_number_offset = + triply_indirect_block_start_offset + doubly_indirect_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, doubly_indirect_block_number_offset, + sizeof(uint32_t)); + + auto const doubly_indirect_block_start_offset = block_number_buffer * block_size; + auto const indirect_block_number_index = (inode_block_index / numbers_per_block) % numbers_per_block; + auto const indirect_block_number_offset = + doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, + sizeof(uint32_t)); + + auto const indirect_block_start_offset = block_number_buffer * block_size; + auto const direct_block_number_index = inode_block_index % numbers_per_block; + auto const direct_block_number_offset = + indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, + sizeof(uint32_t)); + return block_number_buffer; + } + + return 0; // TODO BA-FS26 really correct?? + } + auto filesystem::get_block_size() -> size_t { return 1024U << m_superblock.log_block_size; @@ -173,4 +240,9 @@ namespace kernel::filesystem::ext2 { return m_superblock.rev_level == 0 ? 128 : m_superblock.inode_size; } + + auto filesystem::get_inode_block_count(inode_data const & data) -> uint32_t + { + return data.blocks / (2 << m_superblock.log_block_size); + } } // namespace kernel::filesystem::ext2 -- cgit v1.2.3 From fe8706422605e466427ae2727ddb98ce5cd984f6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 15:38:16 +0200 Subject: refactoring map_inode_block_index_to_global_block_number --- .../include/kernel/filesystem/ext2/filesystem.hpp | 1 + kernel/src/filesystem/ext2/filesystem.cpp | 101 ++++++++++----------- 2 files changed, 49 insertions(+), 53 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index dea059f..9761903 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -27,6 +27,7 @@ namespace kernel::filesystem::ext2 private: auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; + auto read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t; auto get_block_size() -> size_t; auto get_inode_size() -> size_t; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index a84414f..56c0b88 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -29,8 +29,8 @@ namespace kernel::filesystem::ext2 constexpr uint32_t ROOT_INODE_NUMBER = 2; constexpr size_t DIRECT_BLOCK_COUNT = 12; - constexpr size_t INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; - constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = INDIRECT_BLOCK_INDEX + 1; + constexpr size_t SINGLY_INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; + constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = SINGLY_INDIRECT_BLOCK_INDEX + 1; constexpr size_t TRIPLY_INDIRECT_BLOCK_INDEX = DOUBLY_INDIRECT_BLOCK_INDEX + 1; // Mode bits @@ -167,70 +167,65 @@ namespace kernel::filesystem::ext2 auto const block_size = get_block_size(); auto const numbers_per_block = block_size / sizeof(uint32_t); - uint32_t block_number_buffer = 0; - auto const number_of_singly_indirect_blocks = numbers_per_block; - if (inode_block_index < number_of_singly_indirect_blocks) + auto const block_numbers_per_singly_indirect_block = numbers_per_block; + auto const block_numbers_per_doubly_indirect_block = numbers_per_block * block_numbers_per_singly_indirect_block; + auto const block_numbers_per_triply_indirect_block = numbers_per_block * block_numbers_per_doubly_indirect_block; + + if (inode_block_index < block_numbers_per_singly_indirect_block) { - auto const indirect_block_start_offset = data.block.at(INDIRECT_BLOCK_INDEX) * block_size; - auto const direct_block_number_index = inode_block_index; - auto const direct_block_number_offset = - indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, - sizeof(uint32_t)); - return block_number_buffer; + auto const singly_indirect_block_number = data.block.at(SINGLY_INDIRECT_BLOCK_INDEX); + return read_block_number_at_index(singly_indirect_block_number, inode_block_index); } - inode_block_index -= number_of_singly_indirect_blocks; + inode_block_index -= block_numbers_per_singly_indirect_block; - auto const number_of_doubly_indirect_blocks = numbers_per_block * numbers_per_block; - if (inode_block_index < number_of_doubly_indirect_blocks) + if (inode_block_index < block_numbers_per_doubly_indirect_block) { - auto const doubly_indirect_block_start_offset = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX) * block_size; - auto const indirect_block_number_index = inode_block_index / numbers_per_block; - auto const indirect_block_number_offset = - doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, - sizeof(uint32_t)); - - auto const indirect_block_start_offset = block_number_buffer * block_size; - auto const direct_block_number_index = inode_block_index % numbers_per_block; - auto const direct_block_number_offset = - indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, - sizeof(uint32_t)); - return block_number_buffer; + auto const doubly_indirect_block_number = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX); + auto const singly_indirect_block_index_in_doubly_indirect_block = + inode_block_index / block_numbers_per_singly_indirect_block; + auto const singly_indirect_block_number = read_block_number_at_index( + doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); + + auto const block_index_in_singly_indirect_block = inode_block_index % block_numbers_per_singly_indirect_block; + return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); } - inode_block_index -= number_of_doubly_indirect_blocks; + inode_block_index -= block_numbers_per_doubly_indirect_block; - auto const number_of_triply_indirect_blocks = numbers_per_block * numbers_per_block * numbers_per_block; - if (inode_block_index < number_of_triply_indirect_blocks) + if (inode_block_index < block_numbers_per_triply_indirect_block) { - auto const triply_indirect_block_start_offset = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX) * block_size; - auto const doubly_indirect_block_number_index = inode_block_index / (numbers_per_block * numbers_per_block); - auto const doubly_indirect_block_number_offset = - triply_indirect_block_start_offset + doubly_indirect_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, doubly_indirect_block_number_offset, - sizeof(uint32_t)); - - auto const doubly_indirect_block_start_offset = block_number_buffer * block_size; - auto const indirect_block_number_index = (inode_block_index / numbers_per_block) % numbers_per_block; - auto const indirect_block_number_offset = - doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, - sizeof(uint32_t)); - - auto const indirect_block_start_offset = block_number_buffer * block_size; - auto const direct_block_number_index = inode_block_index % numbers_per_block; - auto const direct_block_number_offset = - indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, - sizeof(uint32_t)); - return block_number_buffer; + auto const triply_indirect_block_number = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX); + auto const doubly_indirect_block_index_in_triply_indirect_block = + inode_block_index / block_numbers_per_doubly_indirect_block; + auto const doubly_indirect_block_number = read_block_number_at_index( + triply_indirect_block_number, doubly_indirect_block_index_in_triply_indirect_block); + + auto const remaining_block_numbers = inode_block_index % block_numbers_per_doubly_indirect_block; + + auto const singly_indirect_block_index_in_doubly_indirect_block = + remaining_block_numbers / block_numbers_per_singly_indirect_block; + auto const singly_indirect_block_number = read_block_number_at_index( + doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); + + auto const block_index_in_singly_indirect_block = + remaining_block_numbers % block_numbers_per_singly_indirect_block; + return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); } return 0; // TODO BA-FS26 really correct?? } + auto filesystem::read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t + { + uint32_t block_number_buffer = 0; + + auto const block_start_offset = block_number * get_block_size(); + auto const number_start_address = block_start_offset + index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, number_start_address, sizeof(uint32_t)); + + return block_number_buffer; + } + auto filesystem::get_block_size() -> size_t { return 1024U << m_superblock.log_block_size; -- cgit v1.2.3 From 725116d22e850c502e6cb8d42b100da1080dfec0 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 6 Apr 2026 10:35:45 +0200 Subject: Add file system pointer to ext2 inode --- kernel/include/kernel/filesystem/ext2/inode.hpp | 5 ++++- kernel/src/filesystem/ext2/filesystem.cpp | 2 +- kernel/src/filesystem/ext2/inode.cpp | 12 ++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 4284e6f..9318008 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -11,6 +11,8 @@ namespace kernel::filesystem::ext2 { + struct filesystem; + struct [[gnu::packed]] inode_data { uint16_t mode; @@ -35,12 +37,13 @@ namespace kernel::filesystem::ext2 struct inode : kernel::filesystem::inode { - inode(); + explicit inode(filesystem * fs); auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; inode_data m_data{}; + filesystem * m_filesystem; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 56c0b88..514bb59 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -136,7 +136,7 @@ namespace kernel::filesystem::ext2 auto const inode_table_offset = static_cast(inode_table_start_block) * block_size; auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); - auto new_inode = kstd::make_shared(); + auto new_inode = kstd::make_shared(this); kernel::devices::block_device_utils::read(m_device, &new_inode->m_data, inode_offset, sizeof(inode_data)); // TODO BA-FS26 improve inode_kind really needed? or just map it to the mode bits? diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index b75969a..4d36e66 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -1,14 +1,22 @@ #include "kernel/filesystem/ext2/inode.hpp" +#include "kapi/system.hpp" + #include "kernel/filesystem/inode.hpp" #include namespace kernel::filesystem::ext2 { - inode::inode() + inode::inode(filesystem * fs) : kernel::filesystem::inode(inode_kind::regular) - {} + , m_filesystem(fs) + { + 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 { -- cgit v1.2.3 From 4a2d4fb3ab38a64c4b10832f5a6318b7240829cc Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 6 Apr 2026 11:40:12 +0200 Subject: Implement read data in ext2 inode --- .../include/kernel/filesystem/ext2/filesystem.hpp | 5 ++-- kernel/include/kernel/filesystem/filesystem.hpp | 1 + kernel/src/filesystem/ext2/inode.cpp | 33 ++++++++++++++++++++-- kernel/src/filesystem/filesystem.cpp | 5 ++++ 4 files changed, 39 insertions(+), 5 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 9761903..762f590 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -24,12 +24,13 @@ namespace kernel::filesystem::ext2 auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + auto get_block_size() -> size_t; + auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; + private: auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; - auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; auto read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t; - auto get_block_size() -> size_t; auto get_inode_size() -> size_t; auto get_inode_block_count(inode_data const & data) -> uint32_t; diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 1c45377..0f9de9f 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -22,6 +22,7 @@ namespace kernel::filesystem virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; + [[nodiscard]] auto device() const -> kstd::shared_ptr const &; protected: kstd::shared_ptr m_root_inode{}; diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 4d36e66..7f4cf69 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -2,9 +2,13 @@ #include "kapi/system.hpp" +#include "kernel/devices/block_device_utils.hpp" +#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/inode.hpp" +#include #include +#include namespace kernel::filesystem::ext2 { @@ -18,10 +22,33 @@ namespace kernel::filesystem::ext2 } } - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t { - // TODO BA-FS26 implement - return 0; + auto block_index = offset / m_filesystem->get_block_size(); + auto in_block_offset = offset % m_filesystem->get_block_size(); + + auto bytes_read = 0uz; + + while (bytes_read < size) + { + auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data); + if (block_number == 0) // TODO BA-FS26 really correct? + { + break; + } + + auto const block_start_offset = block_number * m_filesystem->get_block_size(); + auto const read_offset = block_start_offset + in_block_offset; + auto const bytes_to_read = std::min(size - bytes_read, m_filesystem->get_block_size() - in_block_offset); + + bytes_read += kernel::devices::block_device_utils::read( + m_filesystem->device(), static_cast(buffer) + bytes_read, read_offset, bytes_to_read); + + block_index++; + in_block_offset = 0; // After the first block, we always start at the beginning of the block + } + + return bytes_read; } auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index a06eb80..26c57b6 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -49,4 +49,9 @@ namespace kernel::filesystem { return m_root_inode; } + + auto filesystem::device() const -> kstd::shared_ptr const & + { + return m_device; + } } // namespace kernel::filesystem \ No newline at end of file -- cgit v1.2.3 From bf90441a49d9fa2ab3a1c315679f97289cb33dbe Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 6 Apr 2026 11:41:23 +0200 Subject: Add test for reading file --- kernel/src/main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'kernel') diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 258c28b..186d32e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -23,6 +23,7 @@ #include #include +#include using namespace kstd::units_literals; @@ -119,6 +120,14 @@ auto test_file_lookup() -> void auto ofd_hello = vfs.open("/hello.txt"); + kstd::vector buffer(64); + auto number_of_read_bytes = ofd_hello->read(buffer.data(), buffer.size()); + kstd::println("read bytes: {}", number_of_read_bytes); + kstd::println("buffer: {::#04x}", buffer); + + std::string_view hello_str{reinterpret_cast(buffer.data()), number_of_read_bytes}; + kstd::println("hello_str: {}", hello_str); + auto ofd1 = vfs.open("/a/b/c"); auto ofd2 = vfs.open("/dev/ram0"); auto ofd3 = vfs.open("/a/d/e"); -- cgit v1.2.3 From 2240b9a36e4a9f6f8291c9271e6aac8f5536dbd7 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 7 Apr 2026 22:13:49 +0200 Subject: refactoring --- kernel/include/kernel/filesystem/ext2/filesystem.hpp | 2 +- kernel/src/filesystem/device_inode.cpp | 4 +--- kernel/src/filesystem/ext2/filesystem.cpp | 2 -- kernel/src/filesystem/vfs.cpp | 1 + kernel/src/main.cpp | 2 -- 5 files changed, 3 insertions(+), 8 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 762f590..abab0a6 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -34,7 +34,7 @@ namespace kernel::filesystem::ext2 auto get_inode_size() -> size_t; auto get_inode_block_count(inode_data const & data) -> uint32_t; - superblock m_superblock; + superblock m_superblock; // TODO BA-FS26 initialize kstd::vector m_block_group_descriptors; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index af8cecc..397a0fd 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -1,14 +1,12 @@ #include "kernel/filesystem/device_inode.hpp" +#include "kapi/devices/device.hpp" #include "kapi/system.hpp" #include "kernel/devices/block_device_utils.hpp" -#include "kapi/devices/device.hpp" #include "kernel/filesystem/inode.hpp" -#include #include -#include #include diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 514bb59..d7c989c 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -11,8 +11,6 @@ #include "kernel/filesystem/inode.hpp" #include -#include -#include #include #include diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index d611fc9..f9a051a 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -74,6 +74,7 @@ namespace kernel::filesystem return nullptr; } + // TODO BA-FS26 implement unmount auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int { if (!filesystem) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 186d32e..a2c531f 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -88,8 +88,6 @@ auto test_file_description_manually() -> void auto test_device_with_vfs() -> void { - // TODO BA-FS26 - auto vfs = kernel::filesystem::vfs::get(); auto ofd = vfs.open("/dev/ram0"); if (!ofd) -- cgit v1.2.3 From dd330e7a05905713acfa87ec109956bfe78f78c4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 09:31:32 +0200 Subject: add descriptions, some refactoring --- .../include/kernel/devices/block_device_utils.hpp | 29 +++++++++++++- kernel/include/kernel/filesystem/dentry.hpp | 46 ++++++++++++++++++++++ .../include/kernel/filesystem/devfs/filesystem.hpp | 18 +++++++++ kernel/include/kernel/filesystem/devfs/inode.hpp | 22 +++++++++++ kernel/include/kernel/filesystem/device_inode.hpp | 31 +++++++++++++++ .../filesystem/ext2/block_group_descriptor.hpp | 3 ++ .../include/kernel/filesystem/ext2/filesystem.hpp | 29 +++++++++++++- kernel/include/kernel/filesystem/ext2/inode.hpp | 27 +++++++++++++ .../filesystem/ext2/linked_directory_entry.hpp | 3 ++ .../include/kernel/filesystem/ext2/superblock.hpp | 3 ++ .../kernel/filesystem/file_descriptor_table.hpp | 34 ++++++++++++++++ kernel/include/kernel/filesystem/filesystem.hpp | 37 +++++++++++++++++ kernel/include/kernel/filesystem/inode.hpp | 44 +++++++++++++++++++++ kernel/include/kernel/filesystem/mount.hpp | 28 ++++++++++++- kernel/include/kernel/filesystem/mount_table.hpp | 16 +++++++- .../kernel/filesystem/open_file_description.hpp | 28 +++++++++++++ .../kernel/filesystem/rootfs/filesystem.hpp | 18 +++++++++ kernel/include/kernel/filesystem/rootfs/inode.hpp | 34 ++++++++++++++++ kernel/include/kernel/filesystem/vfs.hpp | 31 +++++++++++++++ kernel/src/filesystem/ext2/filesystem.cpp | 12 +++++- kernel/src/filesystem/ext2/inode.cpp | 2 +- kernel/src/filesystem/filesystem.cpp | 3 +- kernel/src/filesystem/mount.cpp | 2 +- kernel/src/filesystem/mount_table.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 19 +++++---- 25 files changed, 500 insertions(+), 21 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/devices/block_device_utils.hpp b/kernel/include/kernel/devices/block_device_utils.hpp index 5e862ba..7b1daec 100644 --- a/kernel/include/kernel/devices/block_device_utils.hpp +++ b/kernel/include/kernel/devices/block_device_utils.hpp @@ -9,7 +9,34 @@ namespace kernel::devices::block_device_utils { - auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t; + /** + @brief Utility functions for block devices, such as reading/writing data at specific offsets. These functions handle + the necessary logic to interact with block devices, such as calculating block boundaries and ensuring proper access + patterns. They abstract away the details of block device interactions, providing a simple interface for reading and + writing data to block devices. + */ + + /** + @brief Reads data from a @p device into a @p buffer, starting at a specific @p offset and for a given @p size. + @warning Panics if @p buffer or @p device is null. + @param device The block device to read from. + @param buffer The buffer to read data into. + @param offset The offset on the block device to start reading from. + @param size The number of bytes to read. + @return The number of bytes actually read, which may be less than the requested size. + */ + auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) + -> size_t; + + /** + @brief Writes data from a @p buffer to a @p device, starting at a specific @p offset and for a given @p size. + @warning Panics if @p buffer or @p device is null. + @param device The block device to write to. + @param buffer The buffer to write data from. + @param offset The offset on the block device to start writing to. + @param size The number of bytes to write. + @return The number of bytes actually written, which may be less than the requested size. + */ auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) -> size_t; } // namespace kernel::devices::block_device_utils diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index fc85a7d..72d758f 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -12,23 +12,69 @@ namespace kernel::filesystem { + /** + @brief Represents a directory entry (dentry) in the filesystem. A dentry is a node in the directory tree that + represents a file or directory. It contains a reference to its parent dentry, a reference to the associated real + filesystem inode, and a list of child dentries. + */ struct dentry { + /** + @brief Flags for the dentry. + */ enum class dentry_flags : uint32_t { dcache_mounted = 1 << 15 }; + /** + @brief Create a dentry with the given @p parent, associated @p node, and optional @p name. The dentry is initialized + with the provided parent and inode, and the name is stored for lookup purposes. + */ dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name = {}); + /** + @brief Get the associated inode. + @return A reference to the associated inode. + */ [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; + + /** + @brief Get the parent dentry. + @return A reference to the parent dentry. + */ [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + /** + @brief Add a @p child dentry. + @param child The child dentry to add. + */ auto add_child(kstd::shared_ptr const & child) -> void; + + /** + @brief Find a child dentry by @p name. + @param name The name of the child dentry to find. + @return A pointer to the found child dentry, or a null pointer if not found. + */ [[nodiscard]] auto find_child(std::string_view name) const -> kstd::shared_ptr; + /** + @brief Set a @p flag for the dentry. + @param flag The flag to set. + */ auto set_flag(dentry_flags flag) -> void; + + /** + @brief Unset a @p flag for the dentry. + @param flag The flag to unset. + */ auto unset_flag(dentry_flags flag) -> void; + + /** + @brief Check if the dentry has a specific @p flag. + @param flag The flag to check. + @return True if the dentry has the flag, false otherwise. + */ [[nodiscard]] auto has_flag(dentry_flags flag) const -> bool; private: diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 3edeabb..60c39cf 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -13,9 +13,27 @@ namespace kernel::filesystem::devfs { + /** + @brief A filesystem for managing device nodes in the virtual filesystem. This filesystem provides a way to represent + devices as files in the /dev directory, allowing user-space applications to interact with devices using standard file + operations. The devfs filesystem dynamically creates inodes for devices registered in the system, enabling seamless + access to device functionality through the filesystem interface. + */ struct filesystem : kernel::filesystem::filesystem { + /** + @brief Initializes the devfs instance and builds the device inode table. + @param device Backing device passed by the generic filesystem interface (not required by devfs). + @return 0 on success, -1 on failure. + */ auto mount(kstd::shared_ptr const & device) -> int override; + + /** + @brief Looks up an inode by @p name within a @p parent directory. + @param parent The parent directory inode. + @param name The name of the inode to look up. + @return A pointer to the found inode, or a null pointer if not found. + */ auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; diff --git a/kernel/include/kernel/filesystem/devfs/inode.hpp b/kernel/include/kernel/filesystem/devfs/inode.hpp index 9c11edf..c117bd2 100644 --- a/kernel/include/kernel/filesystem/devfs/inode.hpp +++ b/kernel/include/kernel/filesystem/devfs/inode.hpp @@ -7,11 +7,33 @@ namespace kernel::filesystem::devfs { + /** + @brief Inode implementation for the devfs filesystem. + This inode represents root device node in the /dev directory. + */ struct inode : kernel::filesystem::inode { + /** + @brief Create a devfs inode. The inode is initialized with the appropriate kind (directory). + */ inode(); + /** + @brief Reads from the devfs directory inode. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read (always 0 because this inode does not expose file data). + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Writes to the devfs directory inode. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes requested. + @return Number of bytes written (always 0 because writes are not supported for this inode). + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; }; } // namespace kernel::filesystem::devfs diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 18a98f5..c33be2a 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/inode.hpp" #include @@ -10,13 +11,43 @@ namespace kernel::filesystem { + /** + @brief Inode implementation for device inodes in the filesystem. This inode represents a device file that provides + access to a device registered in the system. The device inode allows reading from and writing to the associated + device. + */ struct device_inode : inode { + /** + @brief Create a device inode with the given @p device. + @param device The device to associate with the inode. + */ explicit device_inode(kstd::shared_ptr const & device); + /** + @brief Read data from the device inode (and in the background from the associated device) into a @p buffer, starting + at @p offset and reading @p size bytes. + @param buffer The buffer to read data into. + @param offset The offset to read from. + @param size The number of bytes to read. + @return The number of bytes read. + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Write data to the device inode (and in the background from the associated device) from a @p buffer, starting + at @p offset and writing @p size bytes. + @param buffer The buffer containing data to write. + @param offset The offset to write to. + @param size The number of bytes to write. + @return The number of bytes written. + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + /** + @brief Get the associated device. + @return A reference to the associated device. + */ [[nodiscard]] auto device() const -> kstd::shared_ptr const &; private: diff --git a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp index 2ff91d9..7fbba3f 100644 --- a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp +++ b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp @@ -6,6 +6,9 @@ namespace kernel::filesystem::ext2 { + /** + @brief Represents a block group descriptor in the ext2 filesystem. + */ struct [[gnu::packed]] block_group_descriptor { uint32_t block_bitmap; diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index abab0a6..32374dc 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -18,13 +18,40 @@ namespace kernel::filesystem::ext2 { + /** + @brief A filesystem implementation for the ext2 filesystem format. This class provides methods for mounting an ext2 + filesystem, and looking up inodes. + */ struct filesystem : kernel::filesystem::filesystem { + /** + @brief Initializes the ext2 filesystem with the given @p device. + @param device The device to mount. + @return 0 on success, negative error code on failure. + */ auto mount(kstd::shared_ptr const & device) -> int override; + + /** + @brief Looks up an inode by @p name within a @p parent directory. + @param parent The parent directory inode. + @param name The name of the inode to look up. + @return A pointer to the found inode, or a null pointer if not found. + */ auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + /** + @brief Gets the size of a block in the filesystem. + @return The size of a block in bytes. + */ auto get_block_size() -> size_t; + + /** + @brief Maps an inode block index to a global block number. + @param inode_block_index The index of the block within the inode. + @param data The inode data. + @return The global block number. + */ auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; private: @@ -34,7 +61,7 @@ namespace kernel::filesystem::ext2 auto get_inode_size() -> size_t; auto get_inode_block_count(inode_data const & data) -> uint32_t; - superblock m_superblock; // TODO BA-FS26 initialize + superblock m_superblock{}; kstd::vector m_block_group_descriptors; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 9318008..a1645cd 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -13,6 +13,9 @@ namespace kernel::filesystem::ext2 { struct filesystem; + /** + @brief Represents the data associated with an ext2 inode. + */ struct [[gnu::packed]] inode_data { uint16_t mode; @@ -37,12 +40,36 @@ namespace kernel::filesystem::ext2 struct inode : kernel::filesystem::inode { + /** + @brief Create an ext2 inode associated with the given filesystem. + */ explicit inode(filesystem * fs); + /** + @brief Reads from the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read. + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Writes to the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. + @warning This method is not implemented yet and will panic if called. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes requested. + @return Number of bytes written. + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + /** + @brief The raw inode data as read from the disk. + */ inode_data m_data{}; + + private: filesystem * m_filesystem; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp index 76eb6f5..4097cbb 100644 --- a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -6,6 +6,9 @@ namespace kernel::filesystem::ext2 { + /** + @brief Represents a linked directory entry in the ext2 filesystem. + */ struct [[gnu::packed]] linked_directory_entry { uint32_t inode; diff --git a/kernel/include/kernel/filesystem/ext2/superblock.hpp b/kernel/include/kernel/filesystem/ext2/superblock.hpp index 8e57ae7..41ad935 100644 --- a/kernel/include/kernel/filesystem/ext2/superblock.hpp +++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp @@ -6,6 +6,9 @@ namespace kernel::filesystem::ext2 { + /** + @brief Represents the superblock in the ext2 filesystem. + */ struct [[gnu::packed]] superblock { uint32_t inodes_count; diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 91e2960..5d52c19 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -8,15 +8,49 @@ namespace kernel::filesystem { + /** + @brief A table for managing file descriptors in the filesystem. This class provides methods for adding, retrieving, + and removing open file descriptions. + */ struct file_descriptor_table { + /** + @brief Initialize the global file descriptor table. This method creates the singleton instance of the file + descriptor table. + @warning Panics if called more than once. + */ auto static init() -> void; + + /** + @brief Get the global file descriptor table instance. + @return A reference to the global file descriptor table. + @warning Panics if the file descriptor table has not been initialized. + */ auto static get() -> file_descriptor_table &; + /** + @brief Destructor for the file descriptor table. + */ ~file_descriptor_table() = default; + /** + @brief Add a file to the descriptor table. + @param f The file description to add. + @return The file descriptor index assigned to the file, or -1 on failure. + */ auto add_file(kstd::shared_ptr const & f) -> int; + + /** + @brief Get a file from the descriptor table. + @param fd The file descriptor index to retrieve. + @return A pointer to the requested file description, or a null pointer if not found. + */ [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; + + /** + @brief Remove a file from the descriptor table. + @param fd The file descriptor index to remove. + */ auto remove_file(int fd) -> void; private: diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 0f9de9f..f855380 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -12,16 +12,53 @@ namespace kernel::filesystem { + /** + @brief A base class for implementing filesystems in the kernel. This class provides a common interface for managing + files and directories within the virtual filesystem. + */ struct filesystem { + /** + @brief Virtual destructor for the filesystem. + */ virtual ~filesystem() = default; + /** + @brief Probes the given @p device to determine if it contains a recognizable filesystem, and if so, mounts it and + returns a pointer to the mounted filesystem instance. This method iterates through known filesystem types and + attempts to initialize it with the device until the mount was successful or all types have been tried. + @param device The device to probe and mount. + @return A pointer to the mounted filesystem instance if successful, or a null pointer if no recognizable filesystem + is found on the device. + @warning Panics if @p device is null. + */ auto static probe_and_mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; + /** + @brief Initializes the filesystem with the given @p device. + @param device The device to mount. + @return 0 on success, or a negative error code on failure. + */ virtual auto mount(kstd::shared_ptr const & device) -> int; + + /** + @brief Looks up a child inode within the given @p parent inode with the specified @p name. This method must be + implemented by concrete filesystem subclasses to provide the logic for traversing the filesystem structure and + finding the requested inode. + @param parent The parent inode. + @param name The name of the child inode to look up. + @return A pointer to the requested child inode, or a null pointer if not found. + */ virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; + /** + @brief Returns a reference to the root inode of the filesystem. + */ [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; + + /** + @brief Returns a reference to the device associated with the filesystem. + */ [[nodiscard]] auto device() const -> kstd::shared_ptr const &; protected: diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 59207df..7237184 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -5,8 +5,14 @@ namespace kernel::filesystem { + /** + @brief Represents an inode in the filesystem. + */ struct inode { + /** + @brief Represents the kind of an inode. + */ enum class inode_kind { regular, @@ -14,15 +20,53 @@ namespace kernel::filesystem device }; + /** + @brief Create an inode with the given @p kind. + @param kind The kind of the inode. + */ explicit inode(inode_kind kind); + /** + @brief Virtual destructor for the inode. + */ virtual ~inode() = default; + /** + @brief Reads from the inode into a @p buffer, starting at the specified @p offset and for a given @p size. This + method must be implemented by concrete inode subclasses. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read. + */ virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + + /** + @brief Writes to the inode into a @p buffer, starting at the specified @p offset and for a given @p size. This + method must be implemented by concrete inode subclasses. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes to write. + @return Number of bytes written. + */ virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + /** + @brief Returns whether the inode is a directory. + @return true if the inode is a directory, false otherwise. + */ [[nodiscard]] auto is_directory() const -> bool; + + /** + @brief Returns whether the inode is a regular file. + @return true if the inode is a regular file, false otherwise. + */ [[nodiscard]] auto is_regular() const -> bool; + + /** + @brief Returns whether the inode is a device. + @return true if the inode is a device, false otherwise. + */ [[nodiscard]] auto is_device() const -> bool; // TODO BA-FS26 improve inode_kind really needed? diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index a054750..3e3c69f 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -11,15 +11,41 @@ namespace kernel::filesystem { + /** + @brief Represents a mounted filesystem in the kernel. + */ struct mount { + /** + @brief Creates a mount for the given @p filesystem at the specified @p mount_path. The @p mount_dentry represents + the dentry where the filesystem is mounted, and the @p root_dentry represents the root dentry of the mounted + filesystem. + @param mount_dentry The dentry where the filesystem is mounted. + @param root_dentry The root dentry of the mounted filesystem. + @param fs The filesystem instance being mounted. + @param mount_path The path at which the filesystem is mounted. + */ mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, kstd::shared_ptr const & fs, std::string_view mount_path); - [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr; + /** + @brief Get the dentry where the filesystem is mounted. + */ + [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr const &; + + /** + @brief Get the root dentry of the mounted filesystem. + */ [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; + /** + @brief Get the filesystem instance being mounted. + */ [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; + + /** + @brief Get the path at which the filesystem is mounted. + */ [[nodiscard]] auto get_mount_path() const -> std::string_view; private: diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 6dc2218..a8ef59e 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -10,11 +10,23 @@ namespace kernel::filesystem { + /** + @brief A table for managing mounted filesystems in the kernel. + */ struct mount_table { - public: - void add_mount(kstd::shared_ptr); + /** + @brief Adds a mount to the table. + @param mount The mount to add. + */ + void add_mount(kstd::shared_ptr const & mount); + /** + @brief Finds the mount with the longest prefix matching the given @p path. This method is used to determine which + mounted filesystem should handle a given path lookup. + @param path The path to match against the mount paths in the table. + @return A pointer to the mount with the longest matching prefix, or a null pointer if no mount matches the path. + */ [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr; private: diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index 45719cf..ed878a7 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -9,13 +9,41 @@ namespace kernel::filesystem { + /** + @brief Represents an open file description in the filesystem. This class encapsulates the state of an open file, + including a reference to the associated inode and the current file offset. + */ struct open_file_description { + /** + @brief Constructs an open file description for the given @p inode. + @param inode The inode to associate with the open file description. + */ explicit open_file_description(kstd::shared_ptr const & inode); + /** + @brief Destructor for the open file description. + */ ~open_file_description() = default; + /** + @brief Reads data from the open file description into a @p buffer, starting at the current file offset and for a + given + @p size. The file offset is advanced by the number of bytes read. + @param buffer The buffer to read data into. + @param size The number of bytes to read. + @return The number of bytes read. + */ auto read(void * buffer, size_t size) -> size_t; + + /** + @brief Writes data to the open file description from a @p buffer, starting at the current file offset and for a + given + @p size. The file offset is advanced by the number of bytes written. + @param buffer The buffer to write data from. + @param size The number of bytes to write. + @return The number of bytes written. + */ auto write(void const * buffer, size_t size) -> size_t; private: diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index 7931d87..b7e7c6f 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -14,9 +14,27 @@ namespace kernel::filesystem::rootfs { + /** + @brief A filesystem for the root filesystem. This filesystem provides access to the root directory and its contents, + which are typically populated by the init process during system startup. The rootfs filesystem serves as the top-level + directory in the filesystem hierarchy. It is responsible for providing a stable and consistent interface to the root + directory. + */ struct filesystem : kernel::filesystem::filesystem { + /** + @brief Initializes the rootfs filesystem with the given @p device. + @param device The device to mount (not required by rootfs). + @return 0 on success, negative error code on failure. + */ auto mount(kstd::shared_ptr const & device) -> int override; + + /** + @brief Looks up an inode by @p name within a @p parent directory. + @param parent The parent directory inode. + @param name The name of the inode to look up. + @return A pointer to the found inode, or a null pointer if not found. + */ auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; }; diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 24d3e6b..469e47a 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -13,14 +13,48 @@ namespace kernel::filesystem::rootfs { + /** + @brief Represents an inode in the rootfs filesystem. This inode represents a directory in the root filesystem and + maintains a list of child inodes corresponding to files and subdirectories within the root directory. The rootfs inode + provides methods for reading and writing data (which are no-ops for the root directory), as well as adding and looking + up child inodes by name. + */ struct inode : kernel::filesystem::inode { + /** + @brief Create a rootfs inode. The inode is initialized with the appropriate kind (directory). + */ inode(); + /** + @brief Reads from the rootfs directory inode. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read (always 0 because this inode does not expose file data). + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Writes to the rootfs directory inode. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes requested. + @return Number of bytes written (always 0 because writes are not supported for this inode). + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + /** + @brief Adds a child inode to the rootfs directory inode with the specified @p name. + @param name The name of the child inode. + */ auto add_child(std::string_view name) -> void; + + /** + @brief Looks up a child inode by @p name. + @param name The name of the child inode to look up. + @return A pointer to the found child inode, or a null pointer if not found. + */ auto lookup_child(std::string_view name) -> kstd::shared_ptr; private: diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 5823a83..2d05765 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -12,14 +12,45 @@ namespace kernel::filesystem { + /** + @brief The virtual filesystem (VFS) is responsible for managing mounted filesystems and providing a unified interface + for file operations across different filesystem types. The VFS maintains a mount table to keep track of mounted + filesystems and their associated mount points. It provides methods for opening files by path, which involvesresolving + the path to the appropriate mounted filesystem and delegating the file operation to that filesystem's implementation. + */ struct vfs { + /** + @brief Initialize the virtual filesystem. + @warning Panics if the VFS has already been initialized. + */ auto static init() -> void; + + /** + @brief Get the singleton instance of the virtual filesystem. + @return A reference to the VFS instance. + @warning Panics if the VFS has not been initialized yet. + */ auto static get() -> vfs &; + /** + @brief Destructor for the VFS. + */ ~vfs() = default; + /** + @brief Open a file by its @p path. This method resolves the path and creates an open file description. + @param path The path to the file to open. + @return A shared pointer to the open file description or a null pointer if the file could not be opened. + */ auto open(std::string_view path) -> kstd::shared_ptr; + + /** + @brief Mount a @p filesystem at a specific @p path. + @param path The path where the filesystem should be mounted. + @param filesystem The filesystem to mount. + @return 0 on success, or a negative error code on failure. + */ auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int; private: diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index d7c989c..7ee1072 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -36,6 +36,10 @@ namespace kernel::filesystem::ext2 constexpr uint16_t S_IFREG = 0x8000; constexpr uint16_t S_IFDIR = 0x4000; + // Error codes + constexpr int INVALID_MAGIC_NUMBER = -1; + constexpr int INVALID_ROOT_INODE = -2; + auto S_ISREG(uint16_t mode) -> bool { return (mode & S_IFMT) == S_IFREG; @@ -55,7 +59,7 @@ namespace kernel::filesystem::ext2 if (m_superblock.magic != MAGIC_NUMBER) { - return -1; + return INVALID_MAGIC_NUMBER; } auto const block_size = get_block_size(); @@ -70,7 +74,11 @@ namespace kernel::filesystem::ext2 num_block_groups * sizeof(block_group_descriptor)); m_root_inode = read_inode(ROOT_INODE_NUMBER); - // TODO BA-FS26 check if root inode is valid and is a directory ?? + + if (!m_root_inode || !m_root_inode->is_directory()) + { + return INVALID_ROOT_INODE; + } return 0; } diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 7f4cf69..a29bb3b 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -53,7 +53,7 @@ namespace kernel::filesystem::ext2 auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t { - // TODO BA-FS26 implement + kapi::system::panic("[EXT2] inode::write is not implemented yet"); return 0; } } // namespace kernel::filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 26c57b6..b08b520 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -36,11 +36,12 @@ namespace kernel::filesystem } } - kapi::system::panic("[FILESYSTEM] cannot mount filesystem: no suitable filesystem found on device."); + return nullptr; } auto filesystem::mount(kstd::shared_ptr const & device) -> int { + // TODO BA-FS26 maybe check if device is null and panic here already? m_device = device; return 0; } diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index f9e709c..a6d2f7e 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -25,7 +25,7 @@ namespace kernel::filesystem } } - auto mount::get_mount_dentry() const -> kstd::shared_ptr + auto mount::get_mount_dentry() const -> kstd::shared_ptr const & { return m_mount_dentry; } diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 737434e..195f48a 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -9,7 +9,7 @@ namespace kernel::filesystem { - void mount_table::add_mount(kstd::shared_ptr mount) + void mount_table::add_mount(kstd::shared_ptr const & mount) { m_mounts.push_back(mount); } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index f9a051a..2578036 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -21,6 +21,11 @@ namespace kernel::filesystem namespace { constinit auto static active_vfs = std::optional{}; + + // Error codes + constexpr int INVALID_PATH = -1; + constexpr int MOUNT_POINT_NOT_FOUND = -2; + constexpr int FILESYSTEM_NULL = -3; } // namespace auto vfs::init() -> void @@ -79,18 +84,12 @@ namespace kernel::filesystem { if (!filesystem) { - return -1; // TODO BA-FS26 panic or errorcode? - } - - if (path.empty() || path.front() != '/') - { - return -1; // TODO BA-FS26 panic or errorcode? + return FILESYSTEM_NULL; } - // TODO BA-FS26 better path validation - if ((path.size() > 1 && path.back() == '/')) + if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) { - return -1; // TODO BA-FS26 panic or errorcode? + return INVALID_PATH; } if (auto mount_point_dentry = resolve_path(path)) @@ -99,7 +98,7 @@ namespace kernel::filesystem return 0; } - return -1; + return MOUNT_POINT_NOT_FOUND; } auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, -- cgit v1.2.3 From 9c0fb15aa67a4dda6beed3cbdfc4cc510674313f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 14:13:19 +0200 Subject: add test with multiple correct formatted ext2 file systems increase QEMU memory --- kernel/src/main.cpp | 69 ++++++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 40 deletions(-) (limited to 'kernel') diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index a2c531f..1893b84 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -111,51 +111,40 @@ auto test_device_with_vfs() -> void auto test_file_lookup() -> void { - // TODO BA-FS26 implement a more complete test with multiple files and directories and mounts etc. - auto vfs = kernel::filesystem::vfs::get(); - auto storage_mgmt = kernel::devices::storage::management::get(); - - auto ofd_hello = vfs.open("/hello.txt"); - - kstd::vector buffer(64); - auto number_of_read_bytes = ofd_hello->read(buffer.data(), buffer.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer); - - std::string_view hello_str{reinterpret_cast(buffer.data()), number_of_read_bytes}; - kstd::println("hello_str: {}", hello_str); - - auto ofd1 = vfs.open("/a/b/c"); - auto ofd2 = vfs.open("/dev/ram0"); - auto ofd3 = vfs.open("/a/d/e"); - if (!ofd1 || !ofd2 || !ofd3) - { - kstd::os::panic("test code failed"); - } - - if (auto ofd4 = vfs.open("/dev/xxx")) - { - kstd::os::panic("test code failed"); - } + auto read_and_write_file = [&vfs](std::string_view path) { + kstd::println("[TEST] Reading and writing file at path: {}", path); + auto ofd = vfs.open(path); + if (!ofd) + { + kstd::os::panic("test code failed"); + } + + kstd::vector buffer{32}; + auto number_of_read_bytes = ofd->read(buffer.data(), buffer.size()); + kstd::println("read bytes: {}", number_of_read_bytes); + kstd::println("buffer: {::#04x}", buffer); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), number_of_read_bytes}; + kstd::println("buffer_as_str: {}", buffer_as_str); + }; + + read_and_write_file("/info.txt"); + read_and_write_file("/enclosures/info.txt"); + read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); + read_and_write_file("/enclosures/elephant_house/elephant_1.txt"); + read_and_write_file( + "/enclosures/aquarium/tank_2/" + "this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_" + "limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt"); + auto storage_mgmt = kernel::devices::storage::management::get(); auto device = storage_mgmt.device_by_major_minor(1, 16); auto new_filesystem = kernel::filesystem::filesystem::probe_and_mount(device); - if (vfs.do_mount("/a/b", new_filesystem) != 0) - { - kstd::os::panic("test code failed"); - } - auto ofd5 = vfs.open("/a/b/c"); - if (!ofd5) - { - kstd::os::panic("test code failed"); - } - - if (auto ofd6 = vfs.open("x/y/z")) - { - kstd::os::panic("test code failed"); - } + vfs.do_mount("/enclosures/aquarium", new_filesystem); + read_and_write_file("/enclosures/aquarium/closed.txt"); + read_and_write_file("/enclosures/aquarium/information/info_2.txt"); } auto run_test_code() -> void -- cgit v1.2.3 From 2793770dc6eba30b73b4a4993618d2cbe184790e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 15:21:10 +0200 Subject: implement unmount, improve error codes --- .../include/kernel/filesystem/devfs/filesystem.hpp | 4 +- .../include/kernel/filesystem/ext2/filesystem.hpp | 4 +- kernel/include/kernel/filesystem/filesystem.hpp | 12 +++- kernel/include/kernel/filesystem/mount.hpp | 10 +++- kernel/include/kernel/filesystem/mount_table.hpp | 21 ++++++- .../kernel/filesystem/rootfs/filesystem.hpp | 4 +- kernel/include/kernel/filesystem/vfs.hpp | 23 +++++++- kernel/src/filesystem/devfs/filesystem.cpp | 5 +- kernel/src/filesystem/ext2/filesystem.cpp | 12 ++-- kernel/src/filesystem/filesystem.cpp | 6 +- kernel/src/filesystem/mount.cpp | 9 ++- kernel/src/filesystem/mount_table.cpp | 69 +++++++++++++++++++++- kernel/src/filesystem/rootfs/filesystem.cpp | 4 +- kernel/src/filesystem/vfs.cpp | 45 +++++++++----- kernel/src/main.cpp | 3 + 15 files changed, 187 insertions(+), 44 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 60c39cf..137eca3 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -24,9 +24,9 @@ namespace kernel::filesystem::devfs /** @brief Initializes the devfs instance and builds the device inode table. @param device Backing device passed by the generic filesystem interface (not required by devfs). - @return 0 on success, -1 on failure. + @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 32374dc..65324c8 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -27,9 +27,9 @@ namespace kernel::filesystem::ext2 /** @brief Initializes the ext2 filesystem with the given @p device. @param device The device to mount. - @return 0 on success, negative error code on failure. + @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index f855380..ef6929a 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -18,6 +18,14 @@ namespace kernel::filesystem */ struct filesystem { + enum class operation_result : int + { + success = 0, + invalid_magic_number = -1, + invalid_root_inode = -2, + unmount_failed = -3 + }; + /** @brief Virtual destructor for the filesystem. */ @@ -37,9 +45,9 @@ namespace kernel::filesystem /** @brief Initializes the filesystem with the given @p device. @param device The device to mount. - @return 0 on success, or a negative error code on failure. + @return The result of the mount operation. */ - virtual auto mount(kstd::shared_ptr const & device) -> int; + virtual auto mount(kstd::shared_ptr const & device) -> operation_result; /** @brief Looks up a child inode within the given @p parent inode with the specified @p name. This method must be diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 3e3c69f..0ac6b2f 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -24,9 +24,11 @@ namespace kernel::filesystem @param root_dentry The root dentry of the mounted filesystem. @param fs The filesystem instance being mounted. @param mount_path The path at which the filesystem is mounted. + @param parent_mount The parent mount that this mount is attached beneath. */ mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, std::string_view mount_path); + kstd::shared_ptr const & fs, std::string_view mount_path, + kstd::shared_ptr const & parent_mount); /** @brief Get the dentry where the filesystem is mounted. @@ -48,11 +50,17 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_mount_path() const -> std::string_view; + /** + @brief Get the parent mount that this mount was attached beneath. + */ + [[nodiscard]] auto get_parent_mount() const -> kstd::shared_ptr const &; + private: kstd::string m_mount_path; kstd::shared_ptr m_mount_dentry; kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; + kstd::shared_ptr m_parent_mount{}; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index a8ef59e..a5cdde6 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -15,11 +15,28 @@ namespace kernel::filesystem */ struct mount_table { + /** + @brief Results for mount table operations. + */ + enum class operation_result : int + { + removed = 0, + has_child_mounts = -1, + mount_not_found = -2 + }; + /** @brief Adds a mount to the table. @param mount The mount to add. */ - void add_mount(kstd::shared_ptr const & mount); + auto add_mount(kstd::shared_ptr const & mount) -> void; + + /** + @brief Removes the topmost mount at the given @p path. + @param path The mount path to remove. + @return The result of the removal operation. + */ + [[nodiscard]] auto remove_mount(std::string_view path) -> operation_result; /** @brief Finds the mount with the longest prefix matching the given @p path. This method is used to determine which @@ -30,6 +47,8 @@ namespace kernel::filesystem [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr; private: + [[nodiscard]] auto has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool; + kstd::vector> m_mounts; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index b7e7c6f..0155c41 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -25,9 +25,9 @@ namespace kernel::filesystem::rootfs /** @brief Initializes the rootfs filesystem with the given @p device. @param device The device to mount (not required by rootfs). - @return 0 on success, negative error code on failure. + @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 2d05765..4dd2a83 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -20,6 +20,18 @@ namespace kernel::filesystem */ struct vfs { + /** + @brief Results for VFS operations. + */ + enum class operation_result : int + { + success = 0, + invalid_path = -1, + mount_point_not_found = -2, + filesystem_null = -3, + unmount_failed = -4 + }; + /** @brief Initialize the virtual filesystem. @warning Panics if the VFS has already been initialized. @@ -49,9 +61,16 @@ namespace kernel::filesystem @brief Mount a @p filesystem at a specific @p path. @param path The path where the filesystem should be mounted. @param filesystem The filesystem to mount. - @return 0 on success, or a negative error code on failure. + @return The result of the mount operation. + */ + auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> operation_result; + + /** + @brief Unmount the filesystem mounted at the specified @p path. + @param path The path where the filesystem is mounted. + @return The result of the unmount operation. */ - auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int; + auto unmount(std::string_view path) -> operation_result; private: vfs() = default; diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index 9043ac5..03b4218 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/devfs/filesystem.hpp" #include "kapi/devices/device.hpp" + #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/devfs/inode.hpp" #include "kernel/filesystem/device_inode.hpp" @@ -13,12 +14,12 @@ namespace kernel::filesystem::devfs { - auto filesystem::mount(kstd::shared_ptr const &) -> int + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { m_root_inode = kstd::make_shared(); build_device_inode_table(); - return 0; + return operation_result::success; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 7ee1072..6d5960e 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -36,10 +36,6 @@ namespace kernel::filesystem::ext2 constexpr uint16_t S_IFREG = 0x8000; constexpr uint16_t S_IFDIR = 0x4000; - // Error codes - constexpr int INVALID_MAGIC_NUMBER = -1; - constexpr int INVALID_ROOT_INODE = -2; - auto S_ISREG(uint16_t mode) -> bool { return (mode & S_IFMT) == S_IFREG; @@ -51,7 +47,7 @@ namespace kernel::filesystem::ext2 } } // namespace - auto filesystem::mount(kstd::shared_ptr const & device) -> int + auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result { kernel::filesystem::filesystem::mount(device); @@ -59,7 +55,7 @@ namespace kernel::filesystem::ext2 if (m_superblock.magic != MAGIC_NUMBER) { - return INVALID_MAGIC_NUMBER; + return operation_result::invalid_magic_number; } auto const block_size = get_block_size(); @@ -77,9 +73,9 @@ namespace kernel::filesystem::ext2 if (!m_root_inode || !m_root_inode->is_directory()) { - return INVALID_ROOT_INODE; + return operation_result::invalid_root_inode; } - return 0; + return operation_result::success; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index b08b520..da2838d 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -30,7 +30,7 @@ namespace kernel::filesystem for (auto & factory : filesystem_factories) { auto fs = factory(); - if (fs->mount(device) == 0) + if (fs->mount(device) == operation_result::success) { return fs; } @@ -39,11 +39,11 @@ namespace kernel::filesystem return nullptr; } - auto filesystem::mount(kstd::shared_ptr const & device) -> int + auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result { // TODO BA-FS26 maybe check if device is null and panic here already? m_device = device; - return 0; + return operation_result::success; } auto filesystem::root_inode() const -> kstd::shared_ptr const & diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index a6d2f7e..d165385 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -13,11 +13,13 @@ namespace kernel::filesystem { mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, std::string_view mount_path) + kstd::shared_ptr const & fs, std::string_view mount_path, + kstd::shared_ptr const & parent_mount) : m_mount_path(mount_path) , m_mount_dentry(mount_dentry) , m_root_dentry(root_dentry) , m_filesystem(fs) + , m_parent_mount(parent_mount) { if (!m_filesystem) { @@ -44,4 +46,9 @@ namespace kernel::filesystem { return m_mount_path.view(); } + + auto mount::get_parent_mount() const -> kstd::shared_ptr const & + { + return m_parent_mount; + } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 195f48a..3b1dee3 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -1,17 +1,83 @@ #include "kernel/filesystem/mount_table.hpp" +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/mount.hpp" #include +#include +#include #include +#include #include namespace kernel::filesystem { + namespace + { + auto is_descendant_of(kstd::shared_ptr const & candidate, kstd::shared_ptr const & ancestor) -> bool + { + for (auto current = candidate; current; current = current->get_parent_mount()) + { + if (current == ancestor) + { + return true; + } + } + + return false; + } + + auto is_strict_prefix(std::string_view prefix, std::string_view path) -> bool + { + return prefix != "/" && path.starts_with(prefix) && path.size() > prefix.size() && path[prefix.size()] == '/'; + } + + auto is_visible_mount(kstd::shared_ptr const & candidate, + kstd::vector> const & mounts) -> bool + { + return std::ranges::none_of(mounts, [&](auto const & other) { + return other != candidate && is_strict_prefix(other->get_mount_path(), candidate->get_mount_path()) && + !is_descendant_of(candidate, other); + }); + } + } // namespace + + auto mount_table::has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool + { + return std::ranges::any_of( + m_mounts, [&parent_mount](auto const & mount) { return mount->get_parent_mount() == parent_mount; }); + } + void mount_table::add_mount(kstd::shared_ptr const & mount) { m_mounts.push_back(mount); + if (auto mount_dentry = mount->get_mount_dentry()) + { + mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); + } + } + + auto mount_table::remove_mount(std::string_view path) -> operation_result + { + auto mount_it = std::ranges::find_if(std::ranges::reverse_view(m_mounts), [&](auto const & mount) { + return mount->get_mount_path() == path && is_visible_mount(mount, m_mounts); + }); + + if (mount_it == std::ranges::reverse_view(m_mounts).end()) + { + return operation_result::mount_not_found; + } + + auto const & mount = *mount_it; + if (has_child_mounts(mount)) + { + return operation_result::has_child_mounts; + } + + mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::dcache_mounted); + m_mounts.erase(std::ranges::find(m_mounts, mount)); + return operation_result::removed; } auto mount_table::find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr @@ -25,8 +91,9 @@ namespace kernel::filesystem // /a/b/c should match /a/b but not /a/bb or /a/b/c/d, / should match everything bool is_prefix = path.starts_with(mp) && (mp == "/" || path.size() == mp.size() || path[mp.size()] == '/'); + bool visible = is_visible_mount(mount, m_mounts); - if (is_prefix && mp.size() >= best_len) + if (is_prefix && visible && mp.size() >= best_len) { mount_with_longest_prefix = mount; best_len = mp.size(); diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index a7c746e..dffef99 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -11,13 +11,13 @@ namespace kernel::filesystem::rootfs { - auto filesystem::mount(kstd::shared_ptr const &) -> int + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { auto rfs_inode = kstd::make_shared(); rfs_inode->add_child("dev"); m_root_inode = rfs_inode; - return 0; + return operation_result::success; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 2578036..45ae053 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -7,6 +7,7 @@ #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/open_file_description.hpp" #include "kernel/filesystem/rootfs/filesystem.hpp" @@ -21,11 +22,6 @@ namespace kernel::filesystem namespace { constinit auto static active_vfs = std::optional{}; - - // Error codes - constexpr int INVALID_PATH = -1; - constexpr int MOUNT_POINT_NOT_FOUND = -2; - constexpr int FILESYSTEM_NULL = -3; } // namespace auto vfs::init() -> void @@ -45,7 +41,7 @@ namespace kernel::filesystem root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode()); - m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "")); + m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "", nullptr)); auto storage_mgmt = devices::storage::management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) @@ -79,36 +75,55 @@ namespace kernel::filesystem return nullptr; } - // TODO BA-FS26 implement unmount - auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int + auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> operation_result { if (!filesystem) { - return FILESYSTEM_NULL; + return operation_result::filesystem_null; } if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) { - return INVALID_PATH; + return operation_result::invalid_path; } if (auto mount_point_dentry = resolve_path(path)) { do_mount_internal(path, mount_point_dentry, filesystem); - return 0; + return operation_result::success; + } + + return operation_result::mount_point_not_found; + } + + auto vfs::unmount(std::string_view path) -> operation_result + { + if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) + { + 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; + } + + if (remove_result == mount_table::operation_result::has_child_mounts) + { + return operation_result::unmount_failed; } - return MOUNT_POINT_NOT_FOUND; + return operation_result::mount_point_not_found; } auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void { - // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) + auto parent_mount = m_mount_table.find_longest_prefix_mount(path); auto new_fs_root = kstd::make_shared(mount_point_dentry, fs->root_inode()); - auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path, parent_mount); m_mount_table.add_mount(new_mount); - mount_point_dentry->set_flag(dentry::dentry_flags::dcache_mounted); } auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 1893b84..1d73e20 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -145,6 +145,9 @@ auto test_file_lookup() -> void vfs.do_mount("/enclosures/aquarium", new_filesystem); read_and_write_file("/enclosures/aquarium/closed.txt"); read_and_write_file("/enclosures/aquarium/information/info_2.txt"); + + vfs.unmount("/enclosures/aquarium"); + read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); } auto run_test_code() -> void -- cgit v1.2.3 From 5865dac062f3291b8081eca39c2835015d2c43c6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 15:48:32 +0200 Subject: extend tests with another filesystem --- kernel/src/main.cpp | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 1d73e20..79ed703 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -139,15 +139,37 @@ auto test_file_lookup() -> void "limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt"); auto storage_mgmt = kernel::devices::storage::management::get(); - auto device = storage_mgmt.device_by_major_minor(1, 16); - auto new_filesystem = kernel::filesystem::filesystem::probe_and_mount(device); + auto device_1 = storage_mgmt.device_by_major_minor(1, 16); + auto fs_1 = kernel::filesystem::filesystem::probe_and_mount(device_1); - vfs.do_mount("/enclosures/aquarium", new_filesystem); + vfs.do_mount("/enclosures/aquarium", fs_1); read_and_write_file("/enclosures/aquarium/closed.txt"); read_and_write_file("/enclosures/aquarium/information/info_2.txt"); vfs.unmount("/enclosures/aquarium"); read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); + + auto device_2 = storage_mgmt.device_by_major_minor(1, 32); + auto fs_2 = kernel::filesystem::filesystem::probe_and_mount(device_2); + + vfs.do_mount("/enclosures/elephant_house", fs_2); + read_and_write_file("/enclosures/elephant_house/monkey_house/infrastructure/info.txt"); + + vfs.do_mount("/enclosures/elephant_house/monkey_house", fs_1); + read_and_write_file("/enclosures/elephant_house/monkey_house/information/info_2.txt"); + + auto result = vfs.unmount("/enclosures/elephant_house"); + if (result == kernel::filesystem::vfs::operation_result::unmount_failed) + { + kstd::println("[TEST] Unmount failed as expected due to active child mount."); + } + + vfs.unmount("/enclosures/elephant_house/monkey_house"); + result = vfs.unmount("/enclosures/elephant_house"); + if (result == kernel::filesystem::vfs::operation_result::success) + { + kstd::println("[TEST] Unmount succeeded after unmounting child mount."); + } } auto run_test_code() -> void -- cgit v1.2.3 From e2c08ddb3d79f946399ca5d3bc07b4e6c4de9328 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 16:21:33 +0200 Subject: add dentry tests --- kernel/CMakeLists.txt | 3 + kernel/include/kernel/filesystem/dentry.hpp | 15 ++- kernel/src/filesystem/dentry.cpp | 9 +- kernel/src/filesystem/dentry.tests.cpp | 136 ++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 kernel/src/filesystem/dentry.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 74233cb..85c7661 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -126,6 +126,9 @@ else() "src/memory/bitmap_allocator.tests.cpp" "src/memory/block_list_allocator.tests.cpp" + # Filesystem Subsystem Tests + "src/filesystem/dentry.tests.cpp" + # Storage Subsystem Tests "src/devices/storage/ram_disk/device.tests.cpp" ) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 72d758f..58a918f 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -28,10 +28,13 @@ namespace kernel::filesystem }; /** - @brief Create a dentry with the given @p parent, associated @p node, and optional @p name. The dentry is initialized - with the provided parent and inode, and the name is stored for lookup purposes. + @brief Create a dentry with the given @p parent, associated @p inode, and optional @p name. The dentry is + initialized with the provided parent and inode, and the name is stored for lookup purposes. + @param parent The parent dentry. + @param inode The associated inode for this dentry. + @param name The name of the dentry (optional). */ - dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name = {}); + dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & inode, std::string_view name = {}); /** @brief Get the associated inode. @@ -45,6 +48,12 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + /** + @brief Get the name of the dentry. + @return The name of the dentry. + */ + [[nodiscard]] auto get_name() const -> std::string_view; + /** @brief Add a @p child dentry. @param child The child dentry to add. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 2f99e91..6591011 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -12,10 +12,10 @@ namespace kernel::filesystem { - dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name) + dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & inode, std::string_view name) : m_name(name) , m_parent(parent) - , m_inode(node) + , m_inode(inode) { if (!m_inode) { @@ -33,6 +33,11 @@ namespace kernel::filesystem return m_parent; } + auto dentry::get_name() const -> std::string_view + { + return m_name.view(); + } + auto dentry::add_child(kstd::shared_ptr const & child) -> void { m_children.push_back(child); diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp new file mode 100644 index 0000000..f82024a --- /dev/null +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -0,0 +1,136 @@ +#include "kernel/filesystem/dentry.hpp" + +#include "kernel/filesystem/devfs/inode.hpp" +#include "kernel/test_support/cio.hpp" +#include "kernel/test_support/cpu.hpp" + +#include +#include + +#include + +SCENARIO("Dentry construction", "[filesystem][dentry]") +{ + GIVEN("A parent dentry and inode") + { + auto inode = kstd::make_shared(); + auto parent_dentry = kstd::make_shared(nullptr, inode); + + 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.get_parent() == parent_dentry); + REQUIRE(child_dentry.get_inode() == inode); + REQUIRE(child_dentry.get_name() == "child"); + } + + THEN("no flag is set") + { + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + WHEN("constructing a dentry with an empty name") + { + auto child_dentry = kernel::filesystem::dentry{parent_dentry, inode}; + + THEN("the dentry has the correct parent and inode, and an empty name") + { + REQUIRE(child_dentry.get_parent() == parent_dentry); + REQUIRE(child_dentry.get_inode() == inode); + REQUIRE(child_dentry.get_name().empty()); + } + + THEN("no flag is set") + { + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + 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.get_parent() == nullptr); + REQUIRE(child_dentry.get_inode() == inode); + REQUIRE(child_dentry.get_name() == "child"); + } + + THEN("no flag is set") + { + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + 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(); + auto parent_dentry = kstd::make_shared(nullptr, inode); + + WHEN("adding child dentries") + { + auto child1 = kstd::make_shared(parent_dentry, inode, "child1"); + auto child2 = kstd::make_shared(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(); + auto dentry = kernel::filesystem::dentry{nullptr, inode, "test"}; + + WHEN("setting a flag") + { + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + + THEN("the flag is set") + { + REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + WHEN("unsetting a flag") + { + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + + THEN("the flag is unset") + { + REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + } +} -- cgit v1.2.3 From bf90905a5ded3995af5677685bc31228d485e64b Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 18:52:11 +0200 Subject: add test block_device --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/devices/block_device.hpp | 31 ++++++++++++ kernel/src/test_support/devices/block_device.cpp | 58 ++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 kernel/include/kernel/test_support/devices/block_device.hpp create mode 100644 kernel/src/test_support/devices/block_device.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 85c7661..12683a1 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -101,6 +101,7 @@ else() "src/test_support/kapi/cio.cpp" "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" + "src/test_support/devices/block_device.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/devices/block_device.hpp b/kernel/include/kernel/test_support/devices/block_device.hpp new file mode 100644 index 0000000..2327fc4 --- /dev/null +++ b/kernel/include/kernel/test_support/devices/block_device.hpp @@ -0,0 +1,31 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_BLOCK_DEVICE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_BLOCK_DEVICE_HPP + +#include "kernel/devices/block_device.hpp" + +#include +#include + +#include +#include + +namespace kernel::tests::devices +{ + + struct block_device : kernel::devices::block_device + { + block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size); + + auto init() -> bool override; + + auto read_block(size_t block_index, void * buffer) const -> void override; + auto write_block(size_t block_index, void const * buffer) -> void override; + + [[nodiscard]] auto size() const -> size_t override; + + kstd::vector data{}; + }; + +} // namespace kernel::tests::devices + +#endif \ No newline at end of file diff --git a/kernel/src/test_support/devices/block_device.cpp b/kernel/src/test_support/devices/block_device.cpp new file mode 100644 index 0000000..bce415f --- /dev/null +++ b/kernel/src/test_support/devices/block_device.cpp @@ -0,0 +1,58 @@ +#include "kernel/test_support/devices/block_device.hpp" + +#include "kernel/devices/block_device.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +namespace kernel::tests::devices +{ + block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size) + : kernel::devices::block_device(major, minor, name, block_size) + {} + + auto block_device::init() -> bool + { + return true; + } + + auto block_device::read_block(size_t block_index, void * buffer) const -> void + { + auto const offset = block_index * block_size(); + if (offset >= data.size()) + { + kstd::libc::memset(buffer, 0, block_size()); + return; + } + + auto const bytes_to_read = std::min(block_size(), data.size() - offset); + kstd::libc::memcpy(buffer, data.data() + offset, bytes_to_read); + if (bytes_to_read < block_size()) + { + kstd::libc::memset(static_cast(buffer) + bytes_to_read, 0, block_size() - bytes_to_read); + } + } + + auto block_device::write_block(size_t block_index, void const * buffer) -> void + { + auto const offset = block_index * block_size(); + auto const write_end = offset + block_size(); + if (write_end > data.size()) + { + data.resize(write_end, 0); + } + + kstd::libc::memcpy(data.data() + offset, static_cast(buffer), block_size()); + } + + auto block_device::size() const -> size_t + { + return data.size(); + } +} // namespace kernel::tests::devices \ No newline at end of file -- cgit v1.2.3 From 63c6299262411fc06afca4f224e00e1b2d25eb25 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:01:16 +0200 Subject: clean up includes --- kernel/src/filesystem/dentry.tests.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'kernel') diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index f82024a..94aa48d 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -1,7 +1,6 @@ #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/inode.hpp" -#include "kernel/test_support/cio.hpp" #include "kernel/test_support/cpu.hpp" #include -- cgit v1.2.3 From ad2a744960ef8359a40e25c81f2b5fee0d8952c4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:17:08 +0200 Subject: add block_device_utils tests --- kernel/CMakeLists.txt | 1 + kernel/src/devices/block_device_utils.cpp | 2 +- kernel/src/devices/block_device_utils.tests.cpp | 189 ++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 kernel/src/devices/block_device_utils.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 12683a1..be21fc3 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -131,6 +131,7 @@ else() "src/filesystem/dentry.tests.cpp" # Storage Subsystem Tests + "src/devices/block_device_utils.tests.cpp" "src/devices/storage/ram_disk/device.tests.cpp" ) add_executable("os::kernel_tests" ALIAS "kernel_tests") diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index 6fe89fe..a1fd5e3 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -1,9 +1,9 @@ #include "kernel/devices/block_device_utils.hpp" +#include "kapi/devices/device.hpp" #include "kapi/system.hpp" #include "kernel/devices/block_device.hpp" -#include "kapi/devices/device.hpp" #include #include diff --git a/kernel/src/devices/block_device_utils.tests.cpp b/kernel/src/devices/block_device_utils.tests.cpp new file mode 100644 index 0000000..7ecd202 --- /dev/null +++ b/kernel/src/devices/block_device_utils.tests.cpp @@ -0,0 +1,189 @@ +#include "kernel/devices/block_device_utils.hpp" + +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/devices/block_device.hpp" + +#include +#include +#include + +#include + +#include +#include + +SCENARIO("reading from a block device with block_device_utils", "[devices][block_device_utils]") +{ + GIVEN("a block device with known data") + { + auto const block_size = 512; + auto device = kstd::make_shared(0, 0, "test_block_device", block_size); + kstd::vector block_data(block_size); + for (size_t i = 0; i < block_data.size(); ++i) + { + block_data[i] = static_cast(i % 256); + } + device->write_block(0, block_data.data()); + device->write_block(1, block_data.data()); + + WHEN("reading from the block device using block_device_utils") + { + kstd::vector read_buffer(block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 0, read_buffer.size()); + + THEN("the correct number of bytes is read") + { + REQUIRE(bytes_read == read_buffer.size()); + } + + THEN("the data read matches the data written to the block device") + { + REQUIRE(read_buffer == block_data); + } + } + + WHEN("reading over block boundaries") + { + kstd::vector read_buffer(1024); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 256, read_buffer.size()); + + THEN("the correct number of bytes is read") + { + REQUIRE(bytes_read == 1.5 * block_size); + } + + THEN("the data read matches the expected data across block boundaries") + { + for (size_t i = 0; i < bytes_read; ++i) + { + uint8_t expected_value = static_cast((256 + i) % 256); + REQUIRE(read_buffer[i] == expected_value); + } + } + } + + WHEN("reading beyond the device capacity") + { + kstd::vector read_buffer(block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 1024, read_buffer.size()); + + THEN("no bytes are read") + { + REQUIRE(bytes_read == 0); + } + } + + WHEN("reading nothing") + { + kstd::vector read_buffer(block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 0, 0); + + THEN("no bytes are read") + { + REQUIRE(bytes_read == 0); + } + } + + WHEN("reading with a null buffer") + { + THEN("the system panics") + { + REQUIRE_THROWS_AS(kernel::devices::block_device_utils::read(device, nullptr, 0, 512), kernel::tests::cpu::halt); + } + } + } +} + +SCENARIO("writing to a block device using block_device_utils", "[devices][block_device_utils]") +{ + GIVEN("a block device") + { + auto const block_size = 512; + auto device = kstd::make_shared(0, 0, "test_block_device", block_size); + device->data.resize(2 * block_size); + + WHEN("writing to the block device using block_device_utils") + { + kstd::vector write_buffer(block_size); + for (size_t i = 0; i < write_buffer.size(); ++i) + { + write_buffer[i] = static_cast(i % 256); + } + + auto bytes_written = + kernel::devices::block_device_utils::write(device, write_buffer.data(), 0, write_buffer.size()); + + THEN("the correct number of bytes is written") + { + REQUIRE(bytes_written == write_buffer.size()); + } + + THEN("the data written matches the data read back from the block device") + { + kstd::vector read_buffer(block_size); + device->read_block(0, read_buffer.data()); + REQUIRE(read_buffer == write_buffer); + } + } + + WHEN("writing over block boundaries") + { + kstd::vector write_buffer(2 * block_size); + for (size_t i = 0; i < write_buffer.size(); ++i) + { + write_buffer[i] = static_cast(i % 256); + } + + auto bytes_written = + kernel::devices::block_device_utils::write(device, write_buffer.data(), 256, write_buffer.size()); + + THEN("the correct number of bytes is written") + { + REQUIRE(bytes_written == 1.5 * block_size); + } + + THEN("the data written matches the data read back from the block device across block boundaries") + { + kstd::vector read_buffer(2 * block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 256, 2 * block_size); + + for (size_t i = 0; i < bytes_read; ++i) + { + REQUIRE(read_buffer[i] == write_buffer[i]); + } + } + } + + WHEN("writing beyond the device capacity") + { + kstd::vector write_buffer(block_size); + auto bytes_written = + kernel::devices::block_device_utils::write(device, write_buffer.data(), 1024, write_buffer.size()); + + THEN("no bytes are written") + { + REQUIRE(bytes_written == 0); + } + } + + WHEN("writing nothing") + { + kstd::vector write_buffer(block_size); + auto bytes_written = kernel::devices::block_device_utils::write(device, write_buffer.data(), 0, 0); + + THEN("no bytes are written") + { + REQUIRE(bytes_written == 0); + } + } + + WHEN("writing with a null buffer") + { + THEN("the system panics") + { + REQUIRE_THROWS_AS(kernel::devices::block_device_utils::write(device, nullptr, 0, block_size), + kernel::tests::cpu::halt); + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 8e9e9ffd0528ffa554c336871f1a484c15613bfc Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:28:54 +0200 Subject: add block_device tests --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/devices/block_device.hpp | 2 +- kernel/src/devices/block_device.cpp | 3 +- kernel/src/devices/block_device.tests.cpp | 46 ++++++++++++++++++++++ kernel/src/devices/block_device_utils.tests.cpp | 4 +- kernel/src/test_support/devices/block_device.cpp | 7 +++- 6 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 kernel/src/devices/block_device.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index be21fc3..a360f17 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -132,6 +132,7 @@ else() # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" + "src/devices/block_device.tests.cpp" "src/devices/storage/ram_disk/device.tests.cpp" ) add_executable("os::kernel_tests" ALIAS "kernel_tests") diff --git a/kernel/include/kernel/test_support/devices/block_device.hpp b/kernel/include/kernel/test_support/devices/block_device.hpp index 2327fc4..110872f 100644 --- a/kernel/include/kernel/test_support/devices/block_device.hpp +++ b/kernel/include/kernel/test_support/devices/block_device.hpp @@ -14,7 +14,7 @@ namespace kernel::tests::devices struct block_device : kernel::devices::block_device { - block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size); + block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size, size_t initial_size = 0); auto init() -> bool override; diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp index c006198..b7cb26e 100644 --- a/kernel/src/devices/block_device.cpp +++ b/kernel/src/devices/block_device.cpp @@ -1,8 +1,7 @@ #include "kernel/devices/block_device.hpp" -#include "kapi/system.hpp" - #include "kapi/devices/device.hpp" +#include "kapi/system.hpp" #include diff --git a/kernel/src/devices/block_device.tests.cpp b/kernel/src/devices/block_device.tests.cpp new file mode 100644 index 0000000..378437e --- /dev/null +++ b/kernel/src/devices/block_device.tests.cpp @@ -0,0 +1,46 @@ +#include "kernel/test_support/devices/block_device.hpp" + +#include "kernel/test_support/cpu.hpp" + +#include +#include +#include +#include + +#include + +#include + +SCENARIO("Block device construction", "[devices][block_device]") +{ + GIVEN("parameters for a block device") + { + size_t major = 1; + size_t minor = 0; + kstd::string name = "test_block_device"; + size_t block_size = 512; + + WHEN("constructing a block device") + { + auto device = + kstd::make_shared(major, minor, name, block_size, 3 * block_size); + + THEN("the block device has the correct properties") + { + REQUIRE(device->major() == major); + REQUIRE(device->minor() == minor); + REQUIRE(device->name() == name); + REQUIRE(device->block_size() == block_size); + REQUIRE(device->capacity() == 3 * 512); + } + } + + WHEN("constructing a block device with zero block size") + { + THEN("the constructor panics") + { + REQUIRE_THROWS_AS((kernel::tests::devices::block_device(major, minor, name, 0)), kernel::tests::cpu::halt); + } + } + } +} diff --git a/kernel/src/devices/block_device_utils.tests.cpp b/kernel/src/devices/block_device_utils.tests.cpp index 7ecd202..5f27a9b 100644 --- a/kernel/src/devices/block_device_utils.tests.cpp +++ b/kernel/src/devices/block_device_utils.tests.cpp @@ -99,8 +99,8 @@ SCENARIO("writing to a block device using block_device_utils", "[devices][block_ GIVEN("a block device") { auto const block_size = 512; - auto device = kstd::make_shared(0, 0, "test_block_device", block_size); - device->data.resize(2 * block_size); + auto device = + kstd::make_shared(0, 0, "test_block_device", block_size, 2 * block_size); WHEN("writing to the block device using block_device_utils") { diff --git a/kernel/src/test_support/devices/block_device.cpp b/kernel/src/test_support/devices/block_device.cpp index bce415f..d1d4101 100644 --- a/kernel/src/test_support/devices/block_device.cpp +++ b/kernel/src/test_support/devices/block_device.cpp @@ -13,9 +13,12 @@ namespace kernel::tests::devices { - block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size) + block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size, + size_t initial_size) : kernel::devices::block_device(major, minor, name, block_size) - {} + { + data.resize(initial_size, 0); + } auto block_device::init() -> bool { -- cgit v1.2.3 From 6d72142158970119ee0d36d9149c0e0572dedf5f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:43:15 +0200 Subject: add non block device for tests --- kernel/CMakeLists.txt | 1 + .../test_support/devices/character_device.hpp | 23 ++++++++++++++++++++++ .../src/test_support/devices/character_device.cpp | 20 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 kernel/include/kernel/test_support/devices/character_device.hpp create mode 100644 kernel/src/test_support/devices/character_device.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index a360f17..df20704 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -102,6 +102,7 @@ else() "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" "src/test_support/devices/block_device.cpp" + "src/test_support/devices/character_device.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/devices/character_device.hpp b/kernel/include/kernel/test_support/devices/character_device.hpp new file mode 100644 index 0000000..a106cfb --- /dev/null +++ b/kernel/include/kernel/test_support/devices/character_device.hpp @@ -0,0 +1,23 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_CHARACTER_DEVICE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_CHARACTER_DEVICE_HPP + +#include "kapi/devices/device.hpp" + +#include +#include + +#include + +namespace kernel::tests::devices +{ + // TODO fix inheritance when character devices are implemented + struct character_device : kapi::devices::device + { + character_device(size_t major, size_t minor, kstd::string const & name); + + auto init() -> bool override; + }; + +} // namespace kernel::tests::devices + +#endif \ No newline at end of file diff --git a/kernel/src/test_support/devices/character_device.cpp b/kernel/src/test_support/devices/character_device.cpp new file mode 100644 index 0000000..9e9227d --- /dev/null +++ b/kernel/src/test_support/devices/character_device.cpp @@ -0,0 +1,20 @@ + +#include "kernel/test_support/devices/character_device.hpp" + +#include "kapi/devices.hpp" + +#include + +#include + +namespace kernel::tests::devices +{ + character_device::character_device(size_t major, size_t minor, kstd::string const & name) + : kapi::devices::device(major, minor, name) + {} + + auto character_device::init() -> bool + { + return true; + } +} // namespace kernel::tests::devices \ No newline at end of file -- cgit v1.2.3 From e599e359f727be29415b63c83f3df620d6e4c53c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:50:38 +0200 Subject: fix is_block_device check, add device_inode and non-block device tests --- kernel/CMakeLists.txt | 1 + kernel/src/devices/block_device_utils.cpp | 5 +- kernel/src/devices/block_device_utils.tests.cpp | 32 ++++++- kernel/src/filesystem/device_inode.tests.cpp | 108 ++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 kernel/src/filesystem/device_inode.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index df20704..53ed107 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -130,6 +130,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" + "src/filesystem/device_inode.tests.cpp" # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index a1fd5e3..59e9b97 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -31,12 +31,13 @@ namespace kernel::devices::block_device_utils return 0; } - auto * block_dev = static_cast(device.get()); - if (block_dev == nullptr) + if (!device->is_block_device()) { kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); } + auto * block_dev = static_cast(device.get()); + size_t const block_size = block_dev->block_size(); size_t const capacity = block_dev->capacity(); diff --git a/kernel/src/devices/block_device_utils.tests.cpp b/kernel/src/devices/block_device_utils.tests.cpp index 5f27a9b..f78e477 100644 --- a/kernel/src/devices/block_device_utils.tests.cpp +++ b/kernel/src/devices/block_device_utils.tests.cpp @@ -2,6 +2,7 @@ #include "kernel/test_support/cpu.hpp" #include "kernel/test_support/devices/block_device.hpp" +#include "kernel/test_support/devices/character_device.hpp" #include #include @@ -186,4 +187,33 @@ SCENARIO("writing to a block device using block_device_utils", "[devices][block_ } } } -} \ No newline at end of file +} + +SCENARIO("block_device_utils with a non-block device", "[devices][block_device_utils]") +{ + GIVEN("a non-block device") + { + auto device = kstd::make_shared(0, 0, "test_character_device"); + + WHEN("attempting to read from the non-block device using block_device_utils") + { + kstd::vector read_buffer(512); + THEN("the system panics") + { + REQUIRE_THROWS_AS(kernel::devices::block_device_utils::read(device, read_buffer.data(), 0, read_buffer.size()), + kernel::tests::cpu::halt); + } + } + + WHEN("attempting to write to the non-block device using block_device_utils") + { + kstd::vector write_buffer(512); + THEN("the system panics") + { + REQUIRE_THROWS_AS( + kernel::devices::block_device_utils::write(device, write_buffer.data(), 0, write_buffer.size()), + kernel::tests::cpu::halt); + } + } + } +} diff --git a/kernel/src/filesystem/device_inode.tests.cpp b/kernel/src/filesystem/device_inode.tests.cpp new file mode 100644 index 0000000..4e31812 --- /dev/null +++ b/kernel/src/filesystem/device_inode.tests.cpp @@ -0,0 +1,108 @@ +#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 +#include +#include + +#include + +#include +#include + +SCENARIO("Device inode construction", "[filesystem][device_inode]") +{ + GIVEN("a block device") + { + auto device = kstd::make_shared(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()); + } + } + + 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(0, 0, "test_block_device", 512, 3 * 512); + auto inode = kernel::filesystem::device_inode{device}; + + WHEN("writing to the device inode") + { + kstd::vector write_buffer(1024); + for (size_t i = 0; i < write_buffer.size(); ++i) + { + write_buffer[i] = static_cast(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 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(0, 0, "test_character_device"); + auto inode = kernel::filesystem::device_inode{device}; + + WHEN("reading from the device inode") + { + kstd::vector 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 write_buffer(512); + THEN("the system panics") + { + REQUIRE_THROWS_AS(inode.write(write_buffer.data(), 0, write_buffer.size()), kernel::tests::cpu::halt); + } + } + } +} -- cgit v1.2.3 From 153d061ddfcc6cb9bff1f691a3fe5415f94a3615 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:41:49 +0200 Subject: remove todos --- kernel/include/kernel/filesystem/inode.hpp | 1 - kernel/src/filesystem/filesystem.cpp | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 7237184..072f198 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -69,7 +69,6 @@ namespace kernel::filesystem */ [[nodiscard]] auto is_device() const -> bool; - // TODO BA-FS26 improve inode_kind really needed? public: inode_kind m_kind{inode_kind::regular}; }; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index da2838d..d8b04eb 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -41,7 +41,11 @@ namespace kernel::filesystem auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result { - // TODO BA-FS26 maybe check if device is null and panic here already? + if (!device) + { + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: device is null."); + } + m_device = device; return operation_result::success; } -- cgit v1.2.3 From 597251886a0934315468f5ba4dc595910cbf734c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:42:12 +0200 Subject: use separate test inode --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/filesystem/inode.hpp | 19 +++++++++++++++++++ kernel/src/filesystem/dentry.tests.cpp | 8 ++++---- kernel/src/test_support/filesystem/inode.cpp | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/inode.hpp create mode 100644 kernel/src/test_support/filesystem/inode.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 53ed107..0025e4c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -103,6 +103,7 @@ else() "src/test_support/kapi/memory.cpp" "src/test_support/devices/block_device.cpp" "src/test_support/devices/character_device.cpp" + "src/test_support/filesystem/inode.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/filesystem/inode.hpp b/kernel/include/kernel/test_support/filesystem/inode.hpp new file mode 100644 index 0000000..6568f24 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/inode.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_INODE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace kernel::tests::filesystem +{ + struct inode : kernel::filesystem::inode + { + inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + }; +} // namespace kernel::tests::filesystem + +#endif diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index 94aa48d..a6620d3 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -1,7 +1,7 @@ #include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/devfs/inode.hpp" #include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/filesystem/inode.hpp" #include #include @@ -12,7 +12,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") { GIVEN("A parent dentry and inode") { - auto inode = kstd::make_shared(); + auto inode = kstd::make_shared(); auto parent_dentry = kstd::make_shared(nullptr, inode); WHEN("constructing a dentry") @@ -80,7 +80,7 @@ SCENARIO("Dentry child logic", "[filesystem][dentry]") { GIVEN("A parent dentry and inode") { - auto inode = kstd::make_shared(); + auto inode = kstd::make_shared(); auto parent_dentry = kstd::make_shared(nullptr, inode); WHEN("adding child dentries") @@ -108,7 +108,7 @@ SCENARIO("Dentry Flag logic", "[filesystem][dentry]") { GIVEN("A dentry") { - auto inode = kstd::make_shared(); + auto inode = kstd::make_shared(); auto dentry = kernel::filesystem::dentry{nullptr, inode, "test"}; WHEN("setting a flag") diff --git a/kernel/src/test_support/filesystem/inode.cpp b/kernel/src/test_support/filesystem/inode.cpp new file mode 100644 index 0000000..6352d5a --- /dev/null +++ b/kernel/src/test_support/filesystem/inode.cpp @@ -0,0 +1,22 @@ +#include "kernel/test_support/filesystem/inode.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace kernel::tests::filesystem +{ + inode::inode() + : kernel::filesystem::inode(inode_kind::regular) + {} + + 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; + } +} // namespace kernel::tests::filesystem \ No newline at end of file -- cgit v1.2.3 From 2500898ec94d5071fddb32432ed8041b8e9de26c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:55:49 +0200 Subject: add test_support filesystem --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/filesystem/filesystem.hpp | 22 ++++++++++++++++++++++ kernel/src/test_support/filesystem/filesystem.cpp | 17 +++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 kernel/include/kernel/test_support/filesystem/filesystem.hpp create mode 100644 kernel/src/test_support/filesystem/filesystem.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 0025e4c..502efcc 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -104,6 +104,7 @@ else() "src/test_support/devices/block_device.cpp" "src/test_support/devices/character_device.cpp" "src/test_support/filesystem/inode.cpp" + "src/test_support/filesystem/filesystem.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/filesystem/filesystem.hpp b/kernel/include/kernel/test_support/filesystem/filesystem.hpp new file mode 100644 index 0000000..13aade4 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/filesystem.hpp @@ -0,0 +1,22 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILESYSTEM_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILESYSTEM_HPP + +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace kernel::tests::filesystem +{ + struct filesystem : kernel::filesystem::filesystem + { + filesystem() = default; + + auto lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr override; + }; +} // namespace kernel::tests::filesystem + +#endif diff --git a/kernel/src/test_support/filesystem/filesystem.cpp b/kernel/src/test_support/filesystem/filesystem.cpp new file mode 100644 index 0000000..225d096 --- /dev/null +++ b/kernel/src/test_support/filesystem/filesystem.cpp @@ -0,0 +1,17 @@ +#include "kernel/test_support/filesystem/filesystem.hpp" + +#include "kernel/filesystem/inode.hpp" +#include "kernel/test_support/filesystem/inode.hpp" + +#include + +#include + +namespace kernel::tests::filesystem +{ + auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) + -> kstd::shared_ptr + { + return kstd::make_shared(); + } +} // namespace kernel::tests::filesystem \ No newline at end of file -- cgit v1.2.3 From b36f4ed031bf8da10ccf2b97c9a61d71e672621e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:56:06 +0200 Subject: add mount tests --- kernel/CMakeLists.txt | 1 + kernel/src/filesystem/mount.tests.cpp | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 kernel/src/filesystem/mount.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 502efcc..c00ec14 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -133,6 +133,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" + "src/filesystem/mount.tests.cpp" # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp new file mode 100644 index 0000000..4c4393a --- /dev/null +++ b/kernel/src/filesystem/mount.tests.cpp @@ -0,0 +1,49 @@ +#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 +#include +#include + +#include + +SCENARIO("Mount construction", "[filesystem][mount]") +{ + GIVEN("a filesystem and a root dentry") + { + auto fs = kstd::make_shared(); + auto root_inode = kstd::make_shared(); + auto root_dentry = kstd::make_shared(nullptr, root_inode, "/"); + + WHEN("constructing a mount with the filesystem and root dentry") + { + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, "/", 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.get_mount_dentry() == root_dentry); + REQUIRE(mount.get_mount_path() == "/"); + } + + THEN("the mount has no parent mount") + { + REQUIRE(mount.get_parent_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}), + kernel::tests::cpu::halt); + } + } + } +} -- cgit v1.2.3 From f4e210b1e6169df99db621ca624555027047bc50 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 21:08:08 +0200 Subject: add mount_table tests --- kernel/CMakeLists.txt | 1 + kernel/src/filesystem/mount_table.tests.cpp | 164 ++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 kernel/src/filesystem/mount_table.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index c00ec14..d7f2cc4 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -133,6 +133,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" + "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" # Storage Subsystem Tests diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp new file mode 100644 index 0000000..9f390c6 --- /dev/null +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -0,0 +1,164 @@ +#include "kernel/filesystem/mount_table.hpp" + +#include "kernel/filesystem/dentry.hpp" +#include "kernel/filesystem/mount.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/filesystem/filesystem.hpp" +#include "kernel/test_support/filesystem/inode.hpp" + +#include +#include +#include + +#include + +#include + +SCENARIO("Mount table construction", "[filesystem][mount_table]") +{ + GIVEN("an empty mount table") + { + kernel::filesystem::mount_table table; + + THEN("finding any mount returns null") + { + REQUIRE(table.find_longest_prefix_mount("/") == nullptr); + REQUIRE(table.find_longest_prefix_mount("/any/path") == nullptr); + } + + 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(); + auto root_inode1 = kstd::make_shared(); + auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); + auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + + auto fs2 = kstd::make_shared(); + auto root_inode2 = kstd::make_shared(); + auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); + auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/mnt", nullptr); + + table.add_mount(mount1); + table.add_mount(mount2); + + THEN("dentry flags are set correctly for mounted dentries") + { + REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + + THEN("finding mounts by path returns the correct mount") + { + REQUIRE(table.find_longest_prefix_mount("/") == mount1); + REQUIRE(table.find_longest_prefix_mount("/file") == mount1); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/other") == mount1); + } + + THEN("removing a mount that has no child mounts succeeds") + { + REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(table.find_longest_prefix_mount("/mnt") == nullptr); + } + + 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(); + auto root_inode1 = kstd::make_shared(); + auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); + auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + + auto fs2 = kstd::make_shared(); + auto root_inode2 = kstd::make_shared(); + auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); + auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/", mount1); + + table.add_mount(mount1); + table.add_mount(mount2); + + THEN("finding mounts by path returns the correct mount based on longest prefix") + { + REQUIRE(table.find_longest_prefix_mount("/") == mount2); + REQUIRE(table.find_longest_prefix_mount("/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/other") == mount2); + } + + THEN("removing the topmost mount with the same path succeeds") + { + REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(table.find_longest_prefix_mount("/") == mount1); + } + } + + GIVEN("a mount with child mounts") + { + kernel::filesystem::mount_table table; + + auto fs1 = kstd::make_shared(); + auto root_inode1 = kstd::make_shared(); + auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); + auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + + auto fs2 = kstd::make_shared(); + auto root_inode2 = kstd::make_shared(); + auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); + auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/mnt", mount1); + + auto fs3 = kstd::make_shared(); + auto root_inode3 = kstd::make_shared(); + auto root_dentry3 = kstd::make_shared(nullptr, root_inode3, "/"); + auto mount3 = kstd::make_shared(root_dentry2, root_dentry3, fs3, "/mnt/submnt", mount2); + + table.add_mount(mount1); + table.add_mount(mount2); + table.add_mount(mount3); + + THEN("finding mounts by path returns the correct mount based on longest prefix") + { + REQUIRE(table.find_longest_prefix_mount("/") == mount1); + REQUIRE(table.find_longest_prefix_mount("/file") == mount1); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount3); + REQUIRE(table.find_longest_prefix_mount("/other") == mount1); + } + + 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(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == nullptr); + } + } +} -- cgit v1.2.3 From 7a7c0106257665358e68bbbc99b41acc0c87c0ba Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 21:16:14 +0200 Subject: fix mount table tests --- kernel/src/filesystem/mount_table.tests.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 9f390c6..439fe97 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -2,7 +2,6 @@ #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/mount.hpp" -#include "kernel/test_support/cpu.hpp" #include "kernel/test_support/filesystem/filesystem.hpp" #include "kernel/test_support/filesystem/inode.hpp" @@ -56,7 +55,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("dentry flags are set correctly for mounted dentries") { REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); - REQUIRE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); } THEN("finding mounts by path returns the correct mount") @@ -72,7 +71,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); - REQUIRE(table.find_longest_prefix_mount("/mnt") == nullptr); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount1); } THEN("removing a mount that does not exist returns mount_not_found") @@ -158,7 +157,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] { REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); REQUIRE(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); - REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == nullptr); + REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount2); } } } -- cgit v1.2.3 From af048d3e550bc2a7a6526f4c9714871e32bf7cf4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 21:38:50 +0200 Subject: add open_file_description tests --- kernel/CMakeLists.txt | 1 + .../kernel/filesystem/open_file_description.hpp | 6 +++ kernel/src/filesystem/open_file_description.cpp | 6 +++ .../src/filesystem/open_file_description.tests.cpp | 60 ++++++++++++++++++++++ kernel/src/test_support/filesystem/inode.cpp | 8 +-- 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 kernel/src/filesystem/open_file_description.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d7f2cc4..f578632 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -135,6 +135,7 @@ else() "src/filesystem/device_inode.tests.cpp" "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" + "src/filesystem/open_file_description.tests.cpp" # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index ed878a7..738afd4 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -46,6 +46,12 @@ namespace kernel::filesystem */ auto write(void const * buffer, size_t size) -> size_t; + /** + @brief Returns the current file offset for this open file description. + @return The current file offset in bytes. + */ + [[nodiscard]] auto offset() const -> size_t; + private: kstd::shared_ptr m_inode; size_t m_offset; diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp index 8c04225..f049a34 100644 --- a/kernel/src/filesystem/open_file_description.cpp +++ b/kernel/src/filesystem/open_file_description.cpp @@ -32,4 +32,10 @@ namespace kernel::filesystem m_offset += written_bytes; return written_bytes; } + + auto open_file_description::offset() const -> size_t + { + return m_offset; + } + } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_description.tests.cpp b/kernel/src/filesystem/open_file_description.tests.cpp new file mode 100644 index 0000000..f045094 --- /dev/null +++ b/kernel/src/filesystem/open_file_description.tests.cpp @@ -0,0 +1,60 @@ +#include "kernel/filesystem/open_file_description.hpp" + +#include "kernel/test_support/filesystem/inode.hpp" + +#include +#include +#include + +#include + +SCENARIO("Open file description construction", "[filesystem][open_file_description]") +{ + GIVEN("an inode and an open file description for that inode") + { + auto inode = kstd::make_shared(); + auto file_description = kernel::filesystem::open_file_description{inode}; + + THEN("the initial offset is zero") + { + REQUIRE(file_description.offset() == 0); + } + } +} + +SCENARIO("Open file description read/write offset management", "[filesystem][open_file_description]") +{ + GIVEN("an inode that tracks read/write calls and an open file description for that inode") + { + auto inode = kstd::make_shared(); + auto file_description = kernel::filesystem::open_file_description{inode}; + + THEN("the offset is updated correctly after reads") + { + REQUIRE(file_description.read(nullptr, 100) == 100); + REQUIRE(file_description.offset() == 100); + REQUIRE(file_description.read(nullptr, 50) == 50); + REQUIRE(file_description.offset() == 150); + } + + THEN("the offset is updated correctly after writes") + { + REQUIRE(file_description.write(nullptr, 200) == 200); + REQUIRE(file_description.offset() == 200); + REQUIRE(file_description.write(nullptr, 25) == 25); + REQUIRE(file_description.offset() == 225); + } + + THEN("reads and writes both update the same offset") + { + REQUIRE(file_description.read(nullptr, 10) == 10); + REQUIRE(file_description.offset() == 10); + REQUIRE(file_description.write(nullptr, 20) == 20); + REQUIRE(file_description.offset() == 30); + REQUIRE(file_description.read(nullptr, 5) == 5); + REQUIRE(file_description.offset() == 35); + REQUIRE(file_description.write(nullptr, 15) == 15); + REQUIRE(file_description.offset() == 50); + } + } +} diff --git a/kernel/src/test_support/filesystem/inode.cpp b/kernel/src/test_support/filesystem/inode.cpp index 6352d5a..5df7bcd 100644 --- a/kernel/src/test_support/filesystem/inode.cpp +++ b/kernel/src/test_support/filesystem/inode.cpp @@ -10,13 +10,13 @@ namespace kernel::tests::filesystem : kernel::filesystem::inode(inode_kind::regular) {} - auto inode::read(void *, size_t, size_t) const -> size_t + auto inode::read(void *, size_t, size_t size) const -> size_t { - return 0; + return size; } - auto inode::write(void const *, size_t, size_t) -> size_t + auto inode::write(void const *, size_t, size_t size) -> size_t { - return 0; + return size; } } // namespace kernel::tests::filesystem \ No newline at end of file -- cgit v1.2.3 From 530a478b3e755ef4585b27422a33bd0de1556ce2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 22:12:51 +0200 Subject: add file_descriptor_table tests --- kernel/CMakeLists.txt | 1 + .../kernel/filesystem/file_descriptor_table.hpp | 6 + kernel/src/filesystem/file_descriptor_table.cpp | 5 + .../src/filesystem/file_descriptor_table.tests.cpp | 155 +++++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 kernel/src/filesystem/file_descriptor_table.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index f578632..b664def 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -133,6 +133,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" + "src/filesystem/file_descriptor_table.tests.cpp" "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" "src/filesystem/open_file_description.tests.cpp" diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 5d52c19..cc31511 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -28,6 +28,12 @@ namespace kernel::filesystem */ auto static get() -> file_descriptor_table &; + /** + @brief Reset the file descriptor table to an empty state. This method is intended for testing purposes to allow + resetting the state of the file descriptor table between test cases. + */ + auto static reset() -> void; + /** @brief Destructor for the file descriptor table. */ diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index 287aea2..a31e2e6 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -37,6 +37,11 @@ namespace kernel::filesystem return *global_file_descriptor_table; } + auto file_descriptor_table::reset() -> void + { + global_file_descriptor_table.reset(); + } + auto file_descriptor_table::add_file(kstd::shared_ptr const & file_description) -> int { if (!file_description) diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp new file mode 100644 index 0000000..c431d01 --- /dev/null +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -0,0 +1,155 @@ +#include "kernel/filesystem/file_descriptor_table.hpp" + +#include "kernel/filesystem/open_file_description.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/filesystem/inode.hpp" + +#include +#include +#include + +#include + +struct file_descriptor_table_RAII +{ + file_descriptor_table_RAII() + { + kernel::filesystem::file_descriptor_table::init(); + } + ~file_descriptor_table_RAII() + { + kernel::filesystem::file_descriptor_table::reset(); + } +}; + +struct file_descriptor_table_reset_only +{ + ~file_descriptor_table_reset_only() + { + kernel::filesystem::file_descriptor_table::reset(); + } +}; + +SCENARIO_METHOD(file_descriptor_table_reset_only, "File descriptor table initialization", + "[filesystem][file_descriptor_table]") +{ + THEN("accessing the file descriptor table before initialization panics") + { + REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::get(), kernel::tests::cpu::halt); + } + + THEN("the file descriptor table can be initialized and accessed") + { + kernel::filesystem::file_descriptor_table::init(); + REQUIRE_NOTHROW(kernel::filesystem::file_descriptor_table::get()); + } + + THEN("initializing the file descriptor table more than once panics") + { + kernel::filesystem::file_descriptor_table::init(); + REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::init(), kernel::tests::cpu::halt); + } +} + +SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file", "[filesystem][file_descriptor_table]") +{ + GIVEN("a file descriptor table and an open file description") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + auto inode = kstd::make_shared(); + auto file_description_1 = kstd::make_shared(inode); + auto file_description_2 = kstd::make_shared(inode); + + WHEN("adding the open file description to the file descriptor table") + { + auto fd_1 = table.add_file(file_description_1); + auto fd_2 = table.add_file(file_description_2); + auto fd_3 = table.add_file(file_description_2); + + THEN("a valid file descriptor is returned") + { + REQUIRE(fd_1 == 0); + REQUIRE(fd_2 == 1); + REQUIRE(fd_3 == 1); + } + + THEN("the file description can be retrieved using the returned file descriptor") + { + auto retrieved_description = table.get_file(fd_1); + REQUIRE(retrieved_description == file_description_1); + } + } + } + + GIVEN("a invalid open file description") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + + THEN("adding a null file description returns an error code") + { + auto fd = table.add_file(nullptr); + REQUIRE(fd == -1); + } + + THEN("retrieving a file description with a negative file descriptor returns a null pointer") + { + auto retrieved_description = table.get_file(-1); + REQUIRE(retrieved_description == nullptr); + } + + THEN("retrieving a file description with an out-of-bounds file descriptor returns a null pointer") + { + auto retrieved_description = table.get_file(1000); + REQUIRE(retrieved_description == nullptr); + } + } +} + +SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table remove file", "[filesystem][file_descriptor_table]") +{ + GIVEN("a file descriptor table with an open file description") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + auto inode = kstd::make_shared(); + auto file_description = kstd::make_shared(inode); + auto fd = table.add_file(file_description); + + WHEN("removing the file description using the file descriptor") + { + table.remove_file(fd); + + THEN("the file description can no longer be retrieved using the file descriptor") + { + auto retrieved_description = table.get_file(fd); + REQUIRE(retrieved_description == nullptr); + } + } + + WHEN("removing a file description the other file descriptor keep the same index") + { + auto fd2 = table.add_file(file_description); + table.remove_file(fd); + + THEN("the second file description can still be retrieved using its file descriptor") + { + auto retrieved_description = table.get_file(fd2); + REQUIRE(retrieved_description == file_description); + } + } + } + + GIVEN("an invalid file descriptor") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + + THEN("removing a file with a negative file descriptor does nothing") + { + REQUIRE_NOTHROW(table.remove_file(-1)); + } + + 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 -- cgit v1.2.3 From 643cbff68aa0a5e029b9ce46eb69846c8b01dca7 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 22:17:15 +0200 Subject: fix test --- kernel/src/filesystem/file_descriptor_table.tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp index c431d01..d8c05ca 100644 --- a/kernel/src/filesystem/file_descriptor_table.tests.cpp +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -70,7 +70,7 @@ SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file" { REQUIRE(fd_1 == 0); REQUIRE(fd_2 == 1); - REQUIRE(fd_3 == 1); + REQUIRE(fd_3 == 2); } THEN("the file description can be retrieved using the returned file descriptor") -- cgit v1.2.3 From 60118b1df5196c4e416ddd6ad2a40be062f68251 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 22:57:55 +0200 Subject: add devfs and rootfs inode tests --- kernel/CMakeLists.txt | 2 + kernel/src/filesystem/devfs/inode.tests.cpp | 54 ++++++++++++++++++ kernel/src/filesystem/rootfs/inode.tests.cpp | 83 ++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 kernel/src/filesystem/devfs/inode.tests.cpp create mode 100644 kernel/src/filesystem/rootfs/inode.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b664def..e3a357b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -131,6 +131,8 @@ else() "src/memory/block_list_allocator.tests.cpp" # Filesystem Subsystem Tests + "src/filesystem/devfs/inode.tests.cpp" + "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" "src/filesystem/file_descriptor_table.tests.cpp" diff --git a/kernel/src/filesystem/devfs/inode.tests.cpp b/kernel/src/filesystem/devfs/inode.tests.cpp new file mode 100644 index 0000000..50e34a7 --- /dev/null +++ b/kernel/src/filesystem/devfs/inode.tests.cpp @@ -0,0 +1,54 @@ +#include "kernel/filesystem/devfs/inode.hpp" + +#include +#include +#include + +#include + +#include + +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()); + } + } +} + +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 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 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/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp new file mode 100644 index 0000000..a0c5938 --- /dev/null +++ b/kernel/src/filesystem/rootfs/inode.tests.cpp @@ -0,0 +1,83 @@ +#include "kernel/filesystem/rootfs/inode.hpp" + +#include +#include +#include + +#include + +SCENARIO("Rootfs inode creation", "[filesystem][rootfs][inode]") +{ + GIVEN("a rootfs inode") + { + auto inode = kernel::filesystem::rootfs::inode{}; + + THEN("the inode has the correct kind") + { + REQUIRE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_regular()); + } + + THEN("the inode has no children") + { + REQUIRE(inode.lookup_child("child") == nullptr); + } + } +} + +SCENARIO("Rootfs inode child management", "[filesystem][rootfs][inode]") +{ + GIVEN("a rootfs inode") + { + auto inode = kernel::filesystem::rootfs::inode{}; + + WHEN("adding a child inode") + { + inode.add_child("child"); + inode.add_child("another child"); + + THEN("the child can be looked up by name") + { + auto child_inode = inode.lookup_child("child"); + REQUIRE(child_inode != nullptr); + REQUIRE(child_inode->is_directory()); + } + + THEN("looking up a non-existent child returns null") + { + REQUIRE(inode.lookup_child("nonexistent") == nullptr); + } + } + } +} + +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 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 buffer(10, 'x'); + auto bytes_written = inode.write(buffer.data(), 0, buffer.size()); + + THEN("no bytes are written") + { + REQUIRE(bytes_written == 0); + } + } + } +} -- cgit v1.2.3 From 9ce8ed3dd3aa5f6e21b53d02bac4f62eb8b3f337 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 23:04:40 +0200 Subject: add rootfs filesystem tests --- kernel/CMakeLists.txt | 1 + kernel/src/filesystem/rootfs/filesystem.tests.cpp | 45 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 kernel/src/filesystem/rootfs/filesystem.tests.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index e3a357b..1cbb9e4 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -132,6 +132,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/devfs/inode.tests.cpp" + "src/filesystem/rootfs/filesystem.tests.cpp" "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" diff --git a/kernel/src/filesystem/rootfs/filesystem.tests.cpp b/kernel/src/filesystem/rootfs/filesystem.tests.cpp new file mode 100644 index 0000000..b013f78 --- /dev/null +++ b/kernel/src/filesystem/rootfs/filesystem.tests.cpp @@ -0,0 +1,45 @@ +#include "kernel/filesystem/rootfs/filesystem.hpp" + +#include "kernel/filesystem/filesystem.hpp" + +#include +#include +#include + +#include + +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 the 'dev' directory returns a valid inode") + { + auto dev_inode = fs.lookup(fs.root_inode(), "dev"); + REQUIRE(dev_inode != nullptr); + REQUIRE(dev_inode->is_directory()); + } + + 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); + } + } +} -- cgit v1.2.3 From 3c9ad45492d7417c65594fa7fa2fb9a8d5439276 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 08:32:51 +0200 Subject: add deinit functions for singletons in tests --- .../kernel/filesystem/file_descriptor_table.hpp | 6 --- .../include/kernel/test_support/boot_modules.hpp | 10 +++++ .../test_support/devices/storage/management.hpp | 10 +++++ .../filesystem/file_descriptor_table.hpp | 10 +++++ .../include/kernel/test_support/filesystem/vfs.hpp | 10 +++++ kernel/kapi/boot_modules.cpp | 19 ++++++--- kernel/src/devices/storage/management.cpp | 28 ++++++++----- kernel/src/filesystem/file_descriptor_table.cpp | 25 ++++++------ .../src/filesystem/file_descriptor_table.tests.cpp | 46 +--------------------- kernel/src/filesystem/vfs.cpp | 24 +++++++---- kernel/src/test_support/state_reset_listener.cpp | 12 ++++++ 11 files changed, 116 insertions(+), 84 deletions(-) create mode 100644 kernel/include/kernel/test_support/boot_modules.hpp create mode 100644 kernel/include/kernel/test_support/devices/storage/management.hpp create mode 100644 kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp create mode 100644 kernel/include/kernel/test_support/filesystem/vfs.hpp (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index cc31511..5d52c19 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -28,12 +28,6 @@ namespace kernel::filesystem */ auto static get() -> file_descriptor_table &; - /** - @brief Reset the file descriptor table to an empty state. This method is intended for testing purposes to allow - resetting the state of the file descriptor table between test cases. - */ - auto static reset() -> void; - /** @brief Destructor for the file descriptor table. */ diff --git a/kernel/include/kernel/test_support/boot_modules.hpp b/kernel/include/kernel/test_support/boot_modules.hpp new file mode 100644 index 0000000..2343a10 --- /dev/null +++ b/kernel/include/kernel/test_support/boot_modules.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_BOOT_MODULES_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_BOOT_MODULES_HPP + +namespace kernel::tests::boot_modules +{ + //! Deinitialize the boot module registry. + auto deinit() -> void; +} // namespace kernel::tests::boot_modules + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/devices/storage/management.hpp b/kernel/include/kernel/test_support/devices/storage/management.hpp new file mode 100644 index 0000000..7c0d304 --- /dev/null +++ b/kernel/include/kernel/test_support/devices/storage/management.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP + +namespace kernel::tests::devices::storage +{ + //! Deinitialize the storage management singleton. + auto deinit() -> void; +} // namespace kernel::tests::devices::storage + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp new file mode 100644 index 0000000..bbc0f4a --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP + +namespace kernel::tests::filesystem::file_descriptor_table +{ + //! Deinitialize the file descriptor table singleton. + auto deinit() -> void; +} // namespace kernel::tests::filesystem::file_descriptor_table + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/vfs.hpp b/kernel/include/kernel/test_support/filesystem/vfs.hpp new file mode 100644 index 0000000..739e353 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/vfs.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_VFS_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_VFS_HPP + +namespace kernel::tests::filesystem::vfs +{ + //! Deinitialize the VFS singleton. + auto deinit() -> void; +} // namespace kernel::tests::filesystem::vfs + +#endif \ No newline at end of file diff --git a/kernel/kapi/boot_modules.cpp b/kernel/kapi/boot_modules.cpp index 5a0ef7f..0549368 100644 --- a/kernel/kapi/boot_modules.cpp +++ b/kernel/kapi/boot_modules.cpp @@ -4,14 +4,13 @@ #include -namespace kapi::boot_modules +namespace { + constinit auto static registry = std::optional{}; +} // namespace - namespace - { - constinit auto static registry = std::optional{}; - } // namespace - +namespace kapi::boot_modules +{ auto set_boot_module_registry(boot_module_registry & new_registry) -> void { if (registry) @@ -32,3 +31,11 @@ namespace kapi::boot_modules return *registry; } } // namespace kapi::boot_modules + +namespace kernel::tests::boot_modules +{ + auto deinit() -> void + { + registry.reset(); + } +} // namespace kernel::tests::boot_modules diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index d440bf0..14a045a 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -1,9 +1,9 @@ #include "kernel/devices/storage/management.hpp" #include "kapi/boot_modules.hpp" +#include "kapi/devices/device.hpp" #include "kapi/system.hpp" -#include "kapi/devices/device.hpp" #include "kernel/devices/storage/controller.hpp" #include "kernel/devices/storage/ram_disk/controller.hpp" @@ -14,17 +14,17 @@ #include #include -namespace kernel::devices::storage +namespace { - namespace - { - constexpr size_t static MINORS_PER_DEVICE = 16; - constexpr size_t static START_MAJOR = 1; - constinit size_t static next_free_major = START_MAJOR; + constexpr size_t static MINORS_PER_DEVICE = 16; + constexpr size_t static START_MAJOR = 1; + constinit size_t static next_free_major = START_MAJOR; - constinit auto static active_storage_management = std::optional{}; - } // namespace + constinit auto static active_storage_management = std::optional{}; +} // namespace +namespace kernel::devices::storage +{ auto management::init() -> void { if (active_storage_management) @@ -81,5 +81,13 @@ namespace kernel::devices::storage { return device_by_major_minor(START_MAJOR, 0); } +} // namespace kernel::devices::storage -} // namespace kernel::devices::storage \ No newline at end of file +namespace kernel::tests::devices::storage +{ + auto deinit() -> void + { + active_storage_management.reset(); + next_free_major = START_MAJOR; + } +} // namespace kernel::tests::devices::storage diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index a31e2e6..1c062a1 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -10,13 +10,13 @@ #include #include -namespace kernel::filesystem +namespace { - namespace - { - constinit auto static global_file_descriptor_table = std::optional{}; - } // namespace + constinit auto static global_file_descriptor_table = std::optional{}; +} // namespace +namespace kernel::filesystem +{ auto file_descriptor_table::init() -> void { if (global_file_descriptor_table) @@ -37,11 +37,6 @@ namespace kernel::filesystem return *global_file_descriptor_table; } - auto file_descriptor_table::reset() -> void - { - global_file_descriptor_table.reset(); - } - auto file_descriptor_table::add_file(kstd::shared_ptr const & file_description) -> int { if (!file_description) @@ -92,4 +87,12 @@ namespace kernel::filesystem m_open_files.at(index) = nullptr; } -} // namespace kernel::filesystem \ No newline at end of file +} // namespace kernel::filesystem + +namespace kernel::tests::filesystem::file_descriptor_table +{ + auto deinit() -> void + { + global_file_descriptor_table.reset(); + } +} // namespace kernel::tests::filesystem::file_descriptor_table diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp index d8c05ca..5aeadb2 100644 --- a/kernel/src/filesystem/file_descriptor_table.tests.cpp +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -1,7 +1,6 @@ #include "kernel/filesystem/file_descriptor_table.hpp" #include "kernel/filesystem/open_file_description.hpp" -#include "kernel/test_support/cpu.hpp" #include "kernel/test_support/filesystem/inode.hpp" #include @@ -10,48 +9,7 @@ #include -struct file_descriptor_table_RAII -{ - file_descriptor_table_RAII() - { - kernel::filesystem::file_descriptor_table::init(); - } - ~file_descriptor_table_RAII() - { - kernel::filesystem::file_descriptor_table::reset(); - } -}; - -struct file_descriptor_table_reset_only -{ - ~file_descriptor_table_reset_only() - { - kernel::filesystem::file_descriptor_table::reset(); - } -}; - -SCENARIO_METHOD(file_descriptor_table_reset_only, "File descriptor table initialization", - "[filesystem][file_descriptor_table]") -{ - THEN("accessing the file descriptor table before initialization panics") - { - REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::get(), kernel::tests::cpu::halt); - } - - THEN("the file descriptor table can be initialized and accessed") - { - kernel::filesystem::file_descriptor_table::init(); - REQUIRE_NOTHROW(kernel::filesystem::file_descriptor_table::get()); - } - - THEN("initializing the file descriptor table more than once panics") - { - kernel::filesystem::file_descriptor_table::init(); - REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::init(), kernel::tests::cpu::halt); - } -} - -SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file", "[filesystem][file_descriptor_table]") +SCENARIO("File descriptor table add/get file", "[filesystem][file_descriptor_table]") { GIVEN("a file descriptor table and an open file description") { @@ -105,7 +63,7 @@ SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file" } } -SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table remove file", "[filesystem][file_descriptor_table]") +SCENARIO("File descriptor table remove file", "[filesystem][file_descriptor_table]") { GIVEN("a file descriptor table with an open file description") { diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 45ae053..67d1af2 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -17,13 +17,13 @@ #include #include -namespace kernel::filesystem +namespace { - namespace - { - constinit auto static active_vfs = std::optional{}; - } // namespace + constinit auto static active_vfs = std::optional{}; +} // namespace +namespace kernel::filesystem +{ auto vfs::init() -> void { if (active_vfs) @@ -47,7 +47,10 @@ namespace kernel::filesystem if (auto boot_device = storage_mgmt.determine_boot_device()) { auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device); - do_mount_internal("/", root_fs_root_dentry, boot_root_fs); + if (boot_root_fs) + { + do_mount_internal("/", root_fs_root_dentry, boot_root_fs); + } } auto device_fs = kstd::make_shared(); @@ -172,5 +175,12 @@ namespace kernel::filesystem return current_dentry; } +} // namespace kernel::filesystem -} // namespace kernel::filesystem \ No newline at end of file +namespace kernel::tests::filesystem::vfs +{ + auto deinit() -> void + { + active_vfs.reset(); + } +} // namespace kernel::tests::filesystem::vfs diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index 8c95cc8..af1091c 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -2,8 +2,13 @@ #include "kapi/cpu.hpp" #include "kapi/memory.hpp" +#include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/test_support/boot_modules.hpp" #include "kernel/test_support/cio.hpp" #include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/devices/storage/management.hpp" +#include "kernel/test_support/filesystem/file_descriptor_table.hpp" +#include "kernel/test_support/filesystem/vfs.hpp" #include "kernel/test_support/memory.hpp" #include @@ -17,6 +22,8 @@ struct state_reset_listener : Catch::EventListenerBase void testCaseStarting(Catch::TestCaseInfo const &) override { + kernel::filesystem::file_descriptor_table::init(); + kapi::cio::init(); kapi::cpu::init(); kapi::memory::init(); @@ -24,6 +31,11 @@ struct state_reset_listener : Catch::EventListenerBase void testCaseEnded(Catch::TestCaseStats const &) override { + kernel::tests::filesystem::file_descriptor_table::deinit(); + kernel::tests::filesystem::vfs::deinit(); + kernel::tests::devices::storage::deinit(); + kernel::tests::boot_modules::deinit(); + kernel::tests::memory::deinit(); kernel::tests::cpu::deinit(); kernel::tests::cio::deinit(); -- cgit v1.2.3 From 97e83ad1466d5962a1e5f5f83fa4c951bfeafb2c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 09:13:09 +0200 Subject: add devfs filesystem tests, and storage_boot_module_fixture to mock real boot modules --- kernel/CMakeLists.txt | 2 + .../test_support/devices/storage/management.hpp | 4 +- .../filesystem/storage_boot_module_fixture.hpp | 23 ++++++++ kernel/src/devices/storage/management.cpp | 4 +- kernel/src/filesystem/devfs/filesystem.tests.cpp | 63 ++++++++++++++++++++++ .../filesystem/storage_boot_module_fixture.cpp | 43 +++++++++++++++ kernel/src/test_support/state_reset_listener.cpp | 2 +- 7 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp create mode 100644 kernel/src/filesystem/devfs/filesystem.tests.cpp create mode 100644 kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 1cbb9e4..6a2e4b5 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -105,6 +105,7 @@ else() "src/test_support/devices/character_device.cpp" "src/test_support/filesystem/inode.cpp" "src/test_support/filesystem/filesystem.cpp" + "src/test_support/filesystem/storage_boot_module_fixture.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" @@ -131,6 +132,7 @@ else() "src/memory/block_list_allocator.tests.cpp" # Filesystem Subsystem Tests + "src/filesystem/devfs/filesystem.tests.cpp" "src/filesystem/devfs/inode.tests.cpp" "src/filesystem/rootfs/filesystem.tests.cpp" "src/filesystem/rootfs/inode.tests.cpp" diff --git a/kernel/include/kernel/test_support/devices/storage/management.hpp b/kernel/include/kernel/test_support/devices/storage/management.hpp index 7c0d304..581aa91 100644 --- a/kernel/include/kernel/test_support/devices/storage/management.hpp +++ b/kernel/include/kernel/test_support/devices/storage/management.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP -namespace kernel::tests::devices::storage +namespace kernel::tests::devices::storage::management { //! Deinitialize the storage management singleton. auto deinit() -> void; -} // namespace kernel::tests::devices::storage +} // namespace kernel::tests::devices::storage::management #endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp new file mode 100644 index 0000000..a57659b --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp @@ -0,0 +1,23 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_FIXTURE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_FIXTURE_HPP + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include +#include +#include + +namespace kernel::tests::filesystem +{ + struct storage_boot_module_fixture + { + auto setup_modules(std::size_t module_count, std::size_t module_size = 4096) -> void; + + protected: + kapi::boot_modules::boot_module_registry registry{}; + std::vector module_names{}; + std::vector> module_data{}; + }; +} // namespace kernel::tests::filesystem + +#endif \ No newline at end of file diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index 14a045a..c9fa0a8 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -83,11 +83,11 @@ namespace kernel::devices::storage } } // namespace kernel::devices::storage -namespace kernel::tests::devices::storage +namespace kernel::tests::devices::storage::management { auto deinit() -> void { active_storage_management.reset(); next_free_major = START_MAJOR; } -} // namespace kernel::tests::devices::storage +} // namespace kernel::tests::devices::storage::management diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp new file mode 100644 index 0000000..1d82bf9 --- /dev/null +++ b/kernel/src/filesystem/devfs/filesystem.tests.cpp @@ -0,0 +1,63 @@ +#include "kernel/filesystem/devfs/filesystem.hpp" + +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include + +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 a non-directory parent returns null") + { + auto non_directory_inode = fs.lookup(fs.root_inode(), "ram0"); + REQUIRE(non_directory_inode != nullptr); + REQUIRE(!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/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp new file mode 100644 index 0000000..8bbf194 --- /dev/null +++ b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp @@ -0,0 +1,43 @@ +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/boot_modules.hpp" +#include "kapi/memory.hpp" + +#include "kernel/devices/storage/management.hpp" +#include "kernel/test_support/boot_modules.hpp" +#include "kernel/test_support/devices/storage/management.hpp" + +#include +#include + +namespace kernel::tests::filesystem +{ + auto storage_boot_module_fixture::setup_modules(std::size_t module_count, std::size_t module_size) -> void + { + kernel::tests::devices::storage::management::deinit(); + kernel::tests::boot_modules::deinit(); + + module_names.clear(); + module_data.clear(); + registry = {}; + + module_names.reserve(module_count); + module_data.reserve(module_count); + + for (std::size_t i = 0; i < module_count; ++i) + { + module_names.push_back("test_mod" + std::to_string(i)); + module_data.emplace_back(module_size, std::byte{static_cast(0x40 + (i % 16))}); + } + + for (std::size_t i = 0; i < module_count; ++i) + { + registry.add_boot_module(kapi::boot_modules::boot_module{ + module_names[i], kapi::memory::linear_address{module_data[i].data()}, module_data[i].size()}); + } + + kapi::boot_modules::set_boot_module_registry(registry); + kernel::devices::storage::management::init(); + } +} // namespace kernel::tests::filesystem \ No newline at end of file diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index af1091c..9120c89 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -33,8 +33,8 @@ struct state_reset_listener : Catch::EventListenerBase { kernel::tests::filesystem::file_descriptor_table::deinit(); kernel::tests::filesystem::vfs::deinit(); - kernel::tests::devices::storage::deinit(); kernel::tests::boot_modules::deinit(); + kernel::tests::devices::storage::management::deinit(); kernel::tests::memory::deinit(); kernel::tests::cpu::deinit(); -- cgit v1.2.3 From 186bc5c9a08c5d6e0d306ce8b4fe3d75f4782cd2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 09:19:40 +0200 Subject: add test for devfs edge case --- kernel/src/filesystem/devfs/filesystem.tests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'kernel') diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp index 1d82bf9..f8c4764 100644 --- a/kernel/src/filesystem/devfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/devfs/filesystem.tests.cpp @@ -34,6 +34,15 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, 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"); -- cgit v1.2.3 From 787671aac288590e40c5cabfc9f82a31f21629fe Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 10:33:38 +0200 Subject: add vfs tests with real ext2 images --- kernel/CMakeLists.txt | 9 +- .../filesystem/storage_boot_module_fixture.hpp | 19 ++- .../filesystem/storage_boot_module_vfs_fixture.hpp | 24 +++ kernel/src/filesystem/vfs.tests.cpp | 166 +++++++++++++++++++++ .../filesystem/storage_boot_module_fixture.cpp | 90 +++++++++-- .../filesystem/storage_boot_module_vfs_fixture.cpp | 32 ++++ .../filesystem/test_assets/ext2_1KB_fs.img | 3 + .../filesystem/test_assets/ext2_2KB_fs.img | 3 + .../filesystem/test_assets/ext2_4KB_fs.img | 3 + 9 files changed, 330 insertions(+), 19 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp create mode 100644 kernel/src/filesystem/vfs.tests.cpp create mode 100644 kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp create mode 100644 kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img create mode 100644 kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img create mode 100644 kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 6a2e4b5..88d420b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -106,7 +106,7 @@ else() "src/test_support/filesystem/inode.cpp" "src/test_support/filesystem/filesystem.cpp" "src/test_support/filesystem/storage_boot_module_fixture.cpp" - "src/test_support/log_buffer.cpp" + "src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" "src/test_support/simulated_memory.cpp" @@ -142,7 +142,8 @@ else() "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" "src/filesystem/open_file_description.tests.cpp" - + "src/filesystem/vfs.tests.cpp" + # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" "src/devices/block_device.tests.cpp" @@ -155,6 +156,10 @@ else() "os::kernel_test_support" ) + target_compile_definitions("kernel_tests" PRIVATE + KERNEL_TEST_ASSETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/test_support/filesystem/test_assets" + ) + set_target_properties("kernel_tests" PROPERTIES C_CLANG_TIDY "" CXX_CLANG_TIDY "" diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp index a57659b..ee658e2 100644 --- a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp @@ -3,20 +3,29 @@ #include "kapi/boot_module/boot_module_registry.hpp" +#include +#include + #include -#include -#include +#include namespace kernel::tests::filesystem { struct storage_boot_module_fixture { + ~storage_boot_module_fixture(); + auto setup_modules(std::size_t module_count, std::size_t module_size = 4096) -> void; + auto setup_modules_from_img(kstd::vector const & module_names, + kstd::vector const & img_paths) -> void; protected: - kapi::boot_modules::boot_module_registry registry{}; - std::vector module_names{}; - std::vector> module_data{}; + kapi::boot_modules::boot_module_registry m_registry{}; + kstd::vector m_module_names{}; + kstd::vector> m_module_data{}; + + private: + auto setup_module_from_img(kstd::string const & module_name, std::filesystem::path const & img_path) -> void; }; } // namespace kernel::tests::filesystem diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp new file mode 100644 index 0000000..98012b0 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp @@ -0,0 +1,24 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_VFS_FIXTURE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_VFS_FIXTURE_HPP + +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include +#include + +#include +#include + +namespace kernel::tests::filesystem +{ + struct storage_boot_module_vfs_fixture : storage_boot_module_fixture + { + ~storage_boot_module_vfs_fixture(); + + auto setup_modules_and_init_vfs(std::size_t module_count, std::size_t module_size = 4096) -> void; + auto setup_modules_from_img_and_init_vfs(kstd::vector const & module_names, + kstd::vector const & img_paths) -> void; + }; +} // namespace kernel::tests::filesystem + +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp new file mode 100644 index 0000000..fec32e1 --- /dev/null +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -0,0 +1,166 @@ +#include "kernel/filesystem/vfs.hpp" + +#include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" + +#include + +#include + +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 & volatilefs = kernel::filesystem::vfs::get(); + auto root = volatilefs.open("/"); + auto dev = volatilefs.open("/dev"); + auto information = volatilefs.open("/information/info_1.txt"); + + REQUIRE(root != nullptr); + REQUIRE(dev != nullptr); + REQUIRE(information != 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(); + auto storage_mgmt = kernel::devices::storage::management::get(); + auto device_1 = storage_mgmt.device_by_major_minor(1, 16); + auto fs_1 = kernel::filesystem::filesystem::probe_and_mount(device_1); + auto device_2 = storage_mgmt.device_by_major_minor(1, 32); + auto fs_2 = kernel::filesystem::filesystem::probe_and_mount(device_2); + + 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("/information", fs_1) == 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::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("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/information/monkey_house/infrastructure", fs_2) == + 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.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("images can be stacked mounted and correct file system is unmounted again") + { + REQUIRE(vfs.do_mount("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/information", fs_2) == kernel::filesystem::vfs::operation_result::success); + + auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); + REQUIRE(mounted_tickets != nullptr); + + 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("mount with null file system fails") + { + REQUIRE(vfs.do_mount("/information", nullptr) == kernel::filesystem::vfs::operation_result::filesystem_null); + } + + THEN("mount with invalid path fails") + { + REQUIRE(vfs.do_mount("", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("information", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("/information/", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); + } + + THEN("mount with non-existent mount point fails") + { + REQUIRE(vfs.do_mount("/information/nonexistent", fs_1) == + 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::invalid_path); + REQUIRE(vfs.unmount("/information/") == kernel::filesystem::vfs::operation_result::invalid_path); + } + + THEN("unmounting non-existent mount point returns expected error code") + { + REQUIRE(vfs.unmount("/information/nonexistent") == + kernel::filesystem::vfs::operation_result::mount_point_not_found); + } + } +} diff --git a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp index 8bbf194..a139f63 100644 --- a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp +++ b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp @@ -8,36 +8,102 @@ #include "kernel/test_support/boot_modules.hpp" #include "kernel/test_support/devices/storage/management.hpp" +#include +#include + #include -#include +#include +#include +#include +#include namespace kernel::tests::filesystem { - auto storage_boot_module_fixture::setup_modules(std::size_t module_count, std::size_t module_size) -> void + storage_boot_module_fixture::~storage_boot_module_fixture() { kernel::tests::devices::storage::management::deinit(); kernel::tests::boot_modules::deinit(); + } - module_names.clear(); - module_data.clear(); - registry = {}; + auto storage_boot_module_fixture::setup_modules(std::size_t module_count, std::size_t module_size) -> void + { + m_module_names.clear(); + m_module_data.clear(); + m_registry = {}; - module_names.reserve(module_count); - module_data.reserve(module_count); + m_module_names.reserve(module_count); + m_module_data.reserve(module_count); for (std::size_t i = 0; i < module_count; ++i) { - module_names.push_back("test_mod" + std::to_string(i)); - module_data.emplace_back(module_size, std::byte{static_cast(0x40 + (i % 16))}); + m_module_names.push_back("test_mod" + kstd::to_string(i)); + m_module_data.emplace_back(module_size, std::byte{static_cast(0x40 + (i % 16))}); } for (std::size_t i = 0; i < module_count; ++i) { - registry.add_boot_module(kapi::boot_modules::boot_module{ - module_names[i], kapi::memory::linear_address{module_data[i].data()}, module_data[i].size()}); + m_registry.add_boot_module(kapi::boot_modules::boot_module{ + m_module_names[i].view(), kapi::memory::linear_address{m_module_data[i].data()}, m_module_data[i].size()}); + } + + kapi::boot_modules::set_boot_module_registry(m_registry); + kernel::devices::storage::management::init(); + } + + auto storage_boot_module_fixture::setup_modules_from_img(kstd::vector const & module_names, + kstd::vector const & img_paths) + -> void + { + m_module_names.clear(); + m_module_data.clear(); + m_registry = {}; + + if (module_names.size() != img_paths.size()) + { + throw std::invalid_argument{"Module names and image paths vectors must have the same size."}; + } + + for (size_t i = 0; i < module_names.size(); ++i) + { + setup_module_from_img(module_names[i], img_paths[i]); } - kapi::boot_modules::set_boot_module_registry(registry); + kapi::boot_modules::set_boot_module_registry(m_registry); kernel::devices::storage::management::init(); } + + auto storage_boot_module_fixture::setup_module_from_img(kstd::string const & module_name, + std::filesystem::path const & img_path) -> void + { + auto file = std::ifstream{img_path, std::ios::binary | std::ios::ate}; + if (!file) + { + throw std::runtime_error{"Failed to open image file for test boot module: " + img_path.string()}; + } + + auto const end_pos = file.tellg(); + if (end_pos < 0) + { + throw std::runtime_error{"Failed to determine image file size for test boot module: " + img_path.string()}; + } + + auto const size = static_cast(end_pos); + file.seekg(0, std::ios::beg); + + m_module_names.push_back(module_name); + m_module_data.emplace_back(size); + + if (size > 0) + { + file.read(reinterpret_cast(m_module_data.back().data()), static_cast(size)); + if (!file) + { + throw std::runtime_error{"Failed to read image file content for test boot module: " + img_path.string()}; + } + } + + m_registry.add_boot_module(kapi::boot_modules::boot_module{ + m_module_names.back().view(), kapi::memory::linear_address{m_module_data.back().data()}, + m_module_data.back().size()}); + } } // namespace kernel::tests::filesystem \ No newline at end of file diff --git a/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp new file mode 100644 index 0000000..cba7278 --- /dev/null +++ b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp @@ -0,0 +1,32 @@ +#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" + +#include "kernel/filesystem/vfs.hpp" +#include "kernel/test_support/filesystem/vfs.hpp" + +#include +#include + +#include +#include + +namespace kernel::tests::filesystem +{ + storage_boot_module_vfs_fixture::~storage_boot_module_vfs_fixture() + { + kernel::tests::filesystem::vfs::deinit(); + } + + auto storage_boot_module_vfs_fixture::setup_modules_and_init_vfs(std::size_t module_count, std::size_t module_size) + -> void + { + setup_modules(module_count, module_size); + kernel::filesystem::vfs::init(); + } + + auto storage_boot_module_vfs_fixture::setup_modules_from_img_and_init_vfs( + kstd::vector const & module_names, kstd::vector const & img_paths) -> void + { + setup_modules_from_img(module_names, img_paths); + kernel::filesystem::vfs::init(); + } +} // namespace kernel::tests::filesystem \ No newline at end of file diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img new file mode 100644 index 0000000..9f1ee4a --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94d3988bc309eb9e81f06042c72bf4c4fb5991cd7fdd597eb00c518a96c792d8 +size 10485760 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img new file mode 100644 index 0000000..1880911 --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a13da5abb9c65c737105b1da0d4344c7cd7604c7952c762c4f4e3d3f96fd42d +size 5242880 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img new file mode 100644 index 0000000..3aaceb8 --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ce6a1aea277906e1af6de223c017ff900b96569f076b4d99fc04eaa1ee986f4 +size 10485760 -- cgit v1.2.3 From a03d4d9acc5115ec3f651b06f94ced9a7c0e192f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 10:56:25 +0200 Subject: add open_file_description tests with real image --- .../src/filesystem/open_file_description.tests.cpp | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'kernel') diff --git a/kernel/src/filesystem/open_file_description.tests.cpp b/kernel/src/filesystem/open_file_description.tests.cpp index f045094..db8eb49 100644 --- a/kernel/src/filesystem/open_file_description.tests.cpp +++ b/kernel/src/filesystem/open_file_description.tests.cpp @@ -1,6 +1,8 @@ #include "kernel/filesystem/open_file_description.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 #include @@ -8,6 +10,10 @@ #include +#include +#include +#include + SCENARIO("Open file description construction", "[filesystem][open_file_description]") { GIVEN("an inode and an open file description for that inode") @@ -58,3 +64,51 @@ SCENARIO("Open file description read/write offset management", "[filesystem][ope } } } + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, + "Open file description read with real image", "[filesystem][open_file_description][img]") +{ + auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + + GIVEN("an open file description 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 ofd = vfs.open("/information/info_1.txt"); + REQUIRE(ofd != nullptr); + + THEN("the file can be read and the offset is updated") + { + kstd::vector buffer(32); + auto bytes_read = ofd->read(buffer.data(), buffer.size()); + REQUIRE(bytes_read == 32); + REQUIRE(ofd->offset() == 32); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + auto const content_end = buffer_as_str.find('\0'); + REQUIRE(buffer_as_str.substr(0, content_end) == "info_1\n"); + + for (auto i = content_end; i < buffer_as_str.size(); ++i) + { + REQUIRE(buffer_as_str[i] == '\0'); + } + } + + THEN("the file can be read multiple times") + { + kstd::vector 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(buffer.data()), bytes_read_1 + bytes_read_2}; + REQUIRE(buffer_as_str == "info"); + } + } +} -- cgit v1.2.3 From 01d5f1b29fa04c69ac9f942a1075354ce5b25da2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 12:32:50 +0200 Subject: add ext2 inode and filesystem tests --- kernel/CMakeLists.txt | 3 + .../include/kernel/filesystem/ext2/filesystem.hpp | 21 +++ .../kernel/test_support/filesystem/ext2.hpp | 17 +++ kernel/src/filesystem/ext2/filesystem.cpp | 38 ++--- kernel/src/filesystem/ext2/filesystem.tests.cpp | 132 +++++++++++++++++ kernel/src/filesystem/ext2/inode.tests.cpp | 159 +++++++++++++++++++++ kernel/src/test_support/filesystem/ext2.cpp | 62 ++++++++ 7 files changed, 406 insertions(+), 26 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/ext2.hpp create mode 100644 kernel/src/filesystem/ext2/filesystem.tests.cpp create mode 100644 kernel/src/filesystem/ext2/inode.tests.cpp create mode 100644 kernel/src/test_support/filesystem/ext2.cpp (limited to 'kernel') diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 88d420b..2f6113a 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -105,6 +105,7 @@ else() "src/test_support/devices/character_device.cpp" "src/test_support/filesystem/inode.cpp" "src/test_support/filesystem/filesystem.cpp" + "src/test_support/filesystem/ext2.cpp" "src/test_support/filesystem/storage_boot_module_fixture.cpp" "src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" @@ -134,6 +135,8 @@ else() # Filesystem Subsystem Tests "src/filesystem/devfs/filesystem.tests.cpp" "src/filesystem/devfs/inode.tests.cpp" + "src/filesystem/ext2/filesystem.tests.cpp" + "src/filesystem/ext2/inode.tests.cpp" "src/filesystem/rootfs/filesystem.tests.cpp" "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 65324c8..a71385f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -18,6 +18,27 @@ namespace kernel::filesystem::ext2 { + /** + @brief Constants related to the ext2 filesystem. + */ + namespace constants + { + constexpr size_t inline base_block_size = 1024; + constexpr size_t inline superblock_offset = base_block_size; + constexpr uint16_t inline magic_number = 0xEF53; + + constexpr uint32_t inline root_inode_number = 2; + + constexpr size_t inline direct_block_count = 12; + constexpr size_t inline singly_indirect_block_index = direct_block_count; + constexpr size_t inline doubly_indirect_block_index = singly_indirect_block_index + 1; + constexpr size_t inline triply_indirect_block_index = doubly_indirect_block_index + 1; + + constexpr uint16_t inline mode_mask = 0xF000; + constexpr uint16_t inline mode_regular = 0x8000; + constexpr uint16_t inline mode_directory = 0x4000; + } // namespace constants + /** @brief A filesystem implementation for the ext2 filesystem format. This class provides methods for mounting an ext2 filesystem, and looking up inodes. diff --git a/kernel/include/kernel/test_support/filesystem/ext2.hpp b/kernel/include/kernel/test_support/filesystem/ext2.hpp new file mode 100644 index 0000000..edbda29 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/ext2.hpp @@ -0,0 +1,17 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP + +#include "kernel/test_support/devices/block_device.hpp" + +#include +#include + +namespace kernel::tests::filesystem::ext2 +{ + auto write_bytes(kernel::tests::devices::block_device & device, size_t offset, void const * source, size_t size) + -> void; + auto write_u32(kernel::tests::devices::block_device & device, size_t offset, uint32_t value) -> void; + auto setup_mock_ext2_layout(kernel::tests::devices::block_device & device) -> void; +} // namespace kernel::tests::filesystem::ext2 + +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 6d5960e..0ad5c97 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -21,29 +21,14 @@ namespace kernel::filesystem::ext2 { namespace { - constexpr size_t SUPERBLOCK_OFFSET = 1024; - constexpr uint16_t MAGIC_NUMBER = 0xEF53; - - constexpr uint32_t ROOT_INODE_NUMBER = 2; - - constexpr size_t DIRECT_BLOCK_COUNT = 12; - constexpr size_t SINGLY_INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; - constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = SINGLY_INDIRECT_BLOCK_INDEX + 1; - constexpr size_t TRIPLY_INDIRECT_BLOCK_INDEX = DOUBLY_INDIRECT_BLOCK_INDEX + 1; - - // Mode bits - constexpr uint16_t S_IFMT = 0xF000; - constexpr uint16_t S_IFREG = 0x8000; - constexpr uint16_t S_IFDIR = 0x4000; - auto S_ISREG(uint16_t mode) -> bool { - return (mode & S_IFMT) == S_IFREG; + return (mode & constants::mode_mask) == constants::mode_regular; } auto S_ISDIR(uint16_t mode) -> bool { - return (mode & S_IFMT) == S_IFDIR; + return (mode & constants::mode_mask) == constants::mode_directory; } } // namespace @@ -51,9 +36,10 @@ namespace kernel::filesystem::ext2 { kernel::filesystem::filesystem::mount(device); - kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); + kernel::devices::block_device_utils::read(m_device, &m_superblock, constants::superblock_offset, + sizeof(m_superblock)); - if (m_superblock.magic != MAGIC_NUMBER) + if (m_superblock.magic != constants::magic_number) { return operation_result::invalid_magic_number; } @@ -69,7 +55,7 @@ namespace kernel::filesystem::ext2 block_group_descriptor_table_offset, num_block_groups * sizeof(block_group_descriptor)); - m_root_inode = read_inode(ROOT_INODE_NUMBER); + m_root_inode = read_inode(constants::root_inode_number); if (!m_root_inode || !m_root_inode->is_directory()) { @@ -161,11 +147,11 @@ namespace kernel::filesystem::ext2 auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t { - if (inode_block_index < DIRECT_BLOCK_COUNT) + if (inode_block_index < constants::direct_block_count) { return data.block.at(inode_block_index); } - inode_block_index -= DIRECT_BLOCK_COUNT; + inode_block_index -= constants::direct_block_count; auto const block_size = get_block_size(); auto const numbers_per_block = block_size / sizeof(uint32_t); @@ -176,14 +162,14 @@ namespace kernel::filesystem::ext2 if (inode_block_index < block_numbers_per_singly_indirect_block) { - auto const singly_indirect_block_number = data.block.at(SINGLY_INDIRECT_BLOCK_INDEX); + auto const singly_indirect_block_number = data.block.at(constants::singly_indirect_block_index); return read_block_number_at_index(singly_indirect_block_number, inode_block_index); } inode_block_index -= block_numbers_per_singly_indirect_block; if (inode_block_index < block_numbers_per_doubly_indirect_block) { - auto const doubly_indirect_block_number = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX); + auto const doubly_indirect_block_number = data.block.at(constants::doubly_indirect_block_index); auto const singly_indirect_block_index_in_doubly_indirect_block = inode_block_index / block_numbers_per_singly_indirect_block; auto const singly_indirect_block_number = read_block_number_at_index( @@ -196,7 +182,7 @@ namespace kernel::filesystem::ext2 if (inode_block_index < block_numbers_per_triply_indirect_block) { - auto const triply_indirect_block_number = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX); + auto const triply_indirect_block_number = data.block.at(constants::triply_indirect_block_index); auto const doubly_indirect_block_index_in_triply_indirect_block = inode_block_index / block_numbers_per_doubly_indirect_block; auto const doubly_indirect_block_number = read_block_number_at_index( @@ -230,7 +216,7 @@ namespace kernel::filesystem::ext2 auto filesystem::get_block_size() -> size_t { - return 1024U << m_superblock.log_block_size; + return constants::base_block_size << m_superblock.log_block_size; } auto filesystem::get_inode_size() -> size_t diff --git a/kernel/src/filesystem/ext2/filesystem.tests.cpp b/kernel/src/filesystem/ext2/filesystem.tests.cpp new file mode 100644 index 0000000..b13ebf3 --- /dev/null +++ b/kernel/src/filesystem/ext2/filesystem.tests.cpp @@ -0,0 +1,132 @@ +#include "kernel/filesystem/ext2/filesystem.hpp" + +#include "kernel/devices/storage/management.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 +#include + +#include + +#include +#include +#include + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, + "Ext2 filesystem mount and lookup with real image", "[filesystem][ext2][filesystem][img]") +{ + auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + + GIVEN("a mounted ext2 filesystem from a real image") + { + REQUIRE(std::filesystem::exists(image_path)); + REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path})); + + auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); + REQUIRE(boot_device != nullptr); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success); + + THEN("the root inode is available and is a directory") + { + REQUIRE(fs.root_inode() != nullptr); + REQUIRE(fs.root_inode()->is_directory()); + } + + THEN("lookup resolves known entries from the image") + { + auto information = fs.lookup(fs.root_inode(), "information"); + REQUIRE(information != nullptr); + REQUIRE(information->is_directory()); + + auto info_1 = fs.lookup(information, "info_1.txt"); + REQUIRE(info_1 != nullptr); + REQUIRE(info_1->is_regular()); + } + + THEN("lookup returns null for invalid inputs") + { + REQUIRE(fs.lookup(nullptr, "information") == nullptr); + + auto information = fs.lookup(fs.root_inode(), "information"); + REQUIRE(information != nullptr); + auto info_1 = fs.lookup(information, "info_1.txt"); + REQUIRE(info_1 != nullptr); + + REQUIRE(fs.lookup(info_1, "anything") == nullptr); + REQUIRE(fs.lookup(fs.root_inode(), "does_not_exist") == nullptr); + } + } +} + +SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem]") +{ + auto const block_size = 1024; + GIVEN("a block device that does not contain an ext2 superblock") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 2 * block_size); + REQUIRE(device != nullptr); + + auto fs = kernel::filesystem::ext2::filesystem{}; + + THEN("mount fails with invalid_magic_number") + { + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::invalid_magic_number); + } + } +} + +SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesystem][ext2][filesystem]") +{ + auto const block_size = 1024; + + GIVEN("a minimally valid ext2 layout with configured indirect block tables") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 128 * block_size); + REQUIRE(device != nullptr); + + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + + auto inode_data = kernel::filesystem::ext2::inode_data{}; + inode_data.block[0] = 7; + + inode_data.block[12] = 30; + kernel::tests::filesystem::ext2::write_u32(*device, 30 * block_size, 31); + + inode_data.block[13] = 40; + kernel::tests::filesystem::ext2::write_u32(*device, 40 * block_size, 41); + kernel::tests::filesystem::ext2::write_u32(*device, 41 * block_size, 42); + + inode_data.block[14] = 50; + kernel::tests::filesystem::ext2::write_u32(*device, 50 * block_size, 51); + kernel::tests::filesystem::ext2::write_u32(*device, 51 * block_size, 52); + kernel::tests::filesystem::ext2::write_u32(*device, 52 * block_size, 53); + + auto const numbers_per_block = static_cast(block_size / sizeof(uint32_t)); + auto const singly_start = static_cast(kernel::filesystem::ext2::constants::direct_block_count); + auto const doubly_start = singly_start + numbers_per_block; + auto const triply_start = doubly_start + numbers_per_block * numbers_per_block; + + THEN("mapping resolves direct, singly, doubly and triply indirect indexes") + { + REQUIRE(fs.map_inode_block_index_to_global_block_number(0, inode_data) == 7); + REQUIRE(fs.map_inode_block_index_to_global_block_number(singly_start, inode_data) == 31); + REQUIRE(fs.map_inode_block_index_to_global_block_number(doubly_start, inode_data) == 42); + REQUIRE(fs.map_inode_block_index_to_global_block_number(triply_start, inode_data) == 53); + } + + THEN("mapping returns zero for out-of-range indexes") + { + auto const beyond_triply = triply_start + numbers_per_block * numbers_per_block * numbers_per_block; + REQUIRE(fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data) == 0); + } + } +} diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp new file mode 100644 index 0000000..795ff10 --- /dev/null +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -0,0 +1,159 @@ +#include "kernel/filesystem/ext2/inode.hpp" + +#include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/ext2/filesystem.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 +#include + +#include + +#include +#include +#include + +SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]") +{ + GIVEN("an ext2 filesystem") + { + auto fs = kernel::filesystem::ext2::filesystem{}; + + THEN("the inode is initialized and has the kind regular") + { + auto inode = kernel::filesystem::ext2::inode{&fs}; + REQUIRE(inode.is_regular()); + REQUIRE(!inode.is_directory()); + REQUIRE(!inode.is_device()); + } + } + + 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 fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success); + + auto information = fs.lookup(fs.root_inode(), "information"); + REQUIRE(information != nullptr); + auto file = fs.lookup(information, "info_1.txt"); + REQUIRE(file != nullptr); + REQUIRE(file->is_regular()); + + THEN("reading from offset zero returns expected file prefix") + { + auto buffer = kstd::vector(6); + auto const bytes_read = file->read(buffer.data(), 0, buffer.size()); + + REQUIRE(bytes_read == 6); + + auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + REQUIRE(text == "info_1"); + } + + THEN("reading with an offset returns the expected byte") + { + auto buffer = kstd::vector(1); + auto const bytes_read = file->read(buffer.data(), 5, buffer.size()); + + REQUIRE(bytes_read == 1); + REQUIRE(static_cast(buffer[0]) == '1'); + } + } +} + +SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesystem][ext2][inode]") +{ + auto const block_size = 1024; + GIVEN("an ext2 inode without mapped data blocks") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + + auto inode = kernel::filesystem::ext2::inode{&fs}; + inode.m_data.blocks = 2; + inode.m_data.block[0] = 0; + + auto buffer = kstd::vector(32, std::byte{0xAB}); + + THEN("no bytes are read") + { + auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); + REQUIRE(bytes_read == 0); + } + } +} + +SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") +{ + auto const block_size = 1024; + GIVEN("an ext2 inode with two direct blocks and a block size of 1024 bytes") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + + auto inode = kernel::filesystem::ext2::inode{&fs}; + inode.m_data.blocks = 2; + inode.m_data.block[0] = 20; + kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6); + inode.m_data.block[1] = 21; + kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size, "World!", 6); + + auto buffer = kstd::vector(12, std::byte{0x00}); + + THEN("reading across the block boundary returns the combined content") + { + auto const bytes_read = inode.read(buffer.data(), block_size - 6, buffer.size()); + REQUIRE(bytes_read == 12); + + auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + REQUIRE(text == "Hello World!"); + } + } +} + +SCENARIO("Ext2 inode write is not implemented", "[filesystem][ext2][inode]") +{ + GIVEN("an ext2 inode") + { + auto fs = kernel::filesystem::ext2::filesystem{}; + auto inode = kernel::filesystem::ext2::inode{&fs}; + + THEN("writing to the inode panics") + { + auto buffer = kstd::vector(32, std::byte{0x00}); + REQUIRE_THROWS_AS(inode.write(buffer.data(), 0, buffer.size()), kernel::tests::cpu::halt); + } + } +} diff --git a/kernel/src/test_support/filesystem/ext2.cpp b/kernel/src/test_support/filesystem/ext2.cpp new file mode 100644 index 0000000..3627373 --- /dev/null +++ b/kernel/src/test_support/filesystem/ext2.cpp @@ -0,0 +1,62 @@ +#include "kernel/test_support/filesystem/ext2.hpp" + +#include "kernel/filesystem/ext2/block_group_descriptor.hpp" +#include "kernel/filesystem/ext2/filesystem.hpp" +#include "kernel/filesystem/ext2/inode.hpp" +#include "kernel/filesystem/ext2/superblock.hpp" +#include "kernel/test_support/devices/block_device.hpp" + +#include +#include + +namespace kernel::tests::filesystem::ext2 +{ + namespace + { + constexpr uint32_t root_directory_data_block = 20; + } // namespace + + auto write_bytes(kernel::tests::devices::block_device & device, size_t offset, void const * source, size_t size) + -> void + { + auto const required_size = offset + size; + if (device.data.size() < required_size) + { + device.data.resize(required_size, 0); + } + + std::memcpy(device.data.data() + offset, source, size); + } + + auto write_u32(kernel::tests::devices::block_device & device, size_t offset, uint32_t value) -> void + { + write_bytes(device, offset, &value, sizeof(value)); + } + + auto setup_mock_ext2_layout(kernel::tests::devices::block_device & device) -> void + { + 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.rev_level = 1; + superblock.inode_size = 128; + write_bytes(device, kernel::filesystem::ext2::constants::superblock_offset, &superblock, sizeof(superblock)); + + auto group_descriptor = kernel::filesystem::ext2::block_group_descriptor{}; + group_descriptor.inode_table = 5; + write_bytes(device, 2048, &group_descriptor, sizeof(group_descriptor)); + + auto root_inode_data = kernel::filesystem::ext2::inode_data{}; + root_inode_data.mode = kernel::filesystem::ext2::constants::mode_directory; + root_inode_data.blocks = 2; + root_inode_data.block[0] = root_directory_data_block; + + auto const root_inode_offset = + static_cast(group_descriptor.inode_table) * kernel::filesystem::ext2::constants::base_block_size + + (kernel::filesystem::ext2::constants::root_inode_number - 1) * superblock.inode_size; + write_bytes(device, root_inode_offset, &root_inode_data, sizeof(root_inode_data)); + } +} // namespace kernel::tests::filesystem::ext2 \ No newline at end of file -- cgit v1.2.3 From 48d01e976cc250c2630c1b599a02cea60b56d32d Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 12 Apr 2026 19:08:50 +0200 Subject: Rename --- kernel/src/filesystem/vfs.tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index fec32e1..f363041 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -47,10 +47,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("vfs initializes and provides expected mount points") { - auto & volatilefs = kernel::filesystem::vfs::get(); - auto root = volatilefs.open("/"); - auto dev = volatilefs.open("/dev"); - auto information = volatilefs.open("/information/info_1.txt"); + 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); -- cgit v1.2.3 From cb7edbe6d4454ee5b217b522f62f4a7b92475a32 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 12 Apr 2026 19:12:15 +0200 Subject: Add comment --- kernel/include/kernel/filesystem/inode.hpp | 1 + 1 file changed, 1 insertion(+) (limited to 'kernel') diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 072f198..5208be2 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -69,6 +69,7 @@ namespace kernel::filesystem */ [[nodiscard]] auto is_device() const -> bool; + // TODO BA-FS26 avoid public member public: inode_kind m_kind{inode_kind::regular}; }; -- cgit v1.2.3