diff options
Diffstat (limited to 'kernel/src')
| -rw-r--r-- | kernel/src/filesystem/dentry.cpp | 11 | ||||
| -rw-r--r-- | kernel/src/filesystem/ext2/inode.tests.cpp | 2 | ||||
| -rw-r--r-- | kernel/src/filesystem/mount.cpp | 28 | ||||
| -rw-r--r-- | kernel/src/filesystem/mount.tests.cpp | 42 | ||||
| -rw-r--r-- | kernel/src/filesystem/mount_table.cpp | 27 | ||||
| -rw-r--r-- | kernel/src/filesystem/mount_table.tests.cpp | 10 | ||||
| -rw-r--r-- | kernel/src/filesystem/open_file_descriptor.cpp | 22 | ||||
| -rw-r--r-- | kernel/src/filesystem/open_file_descriptor.tests.cpp | 38 | ||||
| -rw-r--r-- | kernel/src/filesystem/open_file_table.tests.cpp | 10 | ||||
| -rw-r--r-- | kernel/src/filesystem/rootfs/filesystem.cpp | 9 | ||||
| -rw-r--r-- | kernel/src/filesystem/rootfs/filesystem.tests.cpp | 7 | ||||
| -rw-r--r-- | kernel/src/filesystem/rootfs/inode.cpp | 14 | ||||
| -rw-r--r-- | kernel/src/filesystem/rootfs/inode.tests.cpp | 47 | ||||
| -rw-r--r-- | kernel/src/filesystem/vfs.cpp | 97 | ||||
| -rw-r--r-- | kernel/src/filesystem/vfs.tests.cpp | 60 |
15 files changed, 250 insertions, 174 deletions
diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index c21771b..d963ed7 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -17,6 +17,7 @@ namespace kernel::filesystem : m_name(name) , m_parent(parent) , m_inode(inode) + , m_flags(0) { if (!m_inode) { @@ -66,16 +67,6 @@ namespace kernel::filesystem return path; } - auto dentry::find_mount_root_dentry() const -> kstd::shared_ptr<dentry> - { - auto parent = m_parent; - while (parent && !parent->has_flag(dentry_flags::is_mount_root)) - { - parent = parent->get_parent(); - } - return parent; - } - auto dentry::add_child(kstd::shared_ptr<dentry> const & child) -> void { m_children.push_back(child); diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 45bea51..efc0660 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -1,9 +1,9 @@ #include <kernel/filesystem/ext2/inode.hpp> -#include "kernel/filesystem/ext2/superblock.hpp" #include <kernel/devices/storage/management.hpp> #include <kernel/filesystem/device_inode.hpp> #include <kernel/filesystem/ext2/filesystem.hpp> +#include <kernel/filesystem/ext2/superblock.hpp> #include <kernel/filesystem/filesystem.hpp> #include <kernel/test_support/cpu.hpp> #include <kernel/test_support/devices/block_device.hpp> diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index 749c86a..1e04083 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -8,6 +8,7 @@ #include <kstd/memory> #include <kstd/string> +#include <cstddef> #include <string_view> namespace kernel::filesystem @@ -18,6 +19,7 @@ namespace kernel::filesystem , m_root_dentry(root_dentry) , m_filesystem(fs) , m_parent_mount(parent_mount) + , m_ref_count(0) { if (!m_filesystem) { @@ -53,4 +55,30 @@ namespace kernel::filesystem { return m_parent_mount; } + + auto mount::increment_ref_count() -> void + { + m_ref_count += 1; + } + + auto mount::decrement_ref_count() -> bool + { + if (m_ref_count == 0) + { + return false; + } + + m_ref_count -= 1; + return true; + } + + auto mount::is_ready_to_unmount() const -> bool + { + return m_ref_count == 0; + } + + auto mount::get_ref_count() const -> size_t + { + return m_ref_count; + } } // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index 58e9bab..e7dd709 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -29,6 +29,7 @@ SCENARIO("Mount construction", "[filesystem][mount]") REQUIRE(mount.get_root_dentry() == root_dentry); REQUIRE(mount.get_mount_dentry() == root_dentry); REQUIRE(mount.get_mount_path() == "/"); + REQUIRE(mount.is_ready_to_unmount()); } THEN("the mount has no parent mount") @@ -47,3 +48,44 @@ SCENARIO("Mount construction", "[filesystem][mount]") } } } + +SCENARIO("Mount reference counting", "[filesystem][mount]") +{ + GIVEN("a filesystem and a root dentry") + { + auto fs = kstd::make_shared<kernel::tests::filesystem::filesystem>(); + auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>(); + auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/"); + + THEN("reference count can be incremented and decremented, the mount is ready to unmount when the reference " + "count == 0") + { + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; + + mount.increment_ref_count(); + REQUIRE(mount.get_ref_count() == 1); + REQUIRE_FALSE(mount.is_ready_to_unmount()); + + mount.increment_ref_count(); + REQUIRE(mount.get_ref_count() == 2); + REQUIRE_FALSE(mount.is_ready_to_unmount()); + + REQUIRE(mount.decrement_ref_count()); + REQUIRE(mount.get_ref_count() == 1); + REQUIRE_FALSE(mount.is_ready_to_unmount()); + + REQUIRE(mount.decrement_ref_count()); + REQUIRE(mount.get_ref_count() == 0); + REQUIRE(mount.is_ready_to_unmount()); + } + + THEN("decrementing reference count when it is already zero does not decrement it below zero") + { + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; + + REQUIRE_FALSE(mount.decrement_ref_count()); + REQUIRE(mount.get_ref_count() == 0); + REQUIRE(mount.is_ready_to_unmount()); + } + } +}
\ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 5a49e7a..9951590 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -26,25 +26,21 @@ namespace kernel::filesystem { mount_dentry->set_flag(dentry::dentry_flags::is_mount_point); } - if (auto root_dentry = mount->get_root_dentry()) - { - root_dentry->set_flag(dentry::dentry_flags::is_mount_root); - } } auto mount_table::remove_mount(std::string_view path) -> operation_result { - // TODO BA-FS26 check wheter something is open in this mount - auto mount_range = - std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); - auto mount_it = mount_range.begin(); - + auto mount_it = find_mount_iterator(path); if (mount_it == m_mounts.end()) { return operation_result::mount_not_found; } auto const & mount = *mount_it; + if (!mount->is_ready_to_unmount()) + { + return operation_result::cannot_be_unmounted; + } if (has_child_mounts(mount)) { return operation_result::has_child_mounts; @@ -55,11 +51,16 @@ namespace kernel::filesystem return operation_result::removed; } - auto mount_table::find_exact_mount(std::string_view path) const -> kstd::shared_ptr<mount> + auto mount_table::find_mount(std::string_view path) const -> kstd::shared_ptr<mount> { - auto mount_range = - std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); - auto mount_it = mount_range.begin(); + auto mount_it = find_mount_iterator(path); return (mount_it != m_mounts.end()) ? *mount_it : nullptr; } + + auto mount_table::find_mount_iterator(std::string_view path) const + -> kstd::vector<kstd::shared_ptr<mount>>::const_iterator + { + return std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }) + .begin(); + } } // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 4ae8711..f22b25e 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -58,14 +58,14 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("finding mounts by exact valid path returns the correct mount") { - REQUIRE(table.find_exact_mount("/") == mount1); - REQUIRE(table.find_exact_mount("/mnt") == mount2); + REQUIRE(table.find_mount("/") == mount1); + REQUIRE(table.find_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); + REQUIRE(table.find_mount("/nonexistent") == nullptr); + REQUIRE(table.find_mount("/mnt/file") == nullptr); } THEN("removing a mount that has no child mounts succeeds") @@ -103,7 +103,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("finding mounts by exact valid path returns the correct mount") { - REQUIRE(table.find_exact_mount("/") == mount2); + REQUIRE(table.find_mount("/") == mount2); } THEN("removing the topmost mount with the same path succeeds") diff --git a/kernel/src/filesystem/open_file_descriptor.cpp b/kernel/src/filesystem/open_file_descriptor.cpp index 25bffbd..ebaabef 100644 --- a/kernel/src/filesystem/open_file_descriptor.cpp +++ b/kernel/src/filesystem/open_file_descriptor.cpp @@ -1,6 +1,7 @@ -#include <kernel/filesystem/inode.hpp> #include <kernel/filesystem/open_file_descriptor.hpp> +#include <kernel/filesystem/dentry.hpp> + #include <kstd/memory> #include <kstd/os/error.hpp> @@ -8,33 +9,38 @@ namespace kernel::filesystem { - open_file_descriptor::open_file_descriptor(kstd::shared_ptr<inode> const & inode) - : m_inode(inode) + open_file_descriptor::open_file_descriptor(kstd::shared_ptr<dentry> const & dentry) + : m_dentry(dentry) , m_offset(0) { - if (!inode) + if (!dentry) { - kstd::os::panic("[FILESYSTEM] open_file_descriptor constructed with null inode."); + kstd::os::panic("[FILESYSTEM] open_file_descriptor constructed with null dentry."); } } auto open_file_descriptor::read(void * buffer, size_t size) -> size_t { - auto read_bytes = m_inode->read(buffer, m_offset, size); + auto read_bytes = m_dentry->get_inode()->read(buffer, m_offset, size); m_offset += read_bytes; return read_bytes; } auto open_file_descriptor::write(void const * buffer, size_t size) -> size_t { - auto written_bytes = m_inode->write(buffer, m_offset, size); + auto written_bytes = m_dentry->get_inode()->write(buffer, m_offset, size); m_offset += written_bytes; return written_bytes; } - auto open_file_descriptor::offset() const -> size_t + auto open_file_descriptor::get_offset() const -> size_t { return m_offset; } + auto open_file_descriptor::get_dentry() const -> kstd::shared_ptr<dentry> const & + { + return m_dentry; + } + } // namespace kernel::filesystem
\ No newline at end of file diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp index 53835ba..1910b8b 100644 --- a/kernel/src/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/src/filesystem/open_file_descriptor.tests.cpp @@ -1,5 +1,7 @@ #include <kernel/filesystem/open_file_descriptor.hpp> +#include <kernel/filesystem/dentry.hpp> +#include <kernel/filesystem/inode.hpp> #include <kernel/filesystem/vfs.hpp> #include <kernel/test_support/filesystem/inode.hpp> #include <kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp> @@ -16,51 +18,53 @@ SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor]") { - GIVEN("an inode and an open file descriptor for that inode") + GIVEN("a dentry and an open file descriptor for that dentry") { auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto file_descriptor = kernel::filesystem::open_file_descriptor{inode}; + auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); + auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry}; THEN("the initial offset is zero") { - REQUIRE(file_descriptor.offset() == 0); + REQUIRE(file_descriptor.get_offset() == 0); } } } SCENARIO("Open file descriptor read/write offset management", "[filesystem][open_file_descriptor]") { - GIVEN("an inode that tracks read/write calls and an open file descriptor for that inode") + GIVEN("a dentry that tracks read/write calls and an open file descriptor for that dentry") { auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto file_descriptor = kernel::filesystem::open_file_descriptor{inode}; + auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); + auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry}; THEN("the offset is updated correctly after reads") { REQUIRE(file_descriptor.read(nullptr, 100) == 100); - REQUIRE(file_descriptor.offset() == 100); + REQUIRE(file_descriptor.get_offset() == 100); REQUIRE(file_descriptor.read(nullptr, 50) == 50); - REQUIRE(file_descriptor.offset() == 150); + REQUIRE(file_descriptor.get_offset() == 150); } THEN("the offset is updated correctly after writes") { REQUIRE(file_descriptor.write(nullptr, 200) == 200); - REQUIRE(file_descriptor.offset() == 200); + REQUIRE(file_descriptor.get_offset() == 200); REQUIRE(file_descriptor.write(nullptr, 25) == 25); - REQUIRE(file_descriptor.offset() == 225); + REQUIRE(file_descriptor.get_offset() == 225); } THEN("reads and writes both update the same offset") { REQUIRE(file_descriptor.read(nullptr, 10) == 10); - REQUIRE(file_descriptor.offset() == 10); + REQUIRE(file_descriptor.get_offset() == 10); REQUIRE(file_descriptor.write(nullptr, 20) == 20); - REQUIRE(file_descriptor.offset() == 30); + REQUIRE(file_descriptor.get_offset() == 30); REQUIRE(file_descriptor.read(nullptr, 5) == 5); - REQUIRE(file_descriptor.offset() == 35); + REQUIRE(file_descriptor.get_offset() == 35); REQUIRE(file_descriptor.write(nullptr, 15) == 15); - REQUIRE(file_descriptor.offset() == 50); + REQUIRE(file_descriptor.get_offset() == 50); } } } @@ -78,14 +82,14 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope auto & vfs = kernel::filesystem::vfs::get(); auto dentry = vfs.open("/information/info_1.txt"); REQUIRE(dentry != nullptr); - auto ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry->get_inode()); + auto ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); THEN("the file can be read and the offset is updated") { kstd::vector<std::byte> buffer(32); auto bytes_read = ofd->read(buffer.data(), buffer.size()); REQUIRE(bytes_read == 7); - REQUIRE(ofd->offset() == 7); + REQUIRE(ofd->get_offset() == 7); std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)}; REQUIRE(buffer_as_str == "info_1\n"); @@ -96,11 +100,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope kstd::vector<std::byte> buffer(4); auto bytes_read_1 = ofd->read(buffer.data(), buffer.size() / 2); REQUIRE(bytes_read_1 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size() / 2); + REQUIRE(ofd->get_offset() == buffer.size() / 2); auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, buffer.size() / 2); REQUIRE(bytes_read_2 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size()); + REQUIRE(ofd->get_offset() == buffer.size()); std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), bytes_read_1 + bytes_read_2}; REQUIRE(buffer_as_str == "info"); diff --git a/kernel/src/filesystem/open_file_table.tests.cpp b/kernel/src/filesystem/open_file_table.tests.cpp index a5c791d..456d6b7 100644 --- a/kernel/src/filesystem/open_file_table.tests.cpp +++ b/kernel/src/filesystem/open_file_table.tests.cpp @@ -1,5 +1,6 @@ #include <kernel/filesystem/open_file_table.hpp> +#include <kernel/filesystem/dentry.hpp> #include <kernel/filesystem/open_file_descriptor.hpp> #include <kernel/test_support/filesystem/inode.hpp> @@ -15,8 +16,10 @@ SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") { auto & table = kernel::filesystem::open_file_table::get(); auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto file_descriptor_1 = kstd::make_shared<kernel::filesystem::open_file_descriptor>(inode); - auto file_descriptor_2 = kstd::make_shared<kernel::filesystem::open_file_descriptor>(inode); + auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); + + auto file_descriptor_1 = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); + auto file_descriptor_2 = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); WHEN("adding the open file descriptor to the open file table") { @@ -69,7 +72,8 @@ SCENARIO("Open file table remove file", "[filesystem][open_file_table]") { auto & table = kernel::filesystem::open_file_table::get(); auto inode = kstd::make_shared<kernel::tests::filesystem::inode>(); - auto file_descriptor = kstd::make_shared<kernel::filesystem::open_file_descriptor>(inode); + auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry"); + auto file_descriptor = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); auto fd = table.add_file(file_descriptor); WHEN("removing the file descriptor using the file descriptor") diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index 6187c3c..d49e237 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -11,18 +11,13 @@ namespace kernel::filesystem::rootfs { auto filesystem::mount(kstd::shared_ptr<kernel::filesystem::inode> const &) -> operation_result { - auto rfs_inode = kstd::make_shared<inode>(); - rfs_inode->add_child("dev"); - m_root_inode = rfs_inode; - + m_root_inode = kstd::make_shared<inode>(); return operation_result::success; } - auto filesystem::lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) + auto filesystem::lookup(kstd::shared_ptr<kernel::filesystem::inode> const &, std::string_view) -> kstd::shared_ptr<kernel::filesystem::inode> { - if (auto * rfs_inode = static_cast<inode *>(parent.get())) - return rfs_inode->lookup_child(name); return nullptr; } } // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/filesystem.tests.cpp b/kernel/src/filesystem/rootfs/filesystem.tests.cpp index 81ac9e4..ae320e9 100644 --- a/kernel/src/filesystem/rootfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.tests.cpp @@ -21,13 +21,6 @@ SCENARIO("Rootfs filesystem mount and lookup", "[filesystem][rootfs][filesystem] REQUIRE(fs.root_inode() != nullptr); } - THEN("looking up the 'dev' directory returns a valid inode") - { - auto dev_inode = fs.lookup(fs.root_inode(), "dev"); - REQUIRE(dev_inode != nullptr); - REQUIRE(dev_inode->is_directory()); - } - THEN("looking up a non-existent directory returns null") { auto non_existent_inode_1 = fs.lookup(fs.root_inode(), ""); diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp index d099676..dbe7948 100644 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -5,10 +5,7 @@ #include <kstd/memory> #include <kstd/string> -#include <algorithm> #include <cstddef> -#include <string_view> -#include <utility> namespace kernel::filesystem::rootfs { @@ -22,17 +19,6 @@ namespace kernel::filesystem::rootfs return 0; } - auto inode::add_child(std::string_view name) -> void - { - m_children.push_back(std::make_pair(kstd::string{name}, kstd::make_shared<inode>())); - } - - auto inode::lookup_child(std::string_view name) -> kstd::shared_ptr<inode> - { - 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; diff --git a/kernel/src/filesystem/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp index 7cc217f..f4b634f 100644 --- a/kernel/src/filesystem/rootfs/inode.tests.cpp +++ b/kernel/src/filesystem/rootfs/inode.tests.cpp @@ -6,53 +6,6 @@ #include <catch2/catch_test_macros.hpp> -SCENARIO("Rootfs inode creation", "[filesystem][rootfs][inode]") -{ - GIVEN("a rootfs inode") - { - auto inode = kernel::filesystem::rootfs::inode{}; - - THEN("the inode has the correct kind") - { - REQUIRE(inode.is_directory()); - REQUIRE_FALSE(inode.is_device()); - REQUIRE_FALSE(inode.is_regular()); - REQUIRE_FALSE(inode.is_symbolic_link()); - } - - THEN("the inode has no children") - { - REQUIRE(inode.lookup_child("child") == nullptr); - } - } -} - -SCENARIO("Rootfs inode child management", "[filesystem][rootfs][inode]") -{ - GIVEN("a rootfs inode") - { - auto inode = kernel::filesystem::rootfs::inode{}; - - WHEN("adding a child inode") - { - inode.add_child("child"); - inode.add_child("another child"); - - THEN("the child can be looked up by name") - { - auto child_inode = inode.lookup_child("child"); - REQUIRE(child_inode != nullptr); - REQUIRE(child_inode->is_directory()); - } - - THEN("looking up a non-existent child returns null") - { - REQUIRE(inode.lookup_child("nonexistent") == nullptr); - } - } - } -} - SCENARIO("Rootfs inode read/write", "[filesystem][rootfs][inode]") { GIVEN("a rootfs inode") diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index f5d57be..8636d0f 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -20,6 +20,7 @@ #include <optional> #include <ranges> #include <string_view> +#include <utility> namespace { @@ -46,20 +47,13 @@ namespace kernel::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_mount = kstd::make_shared<mount>(nullptr, root_fs_root_dentry, root_fs, nullptr); + m_mount_table.add_mount(root_mount); // mount devfs at /dev (inside rootfs, temporary, will be shadowed) auto device_fs = kstd::make_shared<devfs::filesystem>(); device_fs->mount(nullptr); - - if (auto dev_mount_point_dentry = resolve_path("/dev")) - { - do_mount_internal(dev_mount_point_dentry, device_fs); - } - else - { - kapi::system::panic("[FILESYSTEM] failed to resolve /dev for initial devfs mount."); - } + graft_persistent_device_fs(device_fs); // mount boot fs at / (shadows rootfs), re-graft devfs if (auto boot_device_dentry = resolve_path("/dev/ram0")) @@ -68,7 +62,7 @@ namespace kernel::filesystem { if (auto root_dentry = resolve_path("/")) { - do_mount_internal(root_dentry, boot_root_fs); + do_mount_internal(root_dentry, root_mount, boot_root_fs); graft_persistent_device_fs(device_fs); } } @@ -87,7 +81,26 @@ namespace kernel::filesystem auto vfs::open(std::string_view path) -> kstd::shared_ptr<dentry> { - return resolve_path(path); + auto [dentry, mount] = resolve_path_internal(path); + if (!dentry || !mount) + { + return nullptr; + } + mount->increment_ref_count(); + return dentry; + } + + auto vfs::close(std::string_view path) -> operation_result + { + if (auto mount = find_mount(path)) + { + if (mount->decrement_ref_count()) + { + return operation_result::success; + } + return operation_result::close_failed; + } + return operation_result::invalid_path; } auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result @@ -97,13 +110,15 @@ namespace kernel::filesystem return operation_result::invalid_path; } - if (auto mount_point_dentry = resolve_path(target)) + auto [mount_point_dentry, mount_context] = resolve_path_internal(target); + + if (mount_point_dentry && mount_context) { if (auto source_dentry = resolve_path(source)) { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(mount_point_dentry, fs); + do_mount_internal(mount_point_dentry, mount_context, fs); return operation_result::success; } return operation_result::invalid_filesystem; @@ -125,25 +140,18 @@ namespace kernel::filesystem { return operation_result::success; } - - if (remove_result == mount_table::operation_result::has_child_mounts) + else if (remove_result == mount_table::operation_result::mount_not_found) { - return operation_result::unmount_failed; + return operation_result::mount_point_not_found; } - return operation_result::mount_point_not_found; + return operation_result::unmount_failed; } auto vfs::do_mount_internal(kstd::shared_ptr<dentry> const & mount_point_dentry, - kstd::shared_ptr<filesystem> const & fs) -> void + kstd::shared_ptr<mount> const & parent_mount, kstd::shared_ptr<filesystem> const & fs) + -> void { - auto parent_mount_dentry = mount_point_dentry->find_mount_root_dentry(); - kstd::shared_ptr<mount> parent_mount = nullptr; - if (parent_mount_dentry) - { - parent_mount = m_mount_table.find_exact_mount(parent_mount_dentry->get_absolute_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, parent_mount); @@ -152,27 +160,28 @@ namespace kernel::filesystem auto vfs::graft_persistent_device_fs(kstd::shared_ptr<devfs::filesystem> const & device_fs) -> void { - if (auto new_root_dentry = resolve_path("/")) + auto [root_mount_point_dentry, root_mount] = resolve_path_internal("/"); + if (root_mount_point_dentry && root_mount) { - auto dev_dentry = new_root_dentry->find_child("dev"); + auto dev_dentry = root_mount_point_dentry->find_child("dev"); if (!dev_dentry) { - dev_dentry = kstd::make_shared<dentry>(new_root_dentry, device_fs->root_inode(), "dev"); - new_root_dentry->add_child(dev_dentry); + dev_dentry = kstd::make_shared<dentry>(root_mount_point_dentry, device_fs->root_inode(), "dev"); + root_mount_point_dentry->add_child(dev_dentry); } - do_mount_internal(dev_dentry, device_fs); + do_mount_internal(dev_dentry, root_mount, device_fs); } } - auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr<dentry> + auto vfs::resolve_path_internal(std::string_view path) -> std::pair<kstd::shared_ptr<dentry>, kstd::shared_ptr<mount>> { if (!path::is_valid_absolute_path(path)) { - return nullptr; + return {nullptr, nullptr}; } - auto current_mount = m_mount_table.find_exact_mount("/"); + auto current_mount = m_mount_table.find_mount("/"); if (!current_mount) { kapi::system::panic("[FILESYSTEM] no root mount found."); @@ -225,7 +234,7 @@ namespace kernel::filesystem auto found_inode = current_fs->lookup(current_dentry->get_inode(), part.view()); if (!found_inode) { - return nullptr; + return {nullptr, nullptr}; } next_dentry = kstd::make_shared<dentry>(current_dentry, found_inode, part.view()); @@ -233,7 +242,7 @@ namespace kernel::filesystem } else if (next_dentry->has_flag(dentry::dentry_flags::is_mount_point)) { - current_mount = m_mount_table.find_exact_mount(next_dentry->get_absolute_path().view()); + current_mount = m_mount_table.find_mount(next_dentry->get_absolute_path().view()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); @@ -246,7 +255,7 @@ namespace kernel::filesystem { if (symlink_counter++ > constants::symloop_max) { - return nullptr; + return {nullptr, nullptr}; } kstd::vector<uint8_t> buffer(constants::symlink_max_path_length); @@ -261,7 +270,7 @@ namespace kernel::filesystem if (path::is_valid_absolute_path(symbolic_link_path)) { - current_mount = m_mount_table.find_exact_mount("/"); + current_mount = m_mount_table.find_mount("/"); current_dentry = current_mount->get_root_dentry(); } continue; @@ -269,9 +278,19 @@ namespace kernel::filesystem current_dentry = next_dentry; } + return {current_dentry, current_mount}; + } - return current_dentry; + auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr<dentry> + { + return resolve_path_internal(path).first; } + + auto vfs::find_mount(std::string_view path) -> kstd::shared_ptr<mount> + { + return resolve_path_internal(path).second; + } + } // namespace kernel::filesystem namespace kernel::tests::filesystem::vfs diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 0f1d6d5..648ebb8 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -105,6 +105,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1 != nullptr); + REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); @@ -126,12 +128,47 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(mounted_monkey_1 != nullptr); REQUIRE(mounted_fish1 != nullptr); + REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_fish1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); } + THEN("image can be mounted, unmount only if no files are open") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + + auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); + REQUIRE(mounted_monkey_1 != nullptr); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); + + REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + } + + THEN("file with invalid path or not opened file cannot be closed") + { + REQUIRE(vfs.close("invalid_path") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.close("/information/info_1.txt") == kernel::filesystem::vfs::operation_result::close_failed); + } + + THEN("file cannot be closed twice") + { + auto info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::close_failed); + } + THEN("images can be stacked mounted and correct file system is unmounted again") { REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); @@ -140,6 +177,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets != nullptr); + REQUIRE(vfs.close(mounted_tickets->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets == nullptr); @@ -161,6 +201,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); + REQUIRE(vfs.close(water->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); info_1 = vfs.open("/information/info_1.txt"); @@ -180,6 +222,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); + REQUIRE(vfs.close(water->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + auto dev_ram_16 = vfs.open("/dev/ram16"); REQUIRE(dev_ram_16 == nullptr); @@ -196,6 +240,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); + REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -310,13 +356,15 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto dentry = vfs.open("/information/sheep_1.txt"); REQUIRE(dentry != nullptr); - auto sheep_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry->get_inode()); + auto sheep_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry); kstd::vector<std::byte> buffer(7); auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); + REQUIRE(vfs.close(dentry->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(unmounted_sheep_1 == nullptr); @@ -335,8 +383,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(sheep_1 != nullptr); REQUIRE(goat_1 != nullptr); - auto sheep_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(sheep_1->get_inode()); - auto goat_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(goat_1->get_inode()); + auto sheep_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(sheep_1); + auto goat_1_ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(goat_1); kstd::vector<std::byte> sheep_buffer(7); auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); @@ -348,6 +396,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS buffer_as_str = std::string_view{reinterpret_cast<char *>(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); + REQUIRE(vfs.close(sheep_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(goat_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/stable") == kernel::filesystem::vfs::operation_result::success); @@ -357,6 +408,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); + REQUIRE(vfs.close(still_mounted_sheep_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(unmounted_sheep_1 == nullptr); |
