#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/test_support/devices/block_device.hpp" #include "kernel/test_support/filesystem/ext2.hpp" #include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" #include #include #include #include #include #include SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 filesystem mount and lookup with real image", "[filesystem][ext2][filesystem][img]") { auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; GIVEN("a mounted ext2 filesystem from a real image") { REQUIRE(std::filesystem::exists(image_path)); REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path})); auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); REQUIRE(boot_device != nullptr); auto fs = kernel::filesystem::ext2::filesystem{}; REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success); THEN("the root inode is available and is a directory") { REQUIRE(fs.root_inode() != nullptr); REQUIRE(fs.root_inode()->is_directory()); } THEN("lookup resolves known entries from the image") { auto information = fs.lookup(fs.root_inode(), "information"); REQUIRE(information != nullptr); REQUIRE(information->is_directory()); auto info_1 = fs.lookup(information, "info_1.txt"); REQUIRE(info_1 != nullptr); REQUIRE(info_1->is_regular()); } THEN("lookup returns null for invalid inputs") { REQUIRE(fs.lookup(nullptr, "information") == nullptr); auto information = fs.lookup(fs.root_inode(), "information"); REQUIRE(information != nullptr); auto info_1 = fs.lookup(information, "info_1.txt"); REQUIRE(info_1 != nullptr); REQUIRE(fs.lookup(info_1, "anything") == nullptr); REQUIRE(fs.lookup(fs.root_inode(), "does_not_exist") == nullptr); } } } SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem]") { auto const block_size = 1024; GIVEN("a block device that does not contain an ext2 superblock") { auto device = kstd::make_shared(0, 0, "mock", block_size, 2 * block_size); REQUIRE(device != nullptr); auto fs = kernel::filesystem::ext2::filesystem{}; THEN("mount fails with invalid_magic_number") { REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::invalid_magic_number); } } } SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesystem][ext2][filesystem]") { auto const block_size = 1024; GIVEN("a minimally valid ext2 layout with configured indirect block tables") { auto device = kstd::make_shared(0, 0, "mock", block_size, 128 * block_size); REQUIRE(device != nullptr); kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); auto fs = kernel::filesystem::ext2::filesystem{}; REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); auto inode_data = kernel::filesystem::ext2::inode_data{}; inode_data.block[0] = 7; inode_data.block[12] = 30; kernel::tests::filesystem::ext2::write_u32(*device, 30 * block_size, 31); inode_data.block[13] = 40; kernel::tests::filesystem::ext2::write_u32(*device, 40 * block_size, 41); kernel::tests::filesystem::ext2::write_u32(*device, 41 * block_size, 42); inode_data.block[14] = 50; kernel::tests::filesystem::ext2::write_u32(*device, 50 * block_size, 51); kernel::tests::filesystem::ext2::write_u32(*device, 51 * block_size, 52); kernel::tests::filesystem::ext2::write_u32(*device, 52 * block_size, 53); auto const numbers_per_block = static_cast(block_size / sizeof(uint32_t)); auto const singly_start = static_cast(kernel::filesystem::ext2::constants::direct_block_count); auto const doubly_start = singly_start + numbers_per_block; auto const triply_start = doubly_start + numbers_per_block * numbers_per_block; THEN("mapping resolves direct, singly, doubly and triply indirect indexes") { REQUIRE(fs.map_inode_block_index_to_global_block_number(0, inode_data) == 7); REQUIRE(fs.map_inode_block_index_to_global_block_number(singly_start, inode_data) == 31); REQUIRE(fs.map_inode_block_index_to_global_block_number(doubly_start, inode_data) == 42); REQUIRE(fs.map_inode_block_index_to_global_block_number(triply_start, inode_data) == 53); } THEN("mapping returns zero for out-of-range indexes") { auto const beyond_triply = triply_start + numbers_per_block * numbers_per_block * numbers_per_block; REQUIRE(fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data) == 0); } } }