diff options
| author | Marcel Braun <marcel.braun@ost.ch> | 2026-05-05 23:25:47 +0200 |
|---|---|---|
| committer | Marcel Braun <marcel.braun@ost.ch> | 2026-05-05 23:25:47 +0200 |
| commit | ea450f4ed742a37b40a4e1dcaf8d17328d635472 (patch) | |
| tree | cd9511ff9145dd993f257c2fcca4d8d06e857625 /kernel | |
| parent | 3082340fa8ab3c7c0da5d2f9d321d2367d399b20 (diff) | |
| parent | 4522374b902ee9a30c83c2ec23880522e80febea (diff) | |
| download | kernel-ea450f4ed742a37b40a4e1dcaf8d17328d635472.tar.xz kernel-ea450f4ed742a37b40a4e1dcaf8d17328d635472.zip | |
Merge branch 'symbolic-links' into 'develop-BA-FS26'
Symbolic links
See merge request teachos/kernel!29
Diffstat (limited to 'kernel')
40 files changed, 769 insertions, 352 deletions
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 909ccf7..860e28b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -188,6 +188,7 @@ if(BUILD_TESTING) "src/filesystem/devfs/inode.tests.cpp" "src/filesystem/ext2/filesystem.tests.cpp" "src/filesystem/ext2/inode.tests.cpp" + "src/filesystem/path.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/constants.hpp b/kernel/include/kernel/filesystem/constants.hpp new file mode 100644 index 0000000..aff512a --- /dev/null +++ b/kernel/include/kernel/filesystem/constants.hpp @@ -0,0 +1,13 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_CONSTANTS_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_CONSTANTS_HPP + +#include <cstddef> +namespace kernel::filesystem::constants +{ + constexpr size_t inline max_path_length = 4096; + + constexpr size_t inline symlink_max_path_length = 4096; + constexpr size_t inline symloop_max = 40; +} // namespace kernel::filesystem::constants + +#endif
\ No newline at end of file diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 94fa39a..bd62735 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -24,7 +24,7 @@ namespace kernel::filesystem */ enum class dentry_flags : uint32_t { - dcache_mounted = 1 << 15 + mounted = 1 << 15 }; /** @@ -55,6 +55,12 @@ namespace kernel::filesystem [[nodiscard]] auto get_name() const -> std::string_view; /** + @brief Get the full path of the dentry by traversing up to the root. + @return The full path of the dentry. + */ + [[nodiscard]] auto get_full_path() const -> kstd::string; + + /** @brief Add a @p child dentry. @param child The child dentry to add. */ diff --git a/kernel/include/kernel/filesystem/devfs/inode.hpp b/kernel/include/kernel/filesystem/devfs/inode.hpp index 0fab280..e428891 100644 --- a/kernel/include/kernel/filesystem/devfs/inode.hpp +++ b/kernel/include/kernel/filesystem/devfs/inode.hpp @@ -14,11 +14,6 @@ namespace kernel::filesystem::devfs 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. @@ -35,6 +30,12 @@ namespace kernel::filesystem::devfs @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 Check if this inode represents a directory. + @return returns true, since this inode represents the /dev directory in the devfs filesystem. + */ + [[nodiscard]] auto is_directory() const -> bool override; }; } // namespace kernel::filesystem::devfs diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 2f79fca..f4aa2d1 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -50,6 +50,12 @@ namespace kernel::filesystem */ [[nodiscard]] auto device() const -> kstd::shared_ptr<kapi::devices::device> const &; + /** + @brief Check if this inode represents a device. + @return returns true, since this indoe is a device inode and represents a device. + */ + [[nodiscard]] auto is_device() const -> bool override; + private: kstd::shared_ptr<kapi::devices::device> m_device; }; diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 516e71d..d22433f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -35,6 +35,7 @@ namespace kernel::filesystem::ext2 constexpr uint16_t inline mode_mask = 0xF000; constexpr uint16_t inline mode_regular = 0x8000; constexpr uint16_t inline mode_directory = 0x4000; + constexpr uint16_t inline mode_symbolic_link = 0xA000; } // namespace constants /** diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 9291eea..b8f892a 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -20,7 +20,7 @@ namespace kernel::filesystem::ext2 { uint16_t mode; uint16_t uid; - uint32_t size; + uint32_t size; // TODO BA-FS26 signed? uint32_t atime; uint32_t ctime; uint32_t mtime; @@ -42,8 +42,10 @@ namespace kernel::filesystem::ext2 { /** @brief Create an ext2 inode associated with the given filesystem. + @param fs The ext2 filesystem that this inode belongs to. + @param data The data associated with this inode, read from the disk. */ - explicit inode(filesystem * fs); + explicit inode(filesystem * fs, inode_data const & data); /** @brief Reads from the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. @@ -65,12 +67,32 @@ namespace kernel::filesystem::ext2 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{}; + @brief Get the data associated with this inode. + @return A reference to the inode data. + */ + [[nodiscard]] auto data() const -> inode_data const &; + + /** + @brief Check if this inode represents a directory. + @return returns true if this inode represents a directory, false otherwise. + */ + [[nodiscard]] auto is_directory() const -> bool override; + + /** + @brief Check if this inode represents a regular file. + @return returns true if this inode represents a regular file, false otherwise. + */ + [[nodiscard]] auto is_regular() const -> bool override; + + /** + @brief Check if this inode represents a symbolic link. + @return returns true if this inode represents a symbolic link, false otherwise. + */ + [[nodiscard]] auto is_symbolic_link() const -> bool override; private: filesystem * m_filesystem; + inode_data m_data{}; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 5208be2..b34b921 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -11,20 +11,9 @@ namespace kernel::filesystem struct inode { /** - @brief Represents the kind of an inode. + @brief Create an inode. */ - enum class inode_kind - { - regular, - directory, - device - }; - - /** - @brief Create an inode with the given @p kind. - @param kind The kind of the inode. - */ - explicit inode(inode_kind kind); + inode() = default; /** @brief Virtual destructor for the inode. @@ -55,23 +44,25 @@ namespace kernel::filesystem @brief Returns whether the inode is a directory. @return true if the inode is a directory, false otherwise. */ - [[nodiscard]] auto is_directory() const -> bool; + [[nodiscard]] virtual 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; + [[nodiscard]] virtual 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; + [[nodiscard]] virtual auto is_device() const -> bool; - // TODO BA-FS26 avoid public member - public: - inode_kind m_kind{inode_kind::regular}; + /** + @brief Returns whether the inode is a symbolic link. + @return true if the inode is a symbolic link, false otherwise. + */ + [[nodiscard]] virtual auto is_symbolic_link() const -> bool; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 00277ea..8e57d9e 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -39,13 +39,19 @@ namespace kernel::filesystem [[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 - mounted filesystem should handle a given path lookup. + @brief Finds the mount with the longest prefix matching the given @p path. @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<mount>; + /** + @brief Finds the mount with the exact mount path matching the given @p path. + @param path The path to match against the mount paths in the table. + @return A pointer to the mount with the exact matching path, or a null pointer if no mount matches the path. + */ + [[nodiscard]] auto find_exact_mount(std::string_view path) const -> kstd::shared_ptr<mount>; + private: [[nodiscard]] auto has_child_mounts(kstd::shared_ptr<mount> const & parent_mount) const -> bool; diff --git a/kernel/include/kernel/filesystem/open_file_table.hpp b/kernel/include/kernel/filesystem/open_file_table.hpp index 2f9a421..694f3b6 100644 --- a/kernel/include/kernel/filesystem/open_file_table.hpp +++ b/kernel/include/kernel/filesystem/open_file_table.hpp @@ -34,10 +34,10 @@ namespace kernel::filesystem /** @brief Add a file to the open file table. - @param f The file descriptor to add. + @param fd The file descriptor to add. @return The file descriptor index assigned to the file, or -1 on failure. */ - auto add_file(kstd::shared_ptr<open_file_descriptor> const & f) -> int; + auto add_file(kstd::shared_ptr<open_file_descriptor> const & fd) -> int; /** @brief Get a file from the open file table. diff --git a/kernel/include/kernel/filesystem/path.hpp b/kernel/include/kernel/filesystem/path.hpp new file mode 100644 index 0000000..4845bf1 --- /dev/null +++ b/kernel/include/kernel/filesystem/path.hpp @@ -0,0 +1,71 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP + +#include <kernel/filesystem/constants.hpp> + +#include <kstd/string> + +#include <ranges> +#include <string_view> + +namespace kernel::filesystem::path +{ + /** + @brief Provides utilities for handling filesystem paths, including validation and splitting into components. + */ + + /** + @brief Checks if the given path is within the maximum allowed length. + @param path The path to check. + @return true if the path length is valid, false otherwise. + */ + auto inline is_valid_path_length(std::string_view path) -> bool + { + return path.length() < kernel::filesystem::constants::max_path_length; + } + + /** + @brief Checks if the given path is a valid absolute path (starts with '/'). + @param path The path to check. + @return true if the path is a valid absolute path, false otherwise. + */ + auto inline is_valid_absolute_path(std::string_view path) -> bool + { + return !path.empty() && path.front() == '/' && is_valid_path_length(path); + } + + /** + @brief Checks if the given path is a valid relative path (doesn't start with '/'). + @param path The path to check. + @return true if the path is a valid relative path, false otherwise. + */ + auto inline is_valid_relative_path(std::string_view path) -> bool + { + return !path.empty() && path.front() != '/' && is_valid_path_length(path); + } + + /** + @brief Checks if the given path is a valid path (either absolute or relative). + @param path The path to check. + @return true if the path is a valid path, false otherwise. + */ + auto inline is_valid_path(std::string_view path) -> bool + { + return is_valid_absolute_path(path) || is_valid_relative_path(path); + } + + /** + @brief Splits the given path into its components. + @param path The path to split. + @return A range of strings representing the components of the path. + */ + auto inline split(std::string_view path) + { + return std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }) | + std::views::transform( + [](auto const & part) { return kstd::string(std::string_view(part.begin(), part.end())); }); + } + +} // namespace kernel::filesystem::path + +#endif // TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP
\ No newline at end of file diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 37d0a30..58035ea 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -22,11 +22,6 @@ namespace kernel::filesystem::rootfs 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. @@ -57,6 +52,12 @@ namespace kernel::filesystem::rootfs */ auto lookup_child(std::string_view name) -> kstd::shared_ptr<inode>; + /** + @brief Check if this inode represents a directory. + @return returns true, since this inode represents the root directory in the rootfs filesystem. + */ + [[nodiscard]] auto is_directory() const -> bool override; + private: kstd::vector<std::pair<kstd::string, kstd::shared_ptr<inode>>> m_children; }; diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 881f458..7e66fb7 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -77,8 +77,8 @@ namespace kernel::filesystem auto init_internal() -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr<dentry>; - auto do_mount_internal(std::string_view path, kstd::shared_ptr<dentry> const & mount_point_dentry, - kstd::shared_ptr<filesystem> const & fs) -> void; + auto do_mount_internal(kstd::shared_ptr<dentry> const & mount_point_dentry, kstd::shared_ptr<filesystem> const & fs) + -> void; mount_table m_mount_table; }; diff --git a/kernel/include/kernel/test_support/filesystem/inode.hpp b/kernel/include/kernel/test_support/filesystem/inode.hpp index 9d17917..8a76437 100644 --- a/kernel/include/kernel/test_support/filesystem/inode.hpp +++ b/kernel/include/kernel/test_support/filesystem/inode.hpp @@ -9,10 +9,10 @@ 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; + + [[nodiscard]] auto is_regular() const -> bool override; }; } // namespace kernel::tests::filesystem diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp index baa8613..1d1f8ee 100644 --- a/kernel/kapi/filesystem.tests.cpp +++ b/kernel/kapi/filesystem.tests.cpp @@ -16,7 +16,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap 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"; - GIVEN("a real image file") + GIVEN("Two real image files") { REQUIRE(std::filesystem::exists(image_path_1)); REQUIRE(std::filesystem::exists(image_path_2)); @@ -38,6 +38,53 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap REQUIRE(kapi::filesystem::close(fd) == 0); } + THEN("files can be opened through absolute symbolic link, read and closed again") + { + auto fd = kapi::filesystem::open("/symlinks/information_directory_absolute/info_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector<std::byte>(6); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)}; + REQUIRE(buffer_as_str == "info_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + + THEN("files can be opened through relative symbolic link, read and closed again") + { + auto fd = kapi::filesystem::open("/symlinks/information_directory_relative/info_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector<std::byte>(6); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)}; + REQUIRE(buffer_as_str == "info_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + + THEN("files can be opened through relative symbolic link over multiple mount points, read and closed again") + { + kapi::filesystem::mount("/archiv/2024.img", "/information"); + + auto fd = kapi::filesystem::open("/information/symlinks/traverse_back_twice/information/sheep_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector<std::byte>(7); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)}; + REQUIRE(buffer_as_str == "sheep_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + THEN("a filesystem can be mounted, files can be opened, read and closed again and unmounted") { REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); @@ -114,14 +161,16 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("not opened files cannot be read from") { std::vector<std::byte> buffer(10); - auto bytes_read = kapi::filesystem::read(999, buffer.data(), buffer.size()); + auto const invalid_fd = 999uz; + auto bytes_read = kapi::filesystem::read(invalid_fd, buffer.data(), buffer.size()); REQUIRE(bytes_read < 0); } THEN("not opened files cannot be written to") { std::vector<std::byte> buffer(10); - auto bytes_written = kapi::filesystem::write(999, buffer.data(), buffer.size()); + auto const invalid_fd = 999uz; + auto bytes_written = kapi::filesystem::write(invalid_fd, buffer.data(), buffer.size()); REQUIRE(bytes_written < 0); } } diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 572dd82..1cf8730 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -5,6 +5,7 @@ #include <kapi/system.hpp> #include <kstd/memory> +#include <kstd/string> #include <algorithm> #include <cstdint> @@ -38,6 +39,22 @@ namespace kernel::filesystem return m_name.view(); } + // NOLINTNEXTLINE(misc-no-recursion) + auto dentry::get_full_path() const -> kstd::string + { + if (m_parent) + { + auto parent_path = m_parent->get_full_path(); + if (parent_path != "/") + { + parent_path += '/'; + } + return parent_path + m_name.view(); + } + + return m_name.view(); + } + auto dentry::add_child(kstd::shared_ptr<dentry> const & child) -> void { m_children.push_back(child); diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index f81c260..c42c405 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -28,7 +28,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } @@ -45,7 +45,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } @@ -62,7 +62,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } @@ -113,22 +113,22 @@ SCENARIO("Dentry Flag logic", "[filesystem][dentry]") WHEN("setting a flag") { - dentry.set_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::mounted); THEN("the flag is set") { - REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::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); + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::mounted); + dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::mounted); THEN("the flag is unset") { - REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } } diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp index 2b6c09b..36cb411 100644 --- a/kernel/src/filesystem/devfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/devfs/filesystem.tests.cpp @@ -47,7 +47,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, { auto non_directory_inode = fs.lookup(fs.root_inode(), "ram0"); REQUIRE(non_directory_inode != nullptr); - REQUIRE(!non_directory_inode->is_directory()); + REQUIRE_FALSE(non_directory_inode->is_directory()); auto result = fs.lookup(non_directory_inode, "anything"); REQUIRE(result == nullptr); diff --git a/kernel/src/filesystem/devfs/inode.cpp b/kernel/src/filesystem/devfs/inode.cpp index 0ed66ad..2029a7f 100644 --- a/kernel/src/filesystem/devfs/inode.cpp +++ b/kernel/src/filesystem/devfs/inode.cpp @@ -1,15 +1,9 @@ #include <kernel/filesystem/devfs/inode.hpp> -#include <kernel/filesystem/inode.hpp> - #include <cstddef> namespace kernel::filesystem::devfs { - inode::inode() - : kernel::filesystem::inode(inode_kind::directory) - {} - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { return 0; @@ -19,4 +13,9 @@ namespace kernel::filesystem::devfs { return 0; } + + auto inode::is_directory() const -> bool + { + return true; + } } // namespace kernel::filesystem::devfs
\ No newline at end of file diff --git a/kernel/src/filesystem/devfs/inode.tests.cpp b/kernel/src/filesystem/devfs/inode.tests.cpp index 030d709..ae26e74 100644 --- a/kernel/src/filesystem/devfs/inode.tests.cpp +++ b/kernel/src/filesystem/devfs/inode.tests.cpp @@ -19,6 +19,7 @@ SCENARIO("Devfs inode creation", "[filesystem][devfs][inode]") REQUIRE(inode.is_directory()); REQUIRE_FALSE(inode.is_device()); REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_symbolic_link()); } } } diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 3bafe06..81a784c 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -1,7 +1,6 @@ #include <kernel/filesystem/device_inode.hpp> #include <kernel/devices/block_device_utils.hpp> -#include <kernel/filesystem/inode.hpp> #include <kapi/devices/device.hpp> #include <kapi/system.hpp> @@ -13,8 +12,7 @@ namespace kernel::filesystem { device_inode::device_inode(kstd::shared_ptr<kapi::devices::device> const & device) - : inode(inode_kind::device) - , m_device(device) + : m_device(device) { if (!device) { @@ -51,4 +49,9 @@ namespace kernel::filesystem return m_device; } + auto device_inode::is_device() const -> bool + { + return true; + } + } // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.tests.cpp b/kernel/src/filesystem/device_inode.tests.cpp index 8ac4eff..025a22a 100644 --- a/kernel/src/filesystem/device_inode.tests.cpp +++ b/kernel/src/filesystem/device_inode.tests.cpp @@ -33,6 +33,7 @@ SCENARIO("Device inode construction", "[filesystem][device_inode]") REQUIRE(inode.is_device()); REQUIRE_FALSE(inode.is_directory()); REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_symbolic_link()); } } diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 41572ee..47e54fe 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -16,19 +16,6 @@ namespace kernel::filesystem::ext2 { - namespace - { - auto S_ISREG(uint16_t mode) -> bool - { - return (mode & constants::mode_mask) == constants::mode_regular; - } - - auto S_ISDIR(uint16_t mode) -> bool - { - return (mode & constants::mode_mask) == constants::mode_directory; - } - } // namespace - auto filesystem::mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) -> operation_result { kernel::filesystem::filesystem::mount(backing_inode); @@ -74,7 +61,7 @@ namespace kernel::filesystem::ext2 } auto const block_size = get_block_size(); - auto const & inode_data = ext2_parent->m_data; + auto const & inode_data = ext2_parent->data(); kstd::vector<uint8_t> buffer(block_size); for (uint32_t i = 0; i < get_inode_block_count(inode_data); ++i) @@ -119,25 +106,10 @@ namespace kernel::filesystem::ext2 auto const inode_table_offset = static_cast<size_t>(inode_table_start_block) * block_size; auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); - auto new_inode = kstd::make_shared<inode>(this); - m_backing_inode->read(&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; - } + auto new_inode_data = inode_data{}; + m_backing_inode->read(&new_inode_data, inode_offset, sizeof(inode_data)); - return new_inode; + return kstd::make_shared<inode>(this, new_inode_data); } auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index c45c41e..1914c70 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -5,15 +5,17 @@ #include <kapi/system.hpp> +#include <kstd/cstring> + #include <algorithm> #include <cstddef> #include <cstdint> namespace kernel::filesystem::ext2 { - inode::inode(filesystem * fs) - : kernel::filesystem::inode(inode_kind::regular) - , m_filesystem(fs) + inode::inode(filesystem * fs, inode_data const & data) + : m_filesystem(fs) + , m_data(data) { if (!m_filesystem) { @@ -23,6 +25,17 @@ namespace kernel::filesystem::ext2 auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t { + // TODO BA-FS26 use revision 1 size + auto const max_readable = static_cast<size_t>(m_data.size) - offset; + auto const requested_size = std::min(size, max_readable); + + if (is_symbolic_link() && m_data.size <= sizeof(m_data.block)) + { + auto inline_target = reinterpret_cast<uint8_t const *>(m_data.block.data()); + kstd::libc::memcpy(static_cast<uint8_t *>(buffer), inline_target + offset, requested_size); + return requested_size; + } + auto block_index = offset / m_filesystem->get_block_size(); auto in_block_offset = offset % m_filesystem->get_block_size(); @@ -40,7 +53,8 @@ namespace kernel::filesystem::ext2 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); + auto const bytes_to_read = + std::min(requested_size - bytes_read, m_filesystem->get_block_size() - in_block_offset); bytes_read += m_filesystem->backing_inode()->read(static_cast<uint8_t *>(buffer) + bytes_read, read_offset, bytes_to_read); @@ -57,4 +71,24 @@ namespace kernel::filesystem::ext2 kapi::system::panic("[EXT2] inode::write is not implemented yet"); return 0; } + + [[nodiscard]] auto inode::data() const -> inode_data const & + { + return m_data; + } + + auto inode::is_regular() const -> bool + { + return (m_data.mode & constants::mode_mask) == constants::mode_regular; + } + + auto inode::is_directory() const -> bool + { + return (m_data.mode & constants::mode_mask) == constants::mode_directory; + } + + auto inode::is_symbolic_link() const -> bool + { + return (m_data.mode & constants::mode_mask) == constants::mode_symbolic_link; + } } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 4d61790..49ba21b 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -23,13 +23,50 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" GIVEN("an ext2 filesystem") { auto fs = kernel::filesystem::ext2::filesystem{}; + auto data = kernel::filesystem::ext2::inode_data{}; - THEN("the inode is initialized and has the kind regular") + THEN("the inode is initialized with regular file mode in data and has the kind regular") { - auto inode = kernel::filesystem::ext2::inode{&fs}; + data.mode = kernel::filesystem::ext2::constants::mode_regular; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + REQUIRE(inode.is_regular()); - REQUIRE(!inode.is_directory()); - REQUIRE(!inode.is_device()); + REQUIRE_FALSE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_symbolic_link()); + } + + THEN("the inode is initialized with directory mode in data and has the kind directory") + { + data.mode = kernel::filesystem::ext2::constants::mode_directory; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + + REQUIRE_FALSE(inode.is_regular()); + REQUIRE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_symbolic_link()); + } + + THEN("the inode is initialized with symbolic link mode in data and has the kind symbolic link") + { + data.mode = kernel::filesystem::ext2::constants::mode_symbolic_link; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + + REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE(inode.is_symbolic_link()); + } + + THEN("the inode is initialized with zero mode in data and has no specific kind") + { + data.mode = 0; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + + REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_symbolic_link()); } } @@ -37,7 +74,7 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" { THEN("constructing an inode with a null filesystem pointer panics") { - REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode{nullptr}, kernel::tests::cpu::halt); + REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode(nullptr, {}), kernel::tests::cpu::halt); } } } @@ -102,9 +139,10 @@ SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesyst auto fs = kernel::filesystem::ext2::filesystem{}; REQUIRE(fs.mount(dev_inode) == 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 data = kernel::filesystem::ext2::inode_data{}; + data.blocks = 2; + data.block[0] = 0; + auto inode = kernel::filesystem::ext2::inode{&fs, data}; auto buffer = kstd::vector<std::byte>(32, std::byte{0xAB}); @@ -130,12 +168,13 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") auto fs = kernel::filesystem::ext2::filesystem{}; REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - auto inode = kernel::filesystem::ext2::inode{&fs}; - inode.m_data.blocks = 2; - inode.m_data.block[0] = 20; + auto inode_data = kernel::filesystem::ext2::inode_data{}; + inode_data.blocks = 2; + inode_data.block[0] = 20; kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6); - inode.m_data.block[1] = 21; + inode_data.block[1] = 21; kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size, "World!", 6); + auto inode = kernel::filesystem::ext2::inode{&fs, inode_data}; auto buffer = kstd::vector<std::byte>(12, std::byte{0x00}); @@ -155,7 +194,7 @@ 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}; + auto inode = kernel::filesystem::ext2::inode{&fs, kernel::filesystem::ext2::inode_data{}}; THEN("writing to the inode panics") { diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp index 2f0764c..c188917 100644 --- a/kernel/src/filesystem/inode.cpp +++ b/kernel/src/filesystem/inode.cpp @@ -2,22 +2,23 @@ namespace kernel::filesystem { - inode::inode(inode_kind kind) - : m_kind(kind) - {} - auto inode::is_directory() const -> bool { - return m_kind == inode_kind::directory; + return false; } auto inode::is_regular() const -> bool { - return m_kind == inode_kind::regular; + return false; } auto inode::is_device() const -> bool { - return m_kind == inode_kind::device; + return false; + } + + auto inode::is_symbolic_link() const -> bool + { + return false; } } // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index da3c451..965e83b 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -54,7 +54,7 @@ namespace kernel::filesystem m_mounts.push_back(mount); if (auto mount_dentry = mount->get_mount_dentry()) { - mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); + mount_dentry->set_flag(dentry::dentry_flags::mounted); } } @@ -75,7 +75,7 @@ namespace kernel::filesystem return operation_result::has_child_mounts; } - mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::dcache_mounted); + mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::mounted); m_mounts.erase(std::ranges::find(m_mounts, mount)); return operation_result::removed; } @@ -102,4 +102,12 @@ namespace kernel::filesystem return mount_with_longest_prefix; } + + auto mount_table::find_exact_mount(std::string_view path) const -> kstd::shared_ptr<mount> + { + auto reversed_mounts = std::ranges::reverse_view(m_mounts); + auto mount_it = + std::ranges::find_if(reversed_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); + return (mount_it != reversed_mounts.end()) ? *mount_it : nullptr; + } } // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 747ffdf..efacdfe 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -54,11 +54,11 @@ 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_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } - THEN("finding mounts by path returns the correct mount") + THEN("finding mounts by longest prefix returns the correct mount") { REQUIRE(table.find_longest_prefix_mount("/") == mount1); REQUIRE(table.find_longest_prefix_mount("/file") == mount1); @@ -67,10 +67,22 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] REQUIRE(table.find_longest_prefix_mount("/other") == mount1); } + THEN("finding mounts by exact valid path returns the correct mount") + { + REQUIRE(table.find_exact_mount("/") == mount1); + REQUIRE(table.find_exact_mount("/mnt") == mount2); + } + + THEN("finding mounts by exact invalid path returns null") + { + REQUIRE(table.find_exact_mount("/nonexistent") == nullptr); + REQUIRE(table.find_exact_mount("/mnt/file") == nullptr); + } + THEN("removing a mount that has no child mounts succeeds") { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/mnt") == mount1); } @@ -97,7 +109,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] table.add_mount(mount1); table.add_mount(mount2); - THEN("finding mounts by path returns the correct mount based on longest prefix") + THEN("finding mounts by longest prefix returns the correct mount") { REQUIRE(table.find_longest_prefix_mount("/") == mount2); REQUIRE(table.find_longest_prefix_mount("/file") == mount2); @@ -106,10 +118,15 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] REQUIRE(table.find_longest_prefix_mount("/other") == mount2); } + THEN("finding mounts by exact valid path returns the correct mount") + { + REQUIRE(table.find_exact_mount("/") == 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_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/") == mount1); } } @@ -156,7 +173,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] 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_FALSE(root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount2); } } diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp index 095e203..53835ba 100644 --- a/kernel/src/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/src/filesystem/open_file_descriptor.tests.cpp @@ -1,4 +1,5 @@ #include <kernel/filesystem/open_file_descriptor.hpp> + #include <kernel/filesystem/vfs.hpp> #include <kernel/test_support/filesystem/inode.hpp> #include <kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp> @@ -83,17 +84,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope { kstd::vector<std::byte> buffer(32); auto bytes_read = ofd->read(buffer.data(), buffer.size()); - REQUIRE(bytes_read == 32); - REQUIRE(ofd->offset() == 32); + REQUIRE(bytes_read == 7); + REQUIRE(ofd->offset() == 7); std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)}; - 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'); - } + REQUIRE(buffer_as_str == "info_1\n"); } THEN("the file can be read multiple times") diff --git a/kernel/src/filesystem/path.tests.cpp b/kernel/src/filesystem/path.tests.cpp new file mode 100644 index 0000000..3c18b5c --- /dev/null +++ b/kernel/src/filesystem/path.tests.cpp @@ -0,0 +1,69 @@ +#include <kernel/filesystem/path.hpp> + +#include <catch2/catch_test_macros.hpp> + +#include <algorithm> +#include <string> +#include <string_view> +#include <vector> + +SCENARIO("path utilities", "[filesystem][path]") +{ + GIVEN("valid and invalid paths") + { + THEN("valid absolute paths are recognized as valid") + { + REQUIRE(kernel::filesystem::path::is_valid_path("/valid/absolute/path")); + REQUIRE(kernel::filesystem::path::is_valid_path("/")); + } + + THEN("valid relative paths are recognized as valid") + { + REQUIRE(kernel::filesystem::path::is_valid_path("valid/../relative/.././path")); + REQUIRE(kernel::filesystem::path::is_valid_path("valid/relative/path")); + REQUIRE(kernel::filesystem::path::is_valid_path("file.txt")); + } + + THEN("invalid paths are recognized as invalid") + { + REQUIRE_FALSE(kernel::filesystem::path::is_valid_path("")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_path(std::string(4096, 'a'))); + } + + THEN("valid absolute paths are recognized as absolute") + { + REQUIRE(kernel::filesystem::path::is_valid_absolute_path("/valid/absolute/path")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("valid/relative/path")); + } + + THEN("invalid paths are not recognized as absolute") + { + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path(std::string(4096, 'a'))); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("invalid/absolute/path")); + } + + THEN("valid relative paths are recognized as relative") + { + REQUIRE(kernel::filesystem::path::is_valid_relative_path("valid/relative/path")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("/valid/absolute/path")); + } + + THEN("invalid paths are not recognized as relative") + { + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path(std::string(4096, 'a'))); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("/invalid/absolute/path")); + } + } + + GIVEN("a valid path") + { + THEN("it can be split into components") + { + auto components = kernel::filesystem::path::split("/a/b///c/d.txt"); + std::vector<std::string_view> expected = {"a", "b", "c", "d.txt"}; + REQUIRE(std::ranges::equal(components, expected)); + } + } +}
\ No newline at end of file diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp index eeea3fe..d099676 100644 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -12,10 +12,6 @@ namespace kernel::filesystem::rootfs { - inode::inode() - : kernel::filesystem::inode(inode_kind::directory) - {} - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { return 0; @@ -36,4 +32,9 @@ namespace kernel::filesystem::rootfs auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; }); return (it != m_children.end()) ? it->second : nullptr; } + + auto inode::is_directory() const -> bool + { + return true; + } } // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp index 879818c..7cc217f 100644 --- a/kernel/src/filesystem/rootfs/inode.tests.cpp +++ b/kernel/src/filesystem/rootfs/inode.tests.cpp @@ -17,6 +17,7 @@ SCENARIO("Rootfs inode creation", "[filesystem][rootfs][inode]") REQUIRE(inode.is_directory()); REQUIRE_FALSE(inode.is_device()); REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_symbolic_link()); } THEN("the inode has no children") diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 5b454f6..519550b 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -1,16 +1,22 @@ #include <kernel/filesystem/vfs.hpp> +#include <kernel/filesystem/constants.hpp> #include <kernel/filesystem/dentry.hpp> #include <kernel/filesystem/devfs/filesystem.hpp> #include <kernel/filesystem/filesystem.hpp> #include <kernel/filesystem/mount.hpp> #include <kernel/filesystem/mount_table.hpp> +#include <kernel/filesystem/path.hpp> #include <kernel/filesystem/rootfs/filesystem.hpp> #include <kapi/system.hpp> #include <kstd/memory> +#include <kstd/string> +#include <kstd/vector> +#include <algorithm> +#include <cstdint> #include <optional> #include <ranges> #include <string_view> @@ -38,18 +44,34 @@ namespace kernel::filesystem auto root_fs = kstd::make_shared<rootfs::filesystem>(); root_fs->mount(nullptr); - auto root_fs_root_dentry = kstd::make_shared<dentry>(nullptr, root_fs->root_inode()); - m_mount_table.add_mount(kstd::make_shared<mount>(nullptr, root_fs_root_dentry, root_fs, "", nullptr)); + auto root_fs_root_dentry = kstd::make_shared<dentry>(nullptr, root_fs->root_inode(), "/"); + m_mount_table.add_mount(kstd::make_shared<mount>(nullptr, root_fs_root_dentry, root_fs, "/", nullptr)); + // Mount devfs at /dev in rootfs (temporary, will be shadowed) auto device_fs = kstd::make_shared<devfs::filesystem>(); device_fs->mount(nullptr); - do_mount_internal("/dev", root_fs_root_dentry, device_fs); + auto dev_mount_point_dentry = resolve_path("/dev"); + if (!dev_mount_point_dentry) + { + kapi::system::panic("[FILESYSTEM] failed to resolve /dev for initial devfs mount."); + } + do_mount_internal(dev_mount_point_dentry, device_fs); - if (auto boot_device_dentry = resolve_path("/dev/ram0")) // TODO BA-FS26 better boot device detection + // Mount boot filesystem at / (will shadow rootfs) + if (auto boot_device_dentry = resolve_path("/dev/ram0")) { if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) { - do_mount_internal("/", root_fs_root_dentry, boot_root_fs); + do_mount_internal(root_fs_root_dentry, boot_root_fs); + + // Resolve / to get the boot root dentry + if (auto boot_root_dentry = resolve_path("/")) + { + auto dev_dentry = kstd::make_shared<dentry>(boot_root_dentry, device_fs->root_inode(), "dev"); + boot_root_dentry->add_child(dev_dentry); + + do_mount_internal(dev_dentry, device_fs); + } } } } @@ -71,7 +93,7 @@ namespace kernel::filesystem auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result { - if (target.empty() || target.front() != '/' || (target.size() > 1 && target.back() == '/')) + if (!path::is_valid_path(source) || !path::is_valid_path(target)) { return operation_result::invalid_path; } @@ -82,7 +104,7 @@ namespace kernel::filesystem { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(target, mount_point_dentry, fs); + do_mount_internal(mount_point_dentry, fs); return operation_result::success; } return operation_result::invalid_filesystem; @@ -94,7 +116,7 @@ namespace kernel::filesystem auto vfs::unmount(std::string_view path) -> operation_result { - if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) + if (!path::is_valid_path(path)) { return operation_result::invalid_path; } @@ -113,55 +135,120 @@ namespace kernel::filesystem return operation_result::mount_point_not_found; } - auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr<dentry> const & mount_point_dentry, + auto vfs::do_mount_internal(kstd::shared_ptr<dentry> const & mount_point_dentry, kstd::shared_ptr<filesystem> const & fs) -> void { - auto parent_mount = m_mount_table.find_longest_prefix_mount(path); - auto new_fs_root = kstd::make_shared<dentry>(mount_point_dentry, fs->root_inode()); - auto new_mount = kstd::make_shared<mount>(mount_point_dentry, new_fs_root, fs, path, parent_mount); + auto mount_path = mount_point_dentry->get_full_path(); + // TODO BA-FS26 refactoring, implement dentry lookup to get the parent mount... + auto parent_mount = m_mount_table.find_longest_prefix_mount(mount_path.view()); + + auto new_fs_root = + kstd::make_shared<dentry>(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); + auto new_mount = kstd::make_shared<mount>(mount_point_dentry, new_fs_root, fs, mount_path.view(), parent_mount); m_mount_table.add_mount(new_mount); } auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr<dentry> { - // TODO BA-FS26 implement full path resolution semantics. - // TODO BA-FS26 better path validation - // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount - - if (path.empty() || path.front() != '/') + if (!path::is_valid_absolute_path(path)) + { return nullptr; + } - // TODO BA-FS26 longest match in mount_table -> jump into final fs directly - // TODO BA-FS26 performance optimization first check mounted_flag O(1) then check mount_table O(n) - - auto best_mount = m_mount_table.find_longest_prefix_mount(path); - if (!best_mount) + auto current_mount = m_mount_table.find_exact_mount("/"); + if (!current_mount) { kapi::system::panic("[FILESYSTEM] no root mount found."); } - auto current_dentry = best_mount->root_dentry(); - auto current_fs = best_mount->get_filesystem(); + auto current_dentry = current_mount->root_dentry(); - std::string_view remaining = path.substr(best_mount->get_mount_path().size()); + auto path_parts = path::split(path); + kstd::vector path_parts_vector(path_parts.begin(), path_parts.end()); + std::ranges::reverse(path_parts_vector); - auto path_parts = - std::views::split(remaining, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); + auto symlink_counter = 0uz; - for (auto const & part : path_parts) + while (!path_parts_vector.empty()) { - std::string_view part_view{part}; + auto part = path_parts_vector.back(); + path_parts_vector.pop_back(); + + if (part == ".") + { + continue; + } + + if (part == "..") + { + auto parent_dentry = current_dentry->get_parent(); + + if (current_dentry == current_mount->root_dentry()) + { + if (current_mount->get_mount_path() == "/") + { + continue; + } + + if (auto parent_mount = current_mount->get_parent_mount()) + { + current_mount = parent_mount; + current_dentry = parent_dentry; + } + } + + current_dentry = parent_dentry; + continue; + } - auto next_dentry = current_dentry->find_child(part_view); + auto next_dentry = current_dentry->find_child(part.view()); if (!next_dentry) { - auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view); + auto current_fs = current_mount->get_filesystem(); + auto found_inode = current_fs->lookup(current_dentry->get_inode(), part.view()); if (!found_inode) + { return nullptr; + } - next_dentry = kstd::make_shared<dentry>(current_dentry, found_inode, part_view); + next_dentry = kstd::make_shared<dentry>(current_dentry, found_inode, part.view()); current_dentry->add_child(next_dentry); } + else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) + { + current_mount = m_mount_table.find_exact_mount(next_dentry->get_full_path().view()); + if (!current_mount) + { + kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); + } + + next_dentry = current_mount->root_dentry(); + } + + if (next_dentry->get_inode()->is_symbolic_link()) + { + if (symlink_counter++ > constants::symloop_max) + { + return nullptr; + } + + kstd::vector<uint8_t> buffer(constants::symlink_max_path_length); + auto const bytes_read = next_dentry->get_inode()->read(buffer.data(), 0, buffer.size()); + auto const symbolic_link_path = std::string_view{reinterpret_cast<char const *>(buffer.data()), bytes_read}; + + auto symbolic_link_parts = path::split(symbolic_link_path); + kstd::vector symbolic_link_parts_vector(symbolic_link_parts.begin(), symbolic_link_parts.end()); + std::ranges::reverse(symbolic_link_parts_vector); + + path_parts_vector.insert_range(path_parts_vector.end(), symbolic_link_parts_vector); + + if (path::is_valid_absolute_path(symbolic_link_path)) + { + current_mount = m_mount_table.find_exact_mount("/"); + current_dentry = current_mount->root_dentry(); + } + continue; + } current_dentry = next_dentry; } diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 979ea42..8e4cb70 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -140,8 +140,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("mount with invalid path fails") { REQUIRE(vfs.do_mount("/dev/ram16", "") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("/dev/ram16", "information") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("/dev/ram16", "/information/") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("/dev/ram16", "information") == + kernel::filesystem::vfs::operation_result::mount_point_not_found); } THEN("mount with non-existent source path fails") @@ -159,8 +159,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS 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); + REQUIRE(vfs.unmount("information") == kernel::filesystem::vfs::operation_result::mount_point_not_found); } THEN("unmounting non-existent mount point returns expected error code") @@ -168,6 +167,52 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information/nonexistent") == kernel::filesystem::vfs::operation_result::mount_point_not_found); } + + THEN("a file can be access if . in the path") + { + auto info_1 = vfs.open("/information/./info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("a file can be accessed within the same mount if path contains .. ") + { + auto info_1 = vfs.open("/archiv/../information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + auto img = vfs.open("/archiv/../information/../archiv/2024.img"); + REQUIRE(img != nullptr); + } + + THEN("a file can be accessed over multiple mounts if path contains .. or . ") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + + auto img = vfs.open("/information/monkey_house/caretaker/../../../../../../archiv/2024.img"); + REQUIRE(img != nullptr); + + auto dev_32 = vfs.open("/information/monkey_house/caretaker/../../../dev/ram32"); + REQUIRE(dev_32 != nullptr); + + auto water = vfs.open("/information/./monkey_house/infrastructure/water.txt"); + REQUIRE(water != nullptr); + } + + THEN("a file can be accessed over multiple mounts (device and file) if path contains .. ") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information/monkey_house/infrastructure") == + kernel::filesystem::vfs::operation_result::success); + + auto pig_1 = vfs.open("/information/monkey_house/infrastructure/stable/pig_1.txt"); + REQUIRE(pig_1 != nullptr); + + auto isabelle = + vfs.open("/information/monkey_house/infrastructure/stable/../../../monkey_house/caretaker/isabelle.txt"); + REQUIRE(isabelle != nullptr); + + auto closed = vfs.open("/information/monkey_house/infrastructure/stable/../../../../closed.txt"); + REQUIRE(closed != nullptr); + } } GIVEN("A real image file containing as filesystem formatted files") @@ -237,4 +282,127 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(unmounted_sheep_1 == nullptr); } } + + GIVEN("A real image files, containing symbolic links") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); + + THEN("file can be opened through absolute symbolic link") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/info_1_absolute"); + REQUIRE(info_1 != nullptr); + } + + THEN("file can be opened through relative symbolic link") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/info_1_relative"); + REQUIRE(info_1 != nullptr); + } + + THEN("file can be opened through symbolic link pointing absolute to the directory containing the file") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/information_directory_absolute/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("file can be opened through symbolic link pointing relative to the directory containing the file") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/information_directory_relative/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("symbolic link with path traversing back to the root") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/traverse_back_5_times/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("symbolic link containing an invalid absolute path is handled correctly") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto invalid_symlink = vfs.open("/symlinks/invalid_absolute"); + REQUIRE(invalid_symlink == nullptr); + } + + THEN("symbolic link containing an invalid relative path is handled correctly") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto invalid_symlink = vfs.open("/symlinks/invalid_relative"); + REQUIRE(invalid_symlink == nullptr); + } + + THEN("circular symbolic links are detected and handled correctly") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto circular_symlink = vfs.open("/symlinks/symloop_a"); + REQUIRE(circular_symlink == nullptr); + } + } + + GIVEN("A real image file containing as filesystem formatted files and this filesystem contains a symbolic link") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); + + auto & vfs = kernel::filesystem::vfs::get(); + vfs.do_mount("/archiv/2024.img", "/information"); + + THEN("file can be opened through symbolic link pointing to the parent filesystem") + { + auto closed_file = vfs.open("/information/symlinks/traverse_back_twice/closed.txt"); + REQUIRE(closed_file != nullptr); + } + } + + GIVEN("Two real images, one containing a symbolic link leaving and reentering filesystem again") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE(std::filesystem::exists(image_path_2)); + REQUIRE_NOTHROW( + setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2"}, {image_path_1, image_path_2})); + + auto & vfs = kernel::filesystem::vfs::get(); + vfs.do_mount("/dev/ram16", "/information"); + + THEN("file can be opened through symbolic link pointing to the parent filesystem and back into the mounted " + "filesystem again") + { + auto monkey_1 = vfs.open("/information/symlinks/leave_and_reenter_mount/monkey_1.txt"); + REQUIRE(monkey_1 != nullptr); + } + } + + GIVEN("One real image containing a very long symbolic link") + { + REQUIRE(std::filesystem::exists(image_path_3)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_3"}, {image_path_3})); + + auto & vfs = kernel::filesystem::vfs::get(); + + THEN("file can be opened through symbolic link with a long path") + { + auto fish_30 = vfs.open("/symlinks/very_long_symlink"); + REQUIRE(fish_30 != nullptr); + } + } + + GIVEN("One real image containing a valid symbolic link chain") + { + REQUIRE(std::filesystem::exists(image_path_3)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_3"}, {image_path_3})); + + auto & vfs = kernel::filesystem::vfs::get(); + + THEN("file can be opened through symbolic link chain") + { + auto map = vfs.open("/symlinks/symlink_chain_1/map.txt"); + REQUIRE(map != nullptr); + } + } } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index ffea979..bfb731a 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -21,173 +21,11 @@ #include <kstd/units> #include <kstd/vector> -#include <algorithm> #include <cstddef> #include <string_view> using namespace kstd::units_literals; -auto test_device_names() -> void -{ - auto storage_mgmt = kernel::devices::storage::management::get(); - std::ranges::for_each(storage_mgmt.all_controllers(), [](auto const & controller) { - std::ranges::for_each(controller->all_devices(), - [](auto const & device) { kstd::println("{}", device->name().view()); }); - }); -} - -auto test_file_descriptor_manually() -> void -{ - // setup - auto fd_table = kernel::filesystem::open_file_table::get(); - auto storage_mgmt = kernel::devices::storage::management::get(); - auto device = storage_mgmt.device_by_major_minor(1, 0); - - auto dev_node = kstd::make_shared<kernel::filesystem::device_inode>(device); - - auto ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dev_node); - auto fd_index = fd_table.add_file(ofd); - - // use: read two bytes and write two again - auto fd = fd_table.get_file(fd_index); - if (!fd) - { - kstd::os::panic("test code failed"); - } - - kstd::vector<std::byte> buffer{2}; - auto number_of_read_bytes = fd->read(buffer.data(), buffer.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer); - - // write half of the file new - auto const value1 = std::byte{0xAA}; - auto const value2 = std::byte{0xBB}; - kstd::vector<std::byte> write_buffer{value1, value2}; - auto written_bytes = fd->write(write_buffer.data(), write_buffer.size()); - - kstd::println("written bytes: {}", written_bytes); - - fd_table.remove_file(fd_index); - - // use: read four bytes again -> two old bytes two new bytes - auto ofd1 = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dev_node); - fd_index = fd_table.add_file(ofd1); - auto fd1 = fd_table.get_file(fd_index); - - if (!fd1) - { - kstd::os::panic("test code failed"); - } - - kstd::vector<std::byte> buffer1{4}; - number_of_read_bytes = fd1->read(buffer1.data(), buffer1.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer1); -} - -auto test_device_with_vfs() -> void -{ - auto vfs = kernel::filesystem::vfs::get(); - auto dentry = vfs.open("/dev/ram0"); - if (!dentry) - { - kstd::os::panic("test code failed"); - } - - auto fd_table = kernel::filesystem::open_file_table::get(); - auto ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry->get_inode()); - auto fd = fd_table.add_file(ofd); - kstd::vector<std::byte> buffer{2}; - auto file = fd_table.get_file(fd); - if (!file) - { - kstd::os::panic("test code failed"); - } - - auto number_of_read_bytes = file->read(buffer.data(), buffer.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer); -} - -auto test_file_lookup() -> void -{ - auto vfs = kernel::filesystem::vfs::get(); - auto read_and_write_file = [&vfs](std::string_view path) { - kstd::println("[TEST] Reading and writing file at path: {}", path); - auto dentry = vfs.open(path); - if (!dentry) - { - kstd::os::panic("test code failed"); - } - - kstd::vector<std::byte> buffer{32}; - auto ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry->get_inode()); - 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<char *>(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"); - - vfs.do_mount("/dev/ram16", "/enclosures/aquarium"); - 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"); - - vfs.do_mount("/dev/ram32", "/enclosures/elephant_house"); - read_and_write_file("/enclosures/elephant_house/monkey_house/infrastructure/info.txt"); - - vfs.do_mount("/dev/ram16", "/enclosures/elephant_house/monkey_house"); - 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 -{ - kstd::println("[TEST] Running test code..."); - - kstd::println("[TEST] device names"); - test_device_names(); - kstd::println("---------------------------------"); - - kstd::println("[TEST] file descriptor manually"); - test_file_descriptor_manually(); - kstd::println("---------------------------------"); - - kstd::println("[TEST] device with VFS"); - test_device_with_vfs(); - kstd::println("---------------------------------"); - - kstd::println("[TEST] file lookup"); - test_file_lookup(); - kstd::println("---------------------------------"); -} - auto run_demo() -> void { // 1) open a file @@ -280,12 +118,8 @@ auto main() -> int kernel::filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); - // TODO BA-FS26 remove demo and test code again? + // TODO BA-FS26 remove demo code? // run_demo(); - // TODO BA-FS26 remove demo and test code again? - run_test_code(); - kstd::println("[TEST] All tests completed."); - kapi::system::panic("Returning from kernel main!"); } diff --git a/kernel/src/test_support/filesystem/inode.cpp b/kernel/src/test_support/filesystem/inode.cpp index 54bd7e0..0c8d956 100644 --- a/kernel/src/test_support/filesystem/inode.cpp +++ b/kernel/src/test_support/filesystem/inode.cpp @@ -6,10 +6,6 @@ namespace kernel::tests::filesystem { - inode::inode() - : kernel::filesystem::inode(inode_kind::regular) - {} - auto inode::read(void *, size_t, size_t size) const -> size_t { return size; @@ -19,4 +15,9 @@ namespace kernel::tests::filesystem { return size; } + + auto inode::is_regular() const -> bool + { + return true; + } } // namespace kernel::tests::filesystem
\ No newline at end of file diff --git a/kernel/src/test_support/filesystem/test_assets/README.md b/kernel/src/test_support/filesystem/test_assets/README.md new file mode 120000 index 0000000..718a227 --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/README.md @@ -0,0 +1 @@ +/arch/x86_64/support/modules/README.md
\ 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 index 5bbb76d..a5202ca 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2ef9536a439825520d9e230eedaa9ae327f9763350eddbc0f24bf5b9b5d2bf2 +oid sha256:98ac3c1be872806e25fb14eea168ca79a91959f4e6a5ac3d00c5d8224c1f73a3 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 index 1880911..8327022 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a13da5abb9c65c737105b1da0d4344c7cd7604c7952c762c4f4e3d3f96fd42d +oid sha256:a1d102f2e40083613060d43b2b32d31031137bbef99761a2d1bf4d38e155adb7 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 index 3aaceb8..c3f6daf 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ce6a1aea277906e1af6de223c017ff900b96569f076b4d99fc04eaa1ee986f4 +oid sha256:026ca30269dbd80beb2dd74677c94676d1d4a7f6b5fe406c4ddb82836ba2dc00 size 10485760 |
