From c26d681e88905726d6521b4fbfaeb63603db8faf Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 30 May 2026 22:04:08 +0200 Subject: add inode write_global_block_number_to_inode_block_index tests --- kernel/kernel/filesystem/ext2/filesystem.cpp | 108 +++++++++++++++++++++ kernel/kernel/filesystem/ext2/filesystem.hpp | 5 +- kernel/kernel/filesystem/ext2/filesystem.tests.cpp | 90 +++++++++++++++++ kernel/kernel/filesystem/ext2/inode.cpp | 7 +- kernel/kernel/filesystem/ext2/inode.tests.cpp | 18 ++-- 5 files changed, 216 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 982505df..3aa07945 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -350,6 +351,113 @@ namespace kernel::filesystem::ext2 return block_number_buffer; } + auto filesystem::write_global_block_number_to_inode_block_index(size_t inode_block_index, inode_data & data, + uint32_t global_block_number) -> kstd::result + { + if (inode_block_index < constants::direct_block_count) + { + data.block[inode_block_index] = global_block_number; + return kstd::success(); + } + inode_block_index -= constants::direct_block_count; + + auto const levels = indirect_levels(); + for (auto const & level : levels) + { + if (inode_block_index >= level.capacity) + { + inode_block_index -= level.capacity; + continue; + } + + auto block_number = data.block[level.slot_index]; + auto parent_byte_offset = 0uz; + bool is_root = true; + + for (auto stride = level.capacity / block_numbers_per_block();; stride /= block_numbers_per_block()) + { + auto const idx = inode_block_index / stride; + inode_block_index %= stride; + + if (idx == 0 && inode_block_index == 0) + { + auto allocated = allocate_blocks(1); + if (!allocated) + { + return kstd::failure(allocated.error()); + } + + block_number = allocated->front(); + + if (is_root) + { + data.block[level.slot_index] = block_number; + } + else + { + if (auto write_result = m_backing_inode->write(&block_number, parent_byte_offset, sizeof(uint32_t)); + !write_result) + { + return kstd::failure(write_result.error()); + } + } + } + + auto const byte_offset = block_number * block_size() + idx * sizeof(uint32_t); + + if (stride == 1) + { + if (auto write_result = m_backing_inode->write(&global_block_number, byte_offset, sizeof(uint32_t)); + !write_result) + { + return kstd::failure(write_result.error()); + } + return kstd::success(); + } + + parent_byte_offset = byte_offset; + is_root = false; + if (auto read_result = read_block_number_at_index(block_number, idx); !read_result) + { + return kstd::failure(read_result.error()); + } + else + { + block_number = read_result.value(); + } + } + } + + return kstd::success(); + } + + auto filesystem::ensure_and_get_block(uint32_t block_number) -> std::optional + { + if (block_number != 0) + { + return block_number; + } + + if (auto allocated = allocate_blocks(1)) + { + return allocated->front(); + } + + return std::nullopt; + } + + auto filesystem::read_block(uint32_t block_number, void * buffer) const -> kstd::result + { + auto const block_offset = static_cast(block_number) * block_size(); + return m_backing_inode->read(buffer, block_offset, block_size()); + } + + auto filesystem::write_block(uint32_t block_number, void const * buffer) -> kstd::result + { + auto const block_offset = static_cast(block_number) * block_size(); + return m_backing_inode->write(buffer, block_offset, block_size()); + } + auto filesystem::block_numbers_per_block() const -> size_t { return block_size() / sizeof(uint32_t); diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index f440d4dc..c6320ee2 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace kernel::filesystem::ext2 @@ -101,7 +102,7 @@ namespace kernel::filesystem::ext2 -> kstd::result; auto write_global_block_number_to_inode_block_index(size_t block_index, inode_data & data, - uint32_t global_block_number) -> void; + uint32_t global_block_number) -> kstd::result; private: struct indirect_level @@ -115,7 +116,7 @@ namespace kernel::filesystem::ext2 [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::result>; [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> kstd::result; - auto ensure_and_get_block(uint32_t & block_number) -> bool; + auto ensure_and_get_block(uint32_t block_number) -> std::optional; [[nodiscard]] auto block_group_descriptor_table_offset() const -> size_t; diff --git a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp index 1702526e..62fc14c1 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -15,8 +17,11 @@ #include #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]") @@ -138,3 +143,88 @@ SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesys } } } + +SCENARIO("Ext2 block writing includes direct and all indirect levels", "[filesystem][ext2][filesystem]") +{ + auto const block_size = 1024; + + GIVEN("a mounted ext2 filesystem with free blocks available for indirect table allocation") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 256 * block_size); + REQUIRE(device != nullptr); + + auto superblock = kernel::filesystem::ext2::superblock{}; + superblock.magic = kernel::filesystem::ext2::constants::magic_number; + superblock.log_block_size = 0; + superblock.blocks_count = 256; + superblock.free_blocks_count = 128; + superblock.blocks_per_group = 256; + superblock.inodes_per_group = 32; + superblock.rev_level = kernel::filesystem::ext2::constants::dynamic_revision; + superblock.inode_size = 128; + + auto block_group_descriptor = kernel::filesystem::ext2::block_group_descriptor{}; + block_group_descriptor.block_bitmap = 255; + block_group_descriptor.inode_table = 5; + block_group_descriptor.free_blocks_count = 128; + + auto block_bitmap = std::vector(block_size, std::byte{0x00}); + block_bitmap[0] = std::byte{0b1111'1111}; + block_bitmap[1] = std::byte{0b1111'1111}; + + auto dev_inode = kstd::make_shared(device); + REQUIRE(dev_inode->write(block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size)); + + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device, superblock, block_group_descriptor); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(dev_inode)); + + auto read_u32 = [&](size_t offset) -> uint32_t { + auto value = uint32_t{0}; + std::memcpy(&value, device->data.data() + offset, sizeof(value)); + return value; + }; + + auto inode_data = kernel::filesystem::ext2::inode_data{}; + + 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("writing resolves direct, singly, doubly and triply indirect indexes") + { + constexpr auto direct_global_block_number = 101; + constexpr auto singly_global_block_number = 202; + constexpr auto doubly_global_block_number = 303; + constexpr auto triply_global_block_number = 404; + + REQUIRE(fs.write_global_block_number_to_inode_block_index(0, inode_data, direct_global_block_number)); + REQUIRE(inode_data.block[0] == direct_global_block_number); + + REQUIRE(fs.write_global_block_number_to_inode_block_index(singly_start, inode_data, singly_global_block_number)); + REQUIRE(inode_data.block[12] == 16); + REQUIRE(read_u32(static_cast(inode_data.block[12]) * block_size) == singly_global_block_number); + + REQUIRE(fs.write_global_block_number_to_inode_block_index(doubly_start, inode_data, doubly_global_block_number)); + REQUIRE(inode_data.block[13] == 17); + auto const doubly_leaf_table = read_u32(static_cast(inode_data.block[13]) * block_size); + REQUIRE(doubly_leaf_table == 18); + REQUIRE(read_u32(static_cast(doubly_leaf_table) * block_size) == doubly_global_block_number); + + REQUIRE(fs.write_global_block_number_to_inode_block_index(triply_start, inode_data, triply_global_block_number)); + REQUIRE(inode_data.block[14] == 19); + auto const triply_middle_table = read_u32(static_cast(inode_data.block[14]) * block_size); + REQUIRE(triply_middle_table == 20); + auto const triply_leaf_table = read_u32(static_cast(triply_middle_table) * block_size); + REQUIRE(triply_leaf_table == 21); + REQUIRE(read_u32(static_cast(triply_leaf_table) * block_size) == triply_global_block_number); + + REQUIRE(fs.map_inode_block_index_to_global_block_number(0, inode_data) == direct_global_block_number); + REQUIRE(fs.map_inode_block_index_to_global_block_number(singly_start, inode_data) == singly_global_block_number); + REQUIRE(fs.map_inode_block_index_to_global_block_number(doubly_start, inode_data) == doubly_global_block_number); + REQUIRE(fs.map_inode_block_index_to_global_block_number(triply_start, inode_data) == triply_global_block_number); + } + } +} diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp index 83db6ec6..f95ddaad 100644 --- a/kernel/kernel/filesystem/ext2/inode.cpp +++ b/kernel/kernel/filesystem/ext2/inode.cpp @@ -159,7 +159,12 @@ namespace kernel::filesystem::ext2 auto const block_index = m_filesystem->inode_block_count(m_data) + i; auto const global_block_number = new_blocks->at(i); - m_filesystem->write_global_block_number_to_inode_block_index(block_index, m_data, global_block_number); + if (auto write_result = + m_filesystem->write_global_block_number_to_inode_block_index(block_index, m_data, global_block_number); + !write_result) + { + return false; + } } m_filesystem->update_inode_block_count(m_data, count); diff --git a/kernel/kernel/filesystem/ext2/inode.tests.cpp b/kernel/kernel/filesystem/ext2/inode.tests.cpp index 90274f6f..35f54fea 100644 --- a/kernel/kernel/filesystem/ext2/inode.tests.cpp +++ b/kernel/kernel/filesystem/ext2/inode.tests.cpp @@ -361,10 +361,10 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" block_bitmap[1] = std::byte{0b1111'1111}; block_bitmap[2] = std::byte{0b0011'0000}; - dev_inode->write(block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size); + REQUIRE(dev_inode->write(block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size)); auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode)); auto inode_data = kernel::filesystem::ext2::inode_data{}; inode_data.size = 5; @@ -378,7 +378,7 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" block_group_descriptor.inode_table * block_size + (inode_number - 1) * fs.inode_size(); auto inode = kernel::filesystem::ext2::inode{&fs, inode_number, inode_data}; - dev_inode->write(&inode, inode_data_offset, sizeof(kernel::filesystem::ext2::inode_data)); + REQUIRE(dev_inode->write(&inode, inode_data_offset, sizeof(kernel::filesystem::ext2::inode_data))); auto buffer = kstd::vector{std::byte{'H'}, std::byte{'e'}, std::byte{'l'}, std::byte{'l'}, std::byte{'o'}, std::byte{' '}, std::byte{'W'}, std::byte{'o'}, @@ -386,7 +386,7 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" auto do_read_inode = [&]() { auto inode_data = kernel::filesystem::ext2::inode_data{}; - dev_inode->read(&inode_data, inode_data_offset, sizeof(kernel::filesystem::ext2::inode_data)); + REQUIRE(dev_inode->read(&inode_data, inode_data_offset, sizeof(kernel::filesystem::ext2::inode_data))); auto read_inode = kernel::filesystem::ext2::inode{&fs, inode_number, inode_data}; return read_inode; }; @@ -401,7 +401,7 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" REQUIRE(text == "Hel"); auto read_block_bitmap = std::vector(block_size, std::byte{0x00}); - dev_inode->read(read_block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size); + REQUIRE(dev_inode->read(read_block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size)); REQUIRE(block_bitmap == read_block_bitmap); auto read_inode = do_read_inode(); @@ -420,11 +420,11 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" REQUIRE(bytes_written == buffer.size()); auto const text = - std::string_view{reinterpret_cast(device->data.data() + 21 * block_size - 4), bytes_written}; + std::string_view{reinterpret_cast(device->data.data() + 21 * block_size - 4), *bytes_written}; REQUIRE(text == "Hello World!"); auto read_block_bitmap = std::vector(block_size, std::byte{0x00}); - dev_inode->read(read_block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size); + REQUIRE(dev_inode->read(read_block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size)); REQUIRE(block_bitmap == read_block_bitmap); auto read_inode = do_read_inode(); @@ -444,11 +444,11 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]" auto read_buffer = std::vector(buffer.size(), std::byte{0x00}); auto const bytes_read = inode.read(read_buffer.data(), offset, buffer.size()); - auto const text = std::string_view{reinterpret_cast(read_buffer.data()), bytes_read}; + auto const text = std::string_view{reinterpret_cast(read_buffer.data()), *bytes_read}; REQUIRE(text == "Hello World!"); auto read_block_bitmap = std::vector(block_size, std::byte{0x00}); - dev_inode->read(read_block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size); + REQUIRE(dev_inode->read(read_block_bitmap.data(), block_group_descriptor.block_bitmap * block_size, block_size)); auto expected_block_bitmap = block_bitmap; expected_block_bitmap[2] = std::byte{0b0011'0001}; -- cgit v1.2.3