#include "kernel/filesystem/vfs.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" #include #include #include #include #include #include SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with dummy modules", "[filesystem][vfs]") { GIVEN("an initialized boot module registry with multiple modules") { REQUIRE_NOTHROW(setup_modules_and_init_vfs(5)); THEN("vfs initializes and provides /dev mount") { auto & vfs = kernel::filesystem::vfs::get(); auto dev = vfs.open("/dev"); REQUIRE(dev != nullptr); } THEN("vfs initializes root filesystem with boot device if boot module is present") { auto & vfs = kernel::filesystem::vfs::get(); auto root_file = vfs.open("/"); REQUIRE(root_file != nullptr); } } } SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with file backed image", "[filesystem][vfs][img]") { auto const image_path_1 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; auto const image_path_2 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_2KB_fs.img"; auto const image_path_3 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_4KB_fs.img"; GIVEN("a real image file") { REQUIRE(std::filesystem::exists(image_path_1)); REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path_1})); THEN("vfs initializes and provides expected mount points") { auto & vfs = kernel::filesystem::vfs::get(); auto root = vfs.open("/"); auto dev = vfs.open("/dev"); auto information = vfs.open("/information/info_1.txt"); REQUIRE(root != nullptr); REQUIRE(dev != nullptr); REQUIRE(information != nullptr); } } GIVEN("three real image files") { REQUIRE(std::filesystem::exists(image_path_1)); REQUIRE(std::filesystem::exists(image_path_2)); REQUIRE(std::filesystem::exists(image_path_3)); REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2", "test_img_module_3"}, {image_path_1, image_path_2, image_path_3})); auto & vfs = kernel::filesystem::vfs::get(); THEN("vfs initializes first module as root") { auto & vfs = kernel::filesystem::vfs::get(); auto info1 = vfs.open("/information/info_1.txt"); auto info2 = vfs.open("/information/info_2.txt"); REQUIRE(info1 != nullptr); REQUIRE(info2 != nullptr); } THEN("second image can be mounted, data retrieved and unmounted again") { REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1 != nullptr); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(unmounted_monkey_1 == nullptr); auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); } THEN("third image can be mounted in a mounted file system, unmount only if no child mount exists") { REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.do_mount("/dev/ram32", "/information/monkey_house/infrastructure") == kernel::filesystem::vfs::operation_result::success); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); auto mounted_fish1 = vfs.open("/information/monkey_house/infrastructure/enclosures/aquarium/tank_1/fish_1.txt"); REQUIRE(mounted_monkey_1 != nullptr); REQUIRE(mounted_fish1 != nullptr); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); } THEN("images can be stacked mounted and correct file system is unmounted again") { REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.do_mount("/dev/ram32", "/information") == kernel::filesystem::vfs::operation_result::success); auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets != nullptr); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets == nullptr); auto mounted_monkey = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey != nullptr); } THEN("mount with null file system fails") { REQUIRE(vfs.do_mount("/closed.txt", "/information") == kernel::filesystem::vfs::operation_result::invalid_filesystem); } THEN("mount with invalid path fails") { REQUIRE(vfs.do_mount("/dev/ram16", "") == kernel::filesystem::vfs::operation_result::invalid_path); REQUIRE(vfs.do_mount("/dev/ram16", "information") == kernel::filesystem::vfs::operation_result::invalid_path); REQUIRE(vfs.do_mount("/dev/ram16", "/information/") == kernel::filesystem::vfs::operation_result::invalid_path); } THEN("mount with non-existent source path fails") { REQUIRE(vfs.do_mount("/dev/nonexistent", "/information") == kernel::filesystem::vfs::operation_result::non_existent_path); } THEN("mount with non-existent mount point fails") { REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent") == kernel::filesystem::vfs::operation_result::mount_point_not_found); } THEN("unmount with invalid path fails") { REQUIRE(vfs.unmount("") == kernel::filesystem::vfs::operation_result::invalid_path); REQUIRE(vfs.unmount("information") == kernel::filesystem::vfs::operation_result::invalid_path); REQUIRE(vfs.unmount("/information/") == kernel::filesystem::vfs::operation_result::invalid_path); } THEN("unmounting non-existent mount point returns expected error code") { REQUIRE(vfs.unmount("/information/nonexistent") == kernel::filesystem::vfs::operation_result::mount_point_not_found); } } GIVEN("A real image file containing as filesystem formatted files") { REQUIRE(std::filesystem::exists(image_path_1)); REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); THEN("the file-filesystem in the image can be mounted, files can be read and unmounted again") { auto & vfs = kernel::filesystem::vfs::get(); REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 == nullptr); auto dentry = vfs.open("/information/sheep_1.txt"); REQUIRE(dentry != nullptr); auto sheep_1_ofd = kstd::make_shared(dentry->get_inode()); kstd::vector buffer(7); auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(unmounted_sheep_1 == nullptr); } THEN("the file-filesystem in the image can be mounted and in this filesystem can another file-filesystem be " "mounted, files can be read and unmounted again") { auto & vfs = kernel::filesystem::vfs::get(); REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.do_mount("/archiv/2025.img", "/information/stable") == kernel::filesystem::vfs::operation_result::success); auto sheep_1 = vfs.open("/information/sheep_1.txt"); auto goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); REQUIRE(sheep_1 != nullptr); REQUIRE(goat_1 != nullptr); auto sheep_1_ofd = kstd::make_shared(sheep_1->get_inode()); auto goat_1_ofd = kstd::make_shared(goat_1->get_inode()); kstd::vector sheep_buffer(7); auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); std::string_view buffer_as_str{reinterpret_cast(sheep_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); kstd::vector goat_buffer(6); bytes_read = goat_1_ofd->read(goat_buffer.data(), goat_buffer.size()); buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/stable") == kernel::filesystem::vfs::operation_result::success); auto unmounted_goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); REQUIRE(unmounted_goat_1 == nullptr); auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(unmounted_sheep_1 == nullptr); } } }