aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorLukas Oesch <lukasoesch20@gmail.com>2026-05-30 22:04:08 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-07-14 20:16:27 +0200
commitc26d681e88905726d6521b4fbfaeb63603db8faf (patch)
treed89677223404f24ab3dc23694fea052754b23c2f /kernel
parentc63737704b8e9b275d9f2c09c9c8a2904230846d (diff)
downloadkernel-c26d681e88905726d6521b4fbfaeb63603db8faf.tar.xz
kernel-c26d681e88905726d6521b4fbfaeb63603db8faf.zip
add inode write_global_block_number_to_inode_block_index tests
Diffstat (limited to 'kernel')
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.cpp108
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.hpp5
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.tests.cpp90
-rw-r--r--kernel/kernel/filesystem/ext2/inode.cpp7
-rw-r--r--kernel/kernel/filesystem/ext2/inode.tests.cpp18
5 files changed, 216 insertions, 12 deletions
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 <cstddef>
#include <cstdint>
#include <expected>
+#include <optional>
#include <string_view>
#include <tuple>
@@ -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<void>
+ {
+ 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<uint32_t>
+ {
+ 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<size_t>
+ {
+ auto const block_offset = static_cast<size_t>(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<size_t>
+ {
+ auto const block_offset = static_cast<size_t>(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 <array>
#include <cstddef>
#include <cstdint>
+#include <optional>
#include <string_view>
namespace kernel::filesystem::ext2
@@ -101,7 +102,7 @@ namespace kernel::filesystem::ext2
-> kstd::result<std::size_t>;
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<void>;
private:
struct indirect_level
@@ -115,7 +116,7 @@ namespace kernel::filesystem::ext2
[[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::result<kstd::shared_ptr<inode>>;
[[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> kstd::result<uint32_t>;
- auto ensure_and_get_block(uint32_t & block_number) -> bool;
+ auto ensure_and_get_block(uint32_t block_number) -> std::optional<uint32_t>;
[[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 <kernel/devices/storage/management.hpp>
#include <kernel/filesystem/device_inode.hpp>
+#include <kernel/filesystem/ext2/block_group_descriptor.hpp>
#include <kernel/filesystem/ext2/error.hpp>
#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/superblock.hpp>
#include <kernel/filesystem/filesystem.hpp>
#include <kernel/test_support/devices/block_device.hpp>
#include <kernel/test_support/filesystem/ext2.hpp>
@@ -15,8 +17,11 @@
#include <catch2/catch_test_macros.hpp>
#include <array>
+#include <cstddef>
#include <cstdint>
+#include <cstring>
#include <filesystem>
+#include <vector>
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<kernel::tests::devices::block_device>(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<std::byte>(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<kernel::filesystem::device_inode>(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<uint32_t>(block_size / sizeof(uint32_t));
+ auto const singly_start = static_cast<uint32_t>(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<size_t>(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<size_t>(inode_data.block[13]) * block_size);
+ REQUIRE(doubly_leaf_table == 18);
+ REQUIRE(read_u32(static_cast<size_t>(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<size_t>(inode_data.block[14]) * block_size);
+ REQUIRE(triply_middle_table == 20);
+ auto const triply_leaf_table = read_u32(static_cast<size_t>(triply_middle_table) * block_size);
+ REQUIRE(triply_leaf_table == 21);
+ REQUIRE(read_u32(static_cast<size_t>(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>{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<std::byte>(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<char const *>(device->data.data() + 21 * block_size - 4), bytes_written};
+ std::string_view{reinterpret_cast<char const *>(device->data.data() + 21 * block_size - 4), *bytes_written};
REQUIRE(text == "Hello World!");
auto read_block_bitmap = std::vector<std::byte>(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<std::byte>(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<char const *>(read_buffer.data()), bytes_read};
+ auto const text = std::string_view{reinterpret_cast<char const *>(read_buffer.data()), *bytes_read};
REQUIRE(text == "Hello World!");
auto read_block_bitmap = std::vector<std::byte>(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};