aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorMarcel Braun <marcel.braun@ost.ch>2026-05-05 23:25:47 +0200
committerMarcel Braun <marcel.braun@ost.ch>2026-05-05 23:25:47 +0200
commitea450f4ed742a37b40a4e1dcaf8d17328d635472 (patch)
treecd9511ff9145dd993f257c2fcca4d8d06e857625 /kernel
parent3082340fa8ab3c7c0da5d2f9d321d2367d399b20 (diff)
parent4522374b902ee9a30c83c2ec23880522e80febea (diff)
downloadkernel-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')
-rw-r--r--kernel/CMakeLists.txt1
-rw-r--r--kernel/include/kernel/filesystem/constants.hpp13
-rw-r--r--kernel/include/kernel/filesystem/dentry.hpp8
-rw-r--r--kernel/include/kernel/filesystem/devfs/inode.hpp11
-rw-r--r--kernel/include/kernel/filesystem/device_inode.hpp6
-rw-r--r--kernel/include/kernel/filesystem/ext2/filesystem.hpp1
-rw-r--r--kernel/include/kernel/filesystem/ext2/inode.hpp32
-rw-r--r--kernel/include/kernel/filesystem/inode.hpp29
-rw-r--r--kernel/include/kernel/filesystem/mount_table.hpp10
-rw-r--r--kernel/include/kernel/filesystem/open_file_table.hpp4
-rw-r--r--kernel/include/kernel/filesystem/path.hpp71
-rw-r--r--kernel/include/kernel/filesystem/rootfs/inode.hpp11
-rw-r--r--kernel/include/kernel/filesystem/vfs.hpp4
-rw-r--r--kernel/include/kernel/test_support/filesystem/inode.hpp4
-rw-r--r--kernel/kapi/filesystem.tests.cpp55
-rw-r--r--kernel/src/filesystem/dentry.cpp17
-rw-r--r--kernel/src/filesystem/dentry.tests.cpp16
-rw-r--r--kernel/src/filesystem/devfs/filesystem.tests.cpp2
-rw-r--r--kernel/src/filesystem/devfs/inode.cpp11
-rw-r--r--kernel/src/filesystem/devfs/inode.tests.cpp1
-rw-r--r--kernel/src/filesystem/device_inode.cpp9
-rw-r--r--kernel/src/filesystem/device_inode.tests.cpp1
-rw-r--r--kernel/src/filesystem/ext2/filesystem.cpp36
-rw-r--r--kernel/src/filesystem/ext2/inode.cpp42
-rw-r--r--kernel/src/filesystem/ext2/inode.tests.cpp65
-rw-r--r--kernel/src/filesystem/inode.cpp15
-rw-r--r--kernel/src/filesystem/mount_table.cpp12
-rw-r--r--kernel/src/filesystem/mount_table.tests.cpp31
-rw-r--r--kernel/src/filesystem/open_file_descriptor.tests.cpp13
-rw-r--r--kernel/src/filesystem/path.tests.cpp69
-rw-r--r--kernel/src/filesystem/rootfs/inode.cpp9
-rw-r--r--kernel/src/filesystem/rootfs/inode.tests.cpp1
-rw-r--r--kernel/src/filesystem/vfs.cpp151
-rw-r--r--kernel/src/filesystem/vfs.tests.cpp176
-rw-r--r--kernel/src/main.cpp168
-rw-r--r--kernel/src/test_support/filesystem/inode.cpp9
l---------kernel/src/test_support/filesystem/test_assets/README.md1
-rw-r--r--kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img2
-rw-r--r--kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img2
-rw-r--r--kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img2
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