aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/CMakeLists.txt1
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.cpp87
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.hpp48
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp118
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.cpp36
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.tests.cpp8
-rw-r--r--kernel/kernel/filesystem/ext2/inode.tests.cpp5
-rw-r--r--kernel/kernel/filesystem/ext2/linked_directory_entry.hpp6
8 files changed, 282 insertions, 27 deletions
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index 6f998084..1a5e2421 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -75,6 +75,7 @@ target_sources("kernel_lib" PRIVATE
"kernel/filesystem/devfs/module.cpp"
# ext2 Filesystem
+ "kernel/filesystem/ext2/directory_iterator.cpp"
"kernel/filesystem/ext2/error.cpp"
"kernel/filesystem/ext2/filesystem.cpp"
"kernel/filesystem/ext2/inode.cpp"
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.cpp b/kernel/kernel/filesystem/ext2/directory_iterator.cpp
new file mode 100644
index 00000000..f37c87b6
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.cpp
@@ -0,0 +1,87 @@
+#include <kernel/filesystem/ext2/directory_iterator.hpp>
+
+#include <kernel/filesystem/ext2/inode.hpp>
+
+#include <kstd/units.hpp>
+#include <kstd/vector.hpp>
+
+#include <system.hpp>
+
+namespace kernel::filesystem::ext2
+{
+
+ directory_iterator::directory_iterator(inode const & inode)
+ : m_inode(&inode)
+ , m_buffer{sizeof(value_type)}
+ {
+ if (!inode.is_directory())
+ {
+ kapi::system::panic("[FS:ext2] Tried perform directory iteration on non-directory inode {}", inode.number());
+ }
+ read();
+ }
+
+ auto directory_iterator::operator*() const -> reference
+ {
+ return *reinterpret_cast<pointer>(m_buffer.data());
+ }
+
+ auto directory_iterator::operator->() const -> pointer
+ {
+ return reinterpret_cast<pointer>(m_buffer.data());
+ }
+
+ auto directory_iterator::operator++() -> directory_iterator &
+ {
+ if (!m_inode || m_file_offset >= m_inode->size())
+ {
+ m_inode = nullptr;
+ return *this;
+ }
+ read();
+ return *this;
+ }
+
+ auto directory_iterator::operator++(int) -> directory_iterator
+ {
+ auto copy = *this;
+ ++(*this);
+ return copy;
+ }
+
+ auto operator==(directory_iterator const & lhs, directory_iterator const & rhs) -> bool
+ {
+ if (lhs.m_inode == rhs.m_inode)
+ {
+ return lhs.m_inode == nullptr || lhs.m_file_offset == rhs.m_file_offset;
+ }
+ return false;
+ }
+
+ auto directory_iterator::read() -> void
+ {
+ if (auto result = m_inode->read(m_buffer, m_file_offset); !result)
+ {
+ kapi::system::panic("[FS:ext2] failed to read directory entry", result.error());
+ }
+
+ auto entry = reinterpret_cast<pointer>(m_buffer.data());
+
+ auto const remainder = entry->name_len - 1;
+
+ if (remainder > 0)
+ {
+ m_buffer.resize(m_buffer.size() + remainder);
+
+ if (auto result = m_inode->read(m_buffer, m_file_offset); !result)
+ {
+ kapi::system::panic("[FS:ext2] failed to read directory entry", result.error());
+ }
+
+ entry = reinterpret_cast<pointer>(m_buffer.data());
+ }
+
+ m_file_offset = m_file_offset + static_cast<kstd::bytes>(entry->rec_len);
+ }
+
+} // namespace kernel::filesystem::ext2 \ No newline at end of file
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.hpp b/kernel/kernel/filesystem/ext2/directory_iterator.hpp
new file mode 100644
index 00000000..92dd2b51
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.hpp
@@ -0,0 +1,48 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_EXT2_DIRECTORY_ITERATOR_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_EXT2_DIRECTORY_ITERATOR_HPP
+
+#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/linked_directory_entry.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/units.hpp>
+#include <kstd/vector.hpp>
+
+#include <cstddef>
+#include <iterator>
+
+namespace kernel::filesystem::ext2
+{
+
+ struct directory_iterator
+ {
+ using iterator_category = std::forward_iterator_tag;
+ using iterator_concept = std::forward_iterator_tag;
+ using value_type = linked_directory_entry;
+ using pointer = linked_directory_entry const *;
+ using reference = linked_directory_entry const &;
+
+ constexpr directory_iterator() = default;
+ explicit directory_iterator(inode const & inode);
+
+ auto operator*() const -> reference;
+
+ auto operator->() const -> pointer;
+
+ auto operator++() -> directory_iterator &;
+
+ auto operator++(int) -> directory_iterator;
+
+ auto friend operator==(directory_iterator const & lhs, directory_iterator const & rhs) -> bool;
+
+ private:
+ auto read() -> void;
+
+ kstd::observer_ptr<inode const> m_inode{};
+ kstd::bytes m_file_offset{};
+ kstd::vector<std::byte> m_buffer{};
+ };
+
+} // namespace kernel::filesystem::ext2
+
+#endif \ No newline at end of file
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp b/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp
new file mode 100644
index 00000000..20ef7009
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp
@@ -0,0 +1,118 @@
+#include <kernel/filesystem/ext2/directory_iterator.hpp>
+
+#include <kernel/devices/storage.hpp>
+#include <kernel/filesystem/device_inode.hpp>
+#include <kernel/filesystem/ext2/filesystem.hpp>
+#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/mount.hpp>
+#include <kernel/test_support/filesystem/storage_boot_module_fixture.hpp>
+
+#include <kstd/memory.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <algorithm>
+#include <filesystem>
+#include <string>
+#include <vector>
+
+SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
+ "Ext2 directory_iterator walks a real directory and terminates", "[filesystem][ext2][readdir]")
+{
+ auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img";
+
+ GIVEN("a mounted ext2 filesystem and its root directory")
+ {
+ REQUIRE(std::filesystem::exists(image_path));
+ REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path}));
+
+ auto boot_device = kernel::devices::storage::determine_boot_device();
+ REQUIRE(boot_device != nullptr);
+
+ auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(boot_device);
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ REQUIRE(mount);
+
+ auto root = (*mount)->root_dentry()->inode();
+ auto const & root_ext2_inode = static_cast<kernel::filesystem::ext2::inode const &>(*root);
+
+ WHEN("iterating from begin to the default-constructed end sentinel")
+ {
+ auto names = std::vector<std::string>{};
+ auto guard = 0;
+
+ for (auto it = kernel::filesystem::ext2::directory_iterator{root_ext2_inode};
+ it != kernel::filesystem::ext2::directory_iterator{}; ++it)
+ {
+ REQUIRE(guard++ < 64); // fails loudly on a non-terminating loop rather than hanging the suite
+ names.emplace_back(&it->name_start, it->name_len);
+ }
+
+ THEN("iteration terminates on its own and finds the expected entries")
+ {
+ REQUIRE(guard < 64);
+ REQUIRE(std::ranges::find(names, ".") != names.end());
+ REQUIRE(std::ranges::find(names, "..") != names.end());
+ REQUIRE(std::ranges::find(names, "information") != names.end());
+ }
+ }
+ }
+}
+
+SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
+ "Ext2 directory_iterator satisfies the forward-iterator multi-pass guarantee",
+ "[filesystem][ext2][readdir]")
+{
+ auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img";
+
+ GIVEN("an iterator positioned at the first entry of the root directory")
+ {
+ REQUIRE(std::filesystem::exists(image_path));
+ REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path}));
+
+ auto boot_device = kernel::devices::storage::determine_boot_device();
+ REQUIRE(boot_device != nullptr);
+
+ auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(boot_device);
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ REQUIRE(mount);
+
+ auto root = (*mount)->root_dentry()->inode();
+ auto const & root_ext2_inode = static_cast<kernel::filesystem::ext2::inode const &>(*root);
+
+ auto original = kernel::filesystem::ext2::directory_iterator{root_ext2_inode};
+ auto const first_name = std::string{&original->name_start, original->name_len};
+
+ WHEN("the iterator is copied, then only the original is advanced")
+ {
+ auto copy = original;
+ ++original;
+
+ THEN("the copy still refers to the first entry, unaffected by advancing the original")
+ {
+ auto const copy_name = std::string{&copy->name_start, copy->name_len};
+ REQUIRE(copy_name == first_name);
+
+ auto const advanced_name = std::string{&original->name_start, original->name_len};
+ REQUIRE(advanced_name != first_name);
+ }
+ }
+ }
+}
+
+SCENARIO("Ext2 directory_iterator's default-constructed value is a valid, comparable end sentinel",
+ "[filesystem][ext2][readdir]")
+{
+ GIVEN("two independently default-constructed iterators")
+ {
+ auto first = kernel::filesystem::ext2::directory_iterator{};
+ auto second = kernel::filesystem::ext2::directory_iterator{};
+
+ THEN("they compare equal to each other")
+ {
+ REQUIRE(first == second);
+ }
+ }
+} \ No newline at end of file
diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp
index 18af5a61..7031cf5f 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.cpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.cpp
@@ -2,6 +2,7 @@
#include <kernel/filesystem/error.hpp>
#include <kernel/filesystem/ext2/block_group_descriptor.hpp>
+#include <kernel/filesystem/ext2/directory_iterator.hpp>
#include <kernel/filesystem/ext2/error.hpp>
#include <kernel/filesystem/ext2/inode.hpp>
#include <kernel/filesystem/ext2/linked_directory_entry.hpp>
@@ -186,13 +187,13 @@ namespace kernel::filesystem::ext2
auto filesystem::lookup(inode_ptr const & parent, std::string_view name, driver_data_ptr driver_data) const
-> kstd::result<inode_ptr>
{
- auto const ext2_parent = static_pointer_cast<inode>(parent);
- if (!ext2_parent)
+ auto const directory = static_pointer_cast<inode>(parent);
+ if (!directory)
{
return kstd::failure(vfs_errc::invalid_inode);
}
- if (!ext2_parent->is_directory())
+ if (!directory->is_directory())
{
return kstd::failure(vfs_errc::not_a_directory);
}
@@ -203,34 +204,16 @@ namespace kernel::filesystem::ext2
return kstd::failure(vfs_errc::not_mounted);
}
- auto buffer = kstd::vector<std::byte>{block_size(*mount_state).value};
-
- for (auto i = 0u; i < block_count(*ext2_parent, *mount_state); ++i)
+ for (auto it = directory_iterator{*directory}; it != directory_iterator{}; ++it)
{
- auto const global_block_number = inode_block_number(i, *ext2_parent, *mount_state);
- if (!global_block_number)
- {
- return kstd::failure(global_block_number.error());
- }
-
- if (auto result = read_block(*global_block_number, buffer, *mount_state); !result)
+ if (it->inode == 0)
{
- return kstd::failure(result.error());
+ continue;
}
- auto entry = reinterpret_cast<linked_directory_entry const *>(buffer.data());
- auto read = 0_B;
-
- while (read < block_size(*mount_state) && entry->inode != 0)
+ if (it->name() == name)
{
- auto const entry_name = std::string_view{&entry->name_start, entry->name_len};
- if (entry_name == name)
- {
- return read_inode(entry->inode, *mount_state);
- }
-
- read += kstd::bytes{entry->rec_len};
- entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + read);
+ return read_inode(it->inode, *mount_state);
}
}
@@ -309,6 +292,7 @@ namespace kernel::filesystem::ext2
{
return kstd::failure(result.error());
}
+ created->set_size(block_size(*mount_state));
}
if (auto result = write_inode(inode_number, created->data(), *mount_state); !result)
diff --git a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp
index 6303959d..8d1fc3d9 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp
@@ -67,10 +67,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
auto information = fs->lookup(root, "information", driver_data);
REQUIRE(information);
REQUIRE(information.value()->is_directory());
+ (*information)->set_owning_mount(*mount);
auto info_1 = fs->lookup(*information, "info_1.txt", driver_data);
REQUIRE(info_1);
REQUIRE(info_1.value()->is_regular());
+ (*info_1)->set_owning_mount(*mount);
}
THEN("lookup returns null for invalid inputs")
@@ -79,8 +81,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
auto information = fs->lookup(root, "information", driver_data);
REQUIRE(information);
+ (*information)->set_owning_mount(*mount);
auto info_1 = fs->lookup(*information, "info_1.txt", driver_data);
REQUIRE(info_1);
+ (*info_1)->set_owning_mount(*mount);
REQUIRE(!fs->lookup(*info_1, "anything", driver_data));
REQUIRE(!fs->lookup(root, "does_not_exist", driver_data));
@@ -117,9 +121,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
auto new_inode = fs->create_inode(root, "blub", kapi::filesystem::file_type::regular, driver_data);
REQUIRE(new_inode);
REQUIRE(new_inode.value()->is_regular());
+ (*new_inode)->set_owning_mount(*mount);
lookup_result = fs->lookup(root, "blub", driver_data);
REQUIRE(lookup_result);
+ (*lookup_result)->set_owning_mount(*mount);
}
THEN("a directory can be created")
@@ -139,10 +145,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
{
auto new_directory = fs->create_inode(root, "blub", kapi::filesystem::file_type::directory, driver_data);
REQUIRE(new_directory);
+ (*new_directory)->set_owning_mount(*mount);
auto new_file =
fs->create_inode(new_directory.value(), "blub_file", kapi::filesystem::file_type::regular, driver_data);
REQUIRE(new_file);
+ (*new_file)->set_owning_mount(*mount);
auto lookup_result = fs->lookup(new_directory.value(), "blub_file", driver_data);
REQUIRE(lookup_result);
diff --git a/kernel/kernel/filesystem/ext2/inode.tests.cpp b/kernel/kernel/filesystem/ext2/inode.tests.cpp
index 2d3a2ec2..4d57768b 100644
--- a/kernel/kernel/filesystem/ext2/inode.tests.cpp
+++ b/kernel/kernel/filesystem/ext2/inode.tests.cpp
@@ -111,6 +111,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in
auto information = fs->lookup(root, "information", driver_data);
REQUIRE(information);
+ (*information)->set_owning_mount(*mount);
+
auto file = fs->lookup(*information, "info_1.txt", driver_data);
REQUIRE(file);
REQUIRE(file.value()->is_regular());
@@ -322,10 +324,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in
auto information = fs->lookup(root, "information", driver_data);
REQUIRE(information);
+ (*information)->set_owning_mount(*mount);
+
auto file = fs->lookup(*information, "info_1.txt", driver_data);
REQUIRE(file);
REQUIRE(file.value()->is_regular());
-
(*file)->set_owning_mount(*mount);
auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>(driver_data);
diff --git a/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp
index 08999cb8..2d364f05 100644
--- a/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp
+++ b/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp
@@ -2,12 +2,18 @@
#define TEACHOS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP
#include <cstdint>
+#include <string_view>
namespace kernel::filesystem::ext2
{
//! A linked directory entry in the ext2 filesystem.
struct [[gnu::packed]] linked_directory_entry
{
+ [[nodiscard]] constexpr auto name() const noexcept -> std::string_view
+ {
+ return std::string_view{&name_start, name_len};
+ }
+
uint32_t inode;
uint16_t rec_len;
uint8_t name_len;