diff options
Diffstat (limited to 'kernel/kernel/filesystems/fat32/file_allocation_table.tests.cpp')
| -rw-r--r-- | kernel/kernel/filesystems/fat32/file_allocation_table.tests.cpp | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/kernel/kernel/filesystems/fat32/file_allocation_table.tests.cpp b/kernel/kernel/filesystems/fat32/file_allocation_table.tests.cpp index bcc85a51..1b79d2bf 100644 --- a/kernel/kernel/filesystems/fat32/file_allocation_table.tests.cpp +++ b/kernel/kernel/filesystems/fat32/file_allocation_table.tests.cpp @@ -1,6 +1,7 @@ #include <kernel/filesystems/fat32/file_allocation_table.hpp> #include <kernel/devices/storage.hpp> +#include <kernel/filesystems/fat32/directory_entry.hpp> #include <kernel/filesystems/fat32/driver_state.hpp> #include <kernel/filesystems/fat32/error.hpp> #include <kernel/filesystems/fat32/filesystem.hpp> @@ -10,14 +11,20 @@ #include <kernel/vfs/device_inode.hpp> #include <kstd/memory.hpp> +#include <kstd/span.hpp> #include <kstd/units.hpp> #include <kstd/vector.hpp> #include <catch2/catch_test_macros.hpp> +#include <algorithm> +#include <array> +#include <cstddef> #include <cstdint> #include <filesystem> #include <optional> +#include <span> +#include <string_view> using namespace kstd::units_literals; @@ -70,6 +77,40 @@ SCENARIO_METHOD(kernel::tests::vfs::storage_boot_module_fixture, "Fat32 cluster REQUIRE(kernel::filesystems::fat32::next_cluster(100, *driver_state).error() == kernel::filesystems::fat32::errc::corrupted_cluster_chain); } + + THEN("the root directory entries can be read from its cluster chain") + { + using namespace kernel::filesystems::fat32; + + auto entries = std::array<directory_entry, 3>{}; + auto const buffer = std::as_writable_bytes(std::span{entries}); + auto const read_result = read_cluster_chain(driver_state->boot_sector.root_cluster, buffer, 0_B, *driver_state); + REQUIRE(read_result); + REQUIRE(*read_result == kstd::bytes{buffer.size()}); + + auto const & volume_label = entries[0]; + REQUIRE(std::string_view{volume_label.name.data(), volume_label.name.size()} == "TESTFAT "); + REQUIRE(volume_label.attributes == constants::attribute_volume_id); + + auto const & hello = entries[1]; + REQUIRE(std::string_view{hello.name.data(), hello.name.size()} == "HELLO TXT"); + REQUIRE(hello.attributes == constants::attribute_archive); + REQUIRE(hello.first_cluster_high == 0); + REQUIRE(hello.first_cluster_low == 3); + REQUIRE(hello.file_size == 12); + + REQUIRE(static_cast<std::uint8_t>(entries[2].name[0]) == constants::end_of_directory_marker); + } + + THEN("the contents of HELLO.TXT can be read from its cluster chain") + { + auto contents = std::array<char, 12>{}; + auto const read_result = kernel::filesystems::fat32::read_cluster_chain( + 3, std::as_writable_bytes(std::span{contents}), 0_B, *driver_state); + REQUIRE(read_result); + REQUIRE(*read_result == 12_B); + REQUIRE(std::string_view{contents.data(), contents.size()} == "Hello World!"); + } } } @@ -199,4 +240,123 @@ SCENARIO("Fat32 cluster chain traversal", "[filesystem][fat32][fat]") } } +SCENARIO("Fat32 cluster chain reading", "[filesystem][fat32][fat]") +{ + auto const block_size = 512_B; + + GIVEN("a mounted minimal fat32 layout with a three cluster chain") + { + auto device = kstd::make_shared<kernel::tests::devices::block_device>("mock", block_size, 18 * block_size); + kernel::tests::filesystems::fat32::setup_mock_fat32_layout(*device); + + using kernel::filesystems::fat32::errc; + using kernel::filesystems::fat32::read_cluster_chain; + using kernel::tests::filesystems::fat32::write_cluster_data; + using kernel::tests::filesystems::fat32::write_fat_entry; + + auto cluster_data = std::array<std::byte, 512>{}; + for (auto const & [cluster, fill] : {std::pair{3u, 'A'}, std::pair{4u, 'B'}, std::pair{5u, 'C'}}) + { + std::ranges::fill(cluster_data, static_cast<std::byte>(fill)); + write_cluster_data(*device, cluster, cluster_data); + } + + write_fat_entry(*device, 3, 4); + write_fat_entry(*device, 4, 5); + write_fat_entry(*device, 5, 0x0FFF'FFF8); + + auto dev_inode = kstd::make_shared<kernel::vfs::device_inode>(device); + auto fs = kstd::make_shared<kernel::filesystems::fat32::filesystem>(); + auto mount = fs->mount(dev_inode); + REQUIRE(mount); + auto driver_state = static_pointer_cast<kernel::filesystems::fat32::driver_state>(mount->second); + REQUIRE(driver_state); + + THEN("the whole chain is read in order") + { + auto buffer = std::array<std::byte, 3 * 512>{}; + auto const read_result = read_cluster_chain(3, buffer, 0_B, *driver_state); + REQUIRE(read_result); + REQUIRE(*read_result == 1536_B); + REQUIRE(buffer[0] == std::byte{'A'}); + REQUIRE(buffer[511] == std::byte{'A'}); + REQUIRE(buffer[512] == std::byte{'B'}); + REQUIRE(buffer[1024] == std::byte{'C'}); + REQUIRE(buffer[1535] == std::byte{'C'}); + } + + THEN("a read spanning a cluster boundary continues in the next cluster") + { + auto buffer = std::array<std::byte, 24>{}; + auto const read_result = read_cluster_chain(3, buffer, 500_B, *driver_state); + REQUIRE(read_result); + REQUIRE(*read_result == 24_B); + REQUIRE(std::ranges::count(buffer, std::byte{'A'}) == 12); + REQUIRE(std::ranges::count(buffer, std::byte{'B'}) == 12); + REQUIRE(buffer[11] == std::byte{'A'}); + REQUIRE(buffer[12] == std::byte{'B'}); + } + + THEN("an offset beyond the first cluster skips the preceding clusters") + { + auto buffer = std::array<std::byte, 10>{}; + auto const read_result = read_cluster_chain(3, buffer, 1100_B, *driver_state); + REQUIRE(read_result); + REQUIRE(*read_result == 10_B); + REQUIRE(std::ranges::count(buffer, std::byte{'C'}) == 10); + } + + THEN("a read is truncated at the end of the chain") + { + auto buffer = std::array<std::byte, 4 * 512>{}; + auto const read_result = read_cluster_chain(3, buffer, 0_B, *driver_state); + REQUIRE(read_result); + REQUIRE(*read_result == 1536_B); + + auto const tail_result = read_cluster_chain(3, buffer, 1500_B, *driver_state); + REQUIRE(tail_result); + REQUIRE(*tail_result == 36_B); + } + + THEN("reading at or past the end of the chain reads nothing") + { + auto buffer = std::array<std::byte, 16>{}; + REQUIRE(read_cluster_chain(3, buffer, 1536_B, *driver_state) == 0_B); + REQUIRE(read_cluster_chain(3, buffer, 5000_B, *driver_state) == 0_B); + } + + THEN("an empty buffer reads nothing") + { + REQUIRE(read_cluster_chain(3, std::span<std::byte>{}, 0_B, *driver_state) == 0_B); + } + + THEN("a chain cannot start outside the data region") + { + auto buffer = std::array<std::byte, 16>{}; + REQUIRE(read_cluster_chain(0, buffer, 0_B, *driver_state).error() == errc::invalid_cluster); + REQUIRE(read_cluster_chain(18, buffer, 0_B, *driver_state).error() == errc::invalid_cluster); + } + + THEN("a chain running into a free cluster is corrupted") + { + write_fat_entry(*device, 8, 9); + write_fat_entry(*device, 9, 0); + + auto buffer = std::array<std::byte, 3 * 512>{}; + REQUIRE(read_cluster_chain(8, buffer, 0_B, *driver_state).error() == errc::corrupted_cluster_chain); + } + + THEN("a cyclic chain is detected while reading and while skipping") + { + write_fat_entry(*device, 13, 14); + write_fat_entry(*device, 14, 13); + + auto buffer = std::array<std::byte, 17 * 512>{}; + REQUIRE(read_cluster_chain(13, buffer, 0_B, *driver_state).error() == errc::corrupted_cluster_chain); + REQUIRE(read_cluster_chain(13, std::span{buffer}.first(16), 20 * 512_B, *driver_state).error() == + errc::corrupted_cluster_chain); + } + } +} + // NOLINTEND(readability-magic-numbers) |
