aboutsummaryrefslogtreecommitdiff
path: root/kernel/src/filesystem/ext2/inode.tests.cpp
diff options
context:
space:
mode:
authorMarcel Braun <marcel.braun@ost.ch>2026-05-13 11:27:49 +0200
committerMarcel Braun <marcel.braun@ost.ch>2026-05-13 11:27:49 +0200
commit9b8aa22868a510ac15463e7d4376a506df2db110 (patch)
treeee783d6c71963054835f04767eff93ce0e9c1337 /kernel/src/filesystem/ext2/inode.tests.cpp
parent9d77ac6e5ae36be07b80d49080d017b19acfa02a (diff)
parent15afa6a030ee6e1fc6c255f9567b54d78c530d25 (diff)
downloadkernel-9b8aa22868a510ac15463e7d4376a506df2db110.tar.xz
kernel-9b8aa22868a510ac15463e7d4376a506df2db110.zip
Merge branch 'ext2-sparse-files' into 'develop-BA-FS26'
Ext2 sparse files See merge request teachos/kernel!35
Diffstat (limited to 'kernel/src/filesystem/ext2/inode.tests.cpp')
-rw-r--r--kernel/src/filesystem/ext2/inode.tests.cpp101
1 files changed, 93 insertions, 8 deletions
diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp
index 783d930..45bea51 100644
--- a/kernel/src/filesystem/ext2/inode.tests.cpp
+++ b/kernel/src/filesystem/ext2/inode.tests.cpp
@@ -15,6 +15,7 @@
#include <catch2/catch_test_macros.hpp>
+#include <algorithm>
#include <cstddef>
#include <filesystem>
#include <string_view>
@@ -126,10 +127,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in
}
}
-SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesystem][ext2][inode]")
+SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesystem][ext2][inode]")
{
auto const block_size = 1024uz;
- GIVEN("an ext2 inode without mapped data blocks")
+ GIVEN("an ext2 inode with only direct mapped data blocks")
{
auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "mock", block_size, 64 * block_size);
REQUIRE(device != nullptr);
@@ -141,16 +142,100 @@ SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesyst
REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success);
auto data = kernel::filesystem::ext2::inode_data{};
- data.blocks = 2;
- data.block[0] = 0;
+ data.block[0] = 30;
+ data.block[1] = 0;
+ data.block[2] = 31;
+ data.size = block_size * 3;
+
+ kernel::tests::filesystem::ext2::write_bytes(*device, 30 * block_size, "Hello", 5);
+ kernel::tests::filesystem::ext2::write_bytes(*device, 31 * block_size, "World!", 6);
+
+ auto inode = kernel::filesystem::ext2::inode{&fs, data};
+
+ auto buffer = kstd::vector<std::byte>(data.size, std::byte{0xAB});
+
+ THEN("correct number of bytes are read and holes are returned as zeros")
+ {
+ auto const bytes_read = inode.read(buffer.data(), 0, buffer.size());
+ REQUIRE(bytes_read == data.size);
+
+ auto const text = std::string_view{reinterpret_cast<char const *>(buffer.data()), bytes_read};
+ REQUIRE(text.substr(0, 5) == "Hello");
+ REQUIRE(std::ranges::all_of(text.substr(5, block_size - 5), [](char c) { return c == '\0'; }));
+ REQUIRE(text.substr(2 * block_size, 6) == "World!");
+ REQUIRE(std::ranges::all_of(text.substr(2 * block_size + 6, 3 * block_size), [](char c) { return c == '\0'; }));
+ }
+ }
+
+ GIVEN("an ext2 indode with file holes in singly indirect blocks")
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "mock", block_size, 64 * block_size);
+ REQUIRE(device != nullptr);
+ kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device);
+
+ auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
+
+ auto fs = kernel::filesystem::ext2::filesystem{};
+ REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success);
+
+ auto data = kernel::filesystem::ext2::inode_data{};
+ data.block[0] = 30;
+ data.block[12] = 31;
+ data.size = block_size * 15;
+
+ kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size, 50);
+ kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size + 4, 0);
+ kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size + 8, 51);
+
+ kernel::tests::filesystem::ext2::write_bytes(*device, 30 * block_size, "Hello", 5);
+ kernel::tests::filesystem::ext2::write_bytes(*device, 50 * block_size, "Blub", 4);
+ kernel::tests::filesystem::ext2::write_bytes(*device, 51 * block_size, "World!", 6);
+
+ auto inode = kernel::filesystem::ext2::inode{&fs, data};
+
+ auto buffer = kstd::vector<std::byte>(data.size, std::byte{0xAB});
+
+ THEN("correct number of bytes are read and holes are returned as zeros")
+ {
+ auto const bytes_read = inode.read(buffer.data(), 0, buffer.size());
+ REQUIRE(bytes_read == data.size);
+
+ auto const text = std::string_view{reinterpret_cast<char const *>(buffer.data()), bytes_read};
+ REQUIRE(text.substr(0, 5) == "Hello");
+ REQUIRE(std::ranges::all_of(text.substr(5, 12 * block_size - 5), [](char c) { return c == '\0'; }));
+ REQUIRE(text.substr(12 * block_size, 4) == "Blub");
+ REQUIRE(
+ std::ranges::all_of(text.substr(12 * block_size + 4, 2 * block_size - 4), [](char c) { return c == '\0'; }));
+ REQUIRE(text.substr(14 * block_size, 6) == "World!");
+ REQUIRE(
+ std::ranges::all_of(text.substr(14 * block_size + 6, 1 * block_size - 6), [](char c) { return c == '\0'; }));
+ }
+ }
+
+ GIVEN("an ext2 inode with zero singly indirect block pointer")
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "mock", block_size, 64 * block_size);
+ REQUIRE(device != nullptr);
+ kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device);
+
+ auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
+
+ auto fs = kernel::filesystem::ext2::filesystem{};
+ REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success);
+
+ auto data = kernel::filesystem::ext2::inode_data{};
+ data.block[12] = 0;
+ data.size = block_size * 15;
+
auto inode = kernel::filesystem::ext2::inode{&fs, data};
- auto buffer = kstd::vector<std::byte>(32, std::byte{0xAB});
+ auto buffer = kstd::vector<std::byte>(block_size * 15, std::byte{0xAB});
- THEN("no bytes are read")
+ THEN("all direct blocks are zero when singly indirect block pointer is zero")
{
auto const bytes_read = inode.read(buffer.data(), 0, buffer.size());
- REQUIRE(bytes_read == 0);
+ REQUIRE(bytes_read == buffer.size());
+ REQUIRE(std::ranges::all_of(buffer, [](std::byte c) { return c == std::byte{0x00}; }));
}
}
}
@@ -170,7 +255,7 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]")
REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success);
auto inode_data = kernel::filesystem::ext2::inode_data{};
- inode_data.blocks = 2;
+ inode_data.size = block_size * 2;
inode_data.block[0] = 20;
kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6);
inode_data.block[1] = 21;