aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/CMakeLists.txt8
-rw-r--r--kernel/kernel/filesystem/dentry.cpp5
-rw-r--r--kernel/kernel/filesystem/dentry.hpp17
-rw-r--r--kernel/kernel/filesystem/dentry.tests.cpp4
-rw-r--r--kernel/kernel/filesystem/devfs/filesystem.cpp82
-rw-r--r--kernel/kernel/filesystem/devfs/filesystem.hpp27
-rw-r--r--kernel/kernel/filesystem/devfs/filesystem.tests.cpp49
-rw-r--r--kernel/kernel/filesystem/devfs/inode.cpp72
-rw-r--r--kernel/kernel/filesystem/devfs/inode.hpp9
-rw-r--r--kernel/kernel/filesystem/devfs/inode.tests.cpp177
-rw-r--r--kernel/kernel/filesystem/devfs/module.cpp28
-rw-r--r--kernel/kernel/filesystem/device_number_registry.cpp1
-rw-r--r--kernel/kernel/filesystem/device_number_registry.hpp4
-rw-r--r--kernel/kernel/filesystem/device_number_registry.tests.cpp7
-rw-r--r--kernel/kernel/filesystem/directory_listing_cursor.hpp23
-rw-r--r--kernel/kernel/filesystem/directory_listing_entry.hpp23
-rw-r--r--kernel/kernel/filesystem/driver_registry.cpp130
-rw-r--r--kernel/kernel/filesystem/driver_registry.hpp73
-rw-r--r--kernel/kernel/filesystem/error.cpp7
-rw-r--r--kernel/kernel/filesystem/error.hpp2
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.cpp91
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.hpp51
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp123
-rw-r--r--kernel/kernel/filesystem/ext2/error.cpp3
-rw-r--r--kernel/kernel/filesystem/ext2/error.hpp3
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.cpp858
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.hpp207
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.tests.cpp129
-rw-r--r--kernel/kernel/filesystem/ext2/inode.cpp307
-rw-r--r--kernel/kernel/filesystem/ext2/inode.hpp96
-rw-r--r--kernel/kernel/filesystem/ext2/inode.tests.cpp145
-rw-r--r--kernel/kernel/filesystem/ext2/linked_directory_entry.hpp9
-rw-r--r--kernel/kernel/filesystem/ext2/module.cpp26
-rw-r--r--kernel/kernel/filesystem/ext2/mount_state.hpp26
-rw-r--r--kernel/kernel/filesystem/ext2/write_batch.cpp67
-rw-r--r--kernel/kernel/filesystem/ext2/write_batch.hpp53
-rw-r--r--kernel/kernel/filesystem/file_type.hpp18
-rw-r--r--kernel/kernel/filesystem/file_type_bindings.hpp7
-rw-r--r--kernel/kernel/filesystem/filesystem.cpp50
-rw-r--r--kernel/kernel/filesystem/filesystem.hpp46
-rw-r--r--kernel/kernel/filesystem/inode.cpp23
-rw-r--r--kernel/kernel/filesystem/inode.hpp34
-rw-r--r--kernel/kernel/filesystem/mount.cpp61
-rw-r--r--kernel/kernel/filesystem/mount.hpp60
-rw-r--r--kernel/kernel/filesystem/mount.tests.cpp59
-rw-r--r--kernel/kernel/filesystem/mount_table.tests.cpp40
-rw-r--r--kernel/kernel/filesystem/open_file_descriptor.cpp6
-rw-r--r--kernel/kernel/filesystem/rootfs/filesystem.cpp45
-rw-r--r--kernel/kernel/filesystem/rootfs/filesystem.hpp15
-rw-r--r--kernel/kernel/filesystem/rootfs/filesystem.tests.cpp18
-rw-r--r--kernel/kernel/filesystem/rootfs/module.cpp28
-rw-r--r--kernel/kernel/filesystem/type.hpp48
-rw-r--r--kernel/kernel/filesystem/type_registry.cpp94
-rw-r--r--kernel/kernel/filesystem/type_registry.hpp59
-rw-r--r--kernel/kernel/filesystem/type_registry.tests.cpp81
-rw-r--r--kernel/kernel/filesystem/vfs.cpp119
-rw-r--r--kernel/kernel/filesystem/vfs.hpp7
-rw-r--r--kernel/kernel/filesystem/vfs.tests.cpp4
-rw-r--r--kernel/kernel/main.cpp4
-rw-r--r--kernel/kernel/test_support/filesystem/driver_registry.hpp10
-rw-r--r--kernel/kernel/test_support/filesystem/filesystem.cpp20
-rw-r--r--kernel/kernel/test_support/filesystem/filesystem.hpp15
-rw-r--r--kernel/kernel/test_support/filesystem/storage_boot_module_fixture.cpp2
-rw-r--r--kernel/kernel/test_support/filesystem/type_registry.hpp10
-rw-r--r--kernel/kernel/test_support/state_reset_listener.cpp11
65 files changed, 2585 insertions, 1351 deletions
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index c7cf9498..35c3a957 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -60,28 +60,32 @@ target_sources("kernel_lib" PRIVATE
"kernel/filesystem/dentry.cpp"
"kernel/filesystem/device_inode.cpp"
"kernel/filesystem/device_number_registry.cpp"
+ "kernel/filesystem/driver_registry.cpp"
"kernel/filesystem/error.cpp"
- "kernel/filesystem/filesystem.cpp"
"kernel/filesystem/inode.cpp"
"kernel/filesystem/mount_table.cpp"
"kernel/filesystem/mount.cpp"
"kernel/filesystem/open_file_descriptor.cpp"
"kernel/filesystem/open_file_table.cpp"
- "kernel/filesystem/type_registry.cpp"
"kernel/filesystem/vfs.cpp"
# DevFS Filesystem
"kernel/filesystem/devfs/filesystem.cpp"
"kernel/filesystem/devfs/inode.cpp"
+ "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"
+ "kernel/filesystem/ext2/module.cpp"
+ "kernel/filesystem/ext2/write_batch.cpp"
# Rootfs Filesystem
"kernel/filesystem/rootfs/filesystem.cpp"
"kernel/filesystem/rootfs/inode.cpp"
+ "kernel/filesystem/rootfs/module.cpp"
)
file(GLOB_RECURSE KERNEL_HEADERS
diff --git a/kernel/kernel/filesystem/dentry.cpp b/kernel/kernel/filesystem/dentry.cpp
index 9321453a..e82c5419 100644
--- a/kernel/kernel/filesystem/dentry.cpp
+++ b/kernel/kernel/filesystem/dentry.cpp
@@ -13,7 +13,8 @@
namespace kernel::filesystem
{
- dentry::dentry(kstd::shared_ptr<dentry> const & parent, kstd::shared_ptr<inode> const & inode, std::string_view name)
+ dentry::dentry(kstd::shared_ptr<dentry> const & parent, kstd::shared_ptr<kernel::filesystem::inode> const & inode,
+ std::string_view name)
: m_name(name)
, m_parent(parent)
, m_inode(inode)
@@ -29,7 +30,7 @@ namespace kernel::filesystem
}
}
- auto dentry::get_inode() const -> kstd::shared_ptr<inode> const &
+ auto dentry::inode() const -> kstd::shared_ptr<kernel::filesystem::inode> const &
{
return m_inode;
}
diff --git a/kernel/kernel/filesystem/dentry.hpp b/kernel/kernel/filesystem/dentry.hpp
index a33ff22d..f7897764 100644
--- a/kernel/kernel/filesystem/dentry.hpp
+++ b/kernel/kernel/filesystem/dentry.hpp
@@ -15,6 +15,9 @@ namespace kernel::filesystem
//! A directory entry in the filesystem.
struct dentry
{
+ using dentry_ptr = kstd::shared_ptr<dentry>;
+ using inode_ptr = kstd::shared_ptr<kernel::filesystem::inode>;
+
//! Flags for the dentry.
enum class dentry_flags : uint32_t
{
@@ -28,17 +31,17 @@ namespace kernel::filesystem
//! @param parent The parent directory entry, if any.
//! @param inode The associated inode for this dentry.
//! @param name The name of the dentry.
- dentry(kstd::shared_ptr<dentry> const & parent, kstd::shared_ptr<inode> const & inode, std::string_view name);
+ dentry(dentry_ptr const & parent, inode_ptr const & inode, std::string_view name);
//! Get this entrys associated inode.
//!
//! @return The inode associated with this directory entry.
- [[nodiscard]] auto get_inode() const -> kstd::shared_ptr<inode> const &;
+ [[nodiscard]] auto inode() const -> inode_ptr const &;
//! Get this entrys parent directory entry.
//!
//! @return The parent directory entry, or @p nullptr if this entry has no parent.
- [[nodiscard]] auto parent() const -> kstd::shared_ptr<dentry>;
+ [[nodiscard]] auto parent() const -> dentry_ptr;
//! Get this entrys name.
//!
@@ -56,7 +59,7 @@ namespace kernel::filesystem
//! Add a child to this entry.
//!
//! @param child The child directory entry to add.
- auto add_child(kstd::shared_ptr<dentry> const & child) -> void;
+ auto add_child(dentry_ptr const & child) -> void;
//! Find a child entry with the given name.
//!
@@ -65,7 +68,7 @@ namespace kernel::filesystem
//!
//! @param name The name of the child directory entry to find.
//! @return A pointer to the child dentry if it exists, a null pointer otherwise.
- [[nodiscard]] auto find_child(std::string_view name) const -> kstd::shared_ptr<dentry>;
+ [[nodiscard]] auto find_child(std::string_view name) const -> dentry_ptr;
//! Set a flag for this entry.
//!
@@ -86,8 +89,8 @@ namespace kernel::filesystem
private:
kstd::string m_name;
kstd::weak_ptr<dentry> m_parent;
- kstd::vector<kstd::shared_ptr<dentry>> m_children;
- kstd::shared_ptr<inode> m_inode;
+ kstd::vector<dentry_ptr> m_children;
+ inode_ptr m_inode;
uint32_t m_flags;
};
} // namespace kernel::filesystem
diff --git a/kernel/kernel/filesystem/dentry.tests.cpp b/kernel/kernel/filesystem/dentry.tests.cpp
index 55cdf21e..cf3915de 100644
--- a/kernel/kernel/filesystem/dentry.tests.cpp
+++ b/kernel/kernel/filesystem/dentry.tests.cpp
@@ -22,7 +22,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]")
THEN("the dentry has the correct parent, inode, and name")
{
REQUIRE(child_dentry.parent() == parent_dentry);
- REQUIRE(child_dentry.get_inode() == inode);
+ REQUIRE(child_dentry.inode() == inode);
REQUIRE(child_dentry.name() == "child");
}
@@ -47,7 +47,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]")
THEN("the dentry has a null parent, the correct inode, and the correct name")
{
REQUIRE(child_dentry.parent() == nullptr);
- REQUIRE(child_dentry.get_inode() == inode);
+ REQUIRE(child_dentry.inode() == inode);
REQUIRE(child_dentry.name() == "child");
}
diff --git a/kernel/kernel/filesystem/devfs/filesystem.cpp b/kernel/kernel/filesystem/devfs/filesystem.cpp
index 92756a65..0de513a7 100644
--- a/kernel/kernel/filesystem/devfs/filesystem.cpp
+++ b/kernel/kernel/filesystem/devfs/filesystem.cpp
@@ -6,11 +6,9 @@
#include <kernel/filesystem/error.hpp>
#include <kernel/filesystem/filesystem.hpp>
#include <kernel/filesystem/inode.hpp>
-#include <kernel/filesystem/type.hpp>
#include <kapi/filesystem.hpp>
-#include <kstd/flat_map.hpp>
#include <kstd/memory.hpp>
#include <kstd/result.hpp>
#include <kstd/system_error.hpp>
@@ -22,50 +20,27 @@
namespace kernel::filesystem::devfs
{
- struct type final : kernel::filesystem::type
- {
- [[nodiscard]] auto name() const noexcept -> std::string_view override
- {
- return "devfs";
- }
- [[nodiscard]] auto probe(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) const noexcept
- -> kstd::result<std::uint32_t> override
- {
- if (backing_inode)
- {
- return kstd::failure(vfs_errc::invalid_argument);
- }
- return 0;
- }
+ static_assert(sizeof(kernel::filesystem::filesystem) == sizeof(devfs::filesystem));
- [[nodiscard]] auto make_instance() const -> kstd::shared_ptr<kernel::filesystem::filesystem> override
+ auto filesystem::probe(inode_ptr const & inode) const -> kstd::result<std::uint32_t>
+ {
+ if (inode)
{
- return kstd::make_shared<filesystem>();
+ return kstd::failure(vfs_errc::invalid_argument);
}
- };
- [[gnu::used]]
- constexpr auto registration = type_registration<type>{};
+ return 0;
+ }
- auto filesystem::mount(kstd::shared_ptr<kernel::filesystem::inode> const &) -> kstd::result<void>
+ auto filesystem::mount(inode_ptr const &) -> kstd::result<mount_result>
{
- m_root_inode = kstd::make_shared<inode>();
-
- std::ranges::for_each(device_number_registry::get().all(), [&](auto const & numbered) {
- if (auto device = numbered.device.lock())
- {
- m_inodes.emplace(numbered.name, kstd::make_shared<device_inode>(device));
- }
- });
-
- device_number_registry::get().subscribe(shared_from_this());
-
- return kstd::success();
+ auto root = kstd::make_shared<inode>();
+ return mount_result{root, root};
}
- auto filesystem::lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
+ auto filesystem::lookup(inode_ptr const & parent, std::string_view name, driver_data_ptr driver_data) const
+ -> kstd::result<inode_ptr>
{
if (!parent)
{
@@ -77,38 +52,31 @@ namespace kernel::filesystem::devfs
return kstd::failure(vfs_errc::not_a_directory);
}
- if (parent.get() != m_root_inode.get())
+ if (parent.get() != driver_data.get())
{
return kstd::failure(vfs_errc::invalid_inode);
}
- auto found = std::ranges::find_if(m_inodes, [&](auto const & entry) { return entry.first == name; });
- if (found != m_inodes.end())
+ auto entries = device_number_registry::get().all();
+ auto found = std::ranges::find_if(entries, [&](auto const & entry) { return entry.name == name; });
+
+ if (found == entries.end())
+ {
+ return kstd::failure(vfs_errc::no_such_file_or_directory);
+ }
+
+ if (auto device = found->device.lock())
{
- return found->second;
+ return kstd::make_shared<device_inode>(device);
}
return kstd::failure(vfs_errc::no_such_file_or_directory);
}
- auto filesystem::create_inode(kstd::shared_ptr<kernel::filesystem::inode> const &, std::string_view,
- kapi::filesystem::file_type, std::optional<kapi::filesystem::device_number>)
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
+ auto filesystem::create_inode(inode_ptr const &, std::string_view, kapi::filesystem::file_type, driver_data_ptr,
+ std::optional<kapi::filesystem::device_number>) -> kstd::result<inode_ptr>
{
return kstd::failure(vfs_errc::read_only_file_system);
}
- auto filesystem::on_device_numbered(device_number_registry::entry const & numbered) -> void
- {
- if (auto device = numbered.device.lock())
- {
- m_inodes.emplace(numbered.name, kstd::make_shared<device_inode>(device));
- }
- }
-
- auto filesystem::on_device_unnumbered(device_number_registry::entry const & numbered) -> void
- {
- erase_if(m_inodes, [&](auto const & entry) { return entry.first == numbered.name; });
- }
-
} // namespace kernel::filesystem::devfs \ No newline at end of file
diff --git a/kernel/kernel/filesystem/devfs/filesystem.hpp b/kernel/kernel/filesystem/devfs/filesystem.hpp
index 2b0221d0..aa33c58b 100644
--- a/kernel/kernel/filesystem/devfs/filesystem.hpp
+++ b/kernel/kernel/filesystem/devfs/filesystem.hpp
@@ -3,17 +3,18 @@
#include <kernel/filesystem/device_inode.hpp>
#include <kernel/filesystem/device_number_registry.hpp>
+#include <kernel/filesystem/error.hpp>
#include <kernel/filesystem/filesystem.hpp>
#include <kernel/filesystem/inode.hpp>
#include <kapi/filesystem.hpp>
-#include <kstd/flat_map.hpp>
#include <kstd/memory.hpp>
#include <kstd/result.hpp>
#include <kstd/string.hpp>
#include <kstd/system_error.hpp>
+#include <cstdint>
#include <optional>
#include <string_view>
@@ -26,27 +27,21 @@ namespace kernel::filesystem::devfs
//! applications to interact with devices using standard file operations. The devfs filesystem dynamically creates
//! inodes for devices registered in the system, enabling seamless access to device functionality through the
//! filesystem interface.
- struct filesystem : kernel::filesystem::filesystem,
- kstd::enable_shared_from_this<filesystem>,
- device_number_registry_observer
+ struct filesystem final : kernel::filesystem::filesystem
{
- auto mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) -> kstd::result<void> override;
+ [[nodiscard]] auto probe(inode_ptr const & inode) const -> kstd::result<std::uint32_t> override;
- [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
+ auto mount(inode_ptr const & backing_inode) -> kstd::result<mount_result> override;
+
+ [[nodiscard]] auto lookup(inode_ptr const & parent, std::string_view name, driver_data_ptr driver_data) const
+ -> kstd::result<inode_ptr> override;
//! @copydoc kernel::filesystem::filesystem::create_inode
//! @return Always returns an error, as creating new inodes is not supported in devfs.
- [[nodiscard]] auto create_inode(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name,
- kapi::filesystem::file_type type,
+ [[nodiscard]] auto create_inode(inode_ptr const & parent, std::string_view name, kapi::filesystem::file_type type,
+ driver_data_ptr driver_data,
std::optional<kapi::filesystem::device_number> raw_device = std::nullopt)
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
-
- private:
- auto on_device_numbered(device_number_registry::entry const & numbered) -> void override;
- auto on_device_unnumbered(device_number_registry::entry const & numbered) -> void override;
-
- kstd::flat_map<kstd::string, kstd::shared_ptr<device_inode>> m_inodes{};
+ -> kstd::result<inode_ptr> override;
};
} // namespace kernel::filesystem::devfs
diff --git a/kernel/kernel/filesystem/devfs/filesystem.tests.cpp b/kernel/kernel/filesystem/devfs/filesystem.tests.cpp
index b5a07a22..ec0c1144 100644
--- a/kernel/kernel/filesystem/devfs/filesystem.tests.cpp
+++ b/kernel/kernel/filesystem/devfs/filesystem.tests.cpp
@@ -25,49 +25,47 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
setup_modules(1);
auto fs = kstd::make_shared<kernel::filesystem::devfs::filesystem>();
- auto result = fs->mount(nullptr);
-
- THEN("mount succeeds")
- {
- REQUIRE(result);
- REQUIRE(fs->root_inode());
- }
+ auto mount = fs->mount(nullptr);
+ REQUIRE(mount);
+ auto [root, driver_data] = *mount;
+ REQUIRE(root);
THEN("lookup on root finds ram0 device inode")
{
- auto inode = fs->lookup(fs->root_inode(), "ram0");
+ auto inode = fs->lookup(root, "ram0", driver_data);
REQUIRE(inode);
REQUIRE(inode.value()->is_device());
}
THEN("lookup of an unknown device returns error")
{
- auto inode = fs->lookup(fs->root_inode(), "ram99");
+ auto inode = fs->lookup(root, "ram99", driver_data);
REQUIRE(!inode);
}
THEN("lookup with wrong parent returns error")
{
auto other_fs = kstd::make_shared<kernel::filesystem::devfs::filesystem>();
- CHECK(other_fs->mount(nullptr));
+ auto other_root = other_fs->mount(nullptr);
+ CHECK(other_root);
- auto inode = fs->lookup(other_fs->root_inode(), "ram0");
+ auto inode = fs->lookup(other_root->first, "ram0", driver_data);
REQUIRE(!inode);
}
THEN("lookup with a non-directory parent returns error")
{
- auto non_directory_inode = fs->lookup(fs->root_inode(), "ram0");
+ auto non_directory_inode = fs->lookup(root, "ram0", driver_data);
REQUIRE(non_directory_inode);
REQUIRE_FALSE(non_directory_inode.value()->is_directory());
- auto result = fs->lookup(*non_directory_inode, "anything");
+ auto result = fs->lookup(*non_directory_inode, "anything", driver_data);
REQUIRE(!result);
}
THEN("create_inode always returns an error")
{
- auto result = fs->create_inode(fs->root_inode(), "new_device", kapi::filesystem::file_type::regular);
+ auto result = fs->create_inode(root, "new_device", kapi::filesystem::file_type::regular, driver_data);
REQUIRE(!result);
}
}
@@ -77,14 +75,16 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
setup_modules(3, 2048);
auto fs = kstd::make_shared<kernel::filesystem::devfs::filesystem>();
- auto result = fs->mount(nullptr);
- REQUIRE(result);
+ auto mount = fs->mount(nullptr);
+ REQUIRE(mount);
+ auto [root, driver_data] = *mount;
+ REQUIRE(root);
THEN("lookup finds all generated RAM devices")
{
- REQUIRE(fs->lookup(fs->root_inode(), "ram0"));
- REQUIRE(fs->lookup(fs->root_inode(), "ram1"));
- REQUIRE(fs->lookup(fs->root_inode(), "ram2"));
+ REQUIRE(fs->lookup(root, "ram0", driver_data));
+ REQUIRE(fs->lookup(root, "ram1", driver_data));
+ REQUIRE(fs->lookup(root, "ram2", driver_data));
}
}
}
@@ -94,12 +94,15 @@ SCENARIO("Devfs filesystem updates", "[filesystem][devfs]")
GIVEN("A mounted devfs, and a bus attached to the root without a device attached")
{
auto fs = kstd::make_shared<kernel::filesystem::devfs::filesystem>();
- REQUIRE(fs->mount(nullptr));
+ auto mount = fs->mount(nullptr);
+ REQUIRE(mount);
+ auto [root, driver_data] = *mount;
+ REQUIRE(root);
auto bus = kstd::make_shared<kapi::devices::bus>("devfs_live_update_bus");
kapi::devices::get_root_bus()->add_child(bus);
- REQUIRE_FALSE(fs->lookup(fs->root_inode(), "devfs_live_update_device_node"));
+ REQUIRE_FALSE(fs->lookup(root, "devfs_live_update_device_node", driver_data));
WHEN("a device is attached, bound, and published")
{
@@ -112,7 +115,7 @@ SCENARIO("Devfs filesystem updates", "[filesystem][devfs]")
THEN("devfs finds it immediately")
{
- REQUIRE(fs->lookup(fs->root_inode(), "devfs_live_update_device_node"));
+ REQUIRE(fs->lookup(root, "devfs_live_update_device_node", driver_data));
}
WHEN("the device is detached")
@@ -121,7 +124,7 @@ SCENARIO("Devfs filesystem updates", "[filesystem][devfs]")
THEN("devfs does not find it anymore")
{
- REQUIRE_FALSE(fs->lookup(fs->root_inode(), "devfs_live_update_device_node"));
+ REQUIRE_FALSE(fs->lookup(root, "devfs_live_update_device_node", driver_data));
}
}
}
diff --git a/kernel/kernel/filesystem/devfs/inode.cpp b/kernel/kernel/filesystem/devfs/inode.cpp
index 4153d3ac..8813eb2a 100644
--- a/kernel/kernel/filesystem/devfs/inode.cpp
+++ b/kernel/kernel/filesystem/devfs/inode.cpp
@@ -1,15 +1,35 @@
#include <kernel/filesystem/devfs/inode.hpp>
+#include <kernel/filesystem/device_number_registry.hpp>
+#include <kernel/filesystem/directory_listing_cursor.hpp>
+#include <kernel/filesystem/directory_listing_entry.hpp>
+#include <kernel/filesystem/error.hpp>
+
+#include <kapi/filesystem.hpp>
+
#include <kstd/result.hpp>
#include <kstd/units.hpp>
+#include <algorithm>
+#include <bit>
#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <ranges>
#include <span>
+#include <utility>
using namespace kstd::units_literals;
namespace kernel::filesystem::devfs
{
+ namespace
+ {
+ constexpr auto static sentinel = directory_listing_cursor{std::numeric_limits<std::uint64_t>::max()};
+ }
+
auto inode::read(std::span<std::byte>, kstd::bytes) const -> kstd::result<kstd::bytes>
{
return 0_B;
@@ -24,4 +44,56 @@ namespace kernel::filesystem::devfs
{
return true;
}
+
+ auto inode::read_directory(directory_listing_cursor position, std::span<directory_listing_entry> entries) const
+ -> kstd::result<std::pair<std::size_t, directory_listing_cursor>>
+ {
+ auto const requested = entries.size();
+ if (requested == 0)
+ {
+ return kstd::failure(vfs_errc::invalid_argument);
+ }
+
+ if (position == sentinel)
+ {
+ return std::pair{0, sentinel};
+ }
+
+ auto devices = kernel::filesystem::device_number_registry::get().all();
+ if (devices.empty())
+ {
+ return std::pair{0, position};
+ }
+
+ std::ranges::stable_sort(devices, std::ranges::less{}, &kernel::filesystem::device_number_registry::entry::number);
+
+ auto const device_number = kapi::filesystem::device_number{static_cast<std::uint8_t>(position.value >> 8),
+ static_cast<std::uint8_t>(position.value & 0xff)};
+ auto const begin = std::ranges::lower_bound(devices, device_number, std::ranges::less{},
+ &kernel::filesystem::device_number_registry::entry::number);
+ auto const end = std::ranges::end(devices);
+ auto const to_read = std::min(requested, static_cast<std::size_t>(std::ranges::distance(begin, end)));
+
+ if (to_read == 0)
+ {
+ return std::pair{0, position};
+ }
+
+ std::ranges::transform(begin, begin + to_read, std::ranges::begin(entries), [](auto const & entry) {
+ return directory_listing_entry{.name = entry.name,
+ .type = entry.type,
+ .inode_number = std::bit_cast<std::uint16_t>(entry.number)};
+ });
+
+ auto const last_copied = begin + to_read;
+ if (last_copied == end)
+ {
+ return std::pair{to_read, sentinel};
+ }
+
+ auto const new_device_number = last_copied->number;
+ auto const new_cursor = directory_listing_cursor{static_cast<std::uint64_t>(new_device_number.minor) |
+ (static_cast<std::uint64_t>(new_device_number.major) << 8)};
+ return std::pair{to_read, new_cursor};
+ }
} // namespace kernel::filesystem::devfs \ No newline at end of file
diff --git a/kernel/kernel/filesystem/devfs/inode.hpp b/kernel/kernel/filesystem/devfs/inode.hpp
index e907c02e..b83d484c 100644
--- a/kernel/kernel/filesystem/devfs/inode.hpp
+++ b/kernel/kernel/filesystem/devfs/inode.hpp
@@ -1,6 +1,8 @@
#ifndef TEACHOS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP
#define TEACHOS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP
+#include <kernel/filesystem/directory_listing_cursor.hpp>
+#include <kernel/filesystem/directory_listing_entry.hpp>
#include <kernel/filesystem/inode.hpp>
#include <kstd/result.hpp>
@@ -8,13 +10,14 @@
#include <cstddef>
#include <span>
+#include <utility>
namespace kernel::filesystem::devfs
{
//! Inode implementation for the devfs filesystem.
//!
//! This inode represents root device node in the /dev directory.
- struct inode : kernel::filesystem::inode
+ struct inode final : kernel::filesystem::inode
{
[[nodiscard]] auto read(std::span<std::byte> buffer, kstd::bytes offset) const
-> kstd::result<kstd::bytes> override;
@@ -22,6 +25,10 @@ namespace kernel::filesystem::devfs
auto write(std::span<std::byte const> buffer, kstd::bytes offset) -> kstd::result<kstd::bytes> override;
[[nodiscard]] auto is_directory() const -> bool override;
+
+ [[nodiscard]] auto read_directory(directory_listing_cursor position,
+ std::span<directory_listing_entry> entries) const
+ -> kstd::result<std::pair<std::size_t, directory_listing_cursor>> override;
};
} // namespace kernel::filesystem::devfs
diff --git a/kernel/kernel/filesystem/devfs/inode.tests.cpp b/kernel/kernel/filesystem/devfs/inode.tests.cpp
index 0d8ed5d1..581bf6c5 100644
--- a/kernel/kernel/filesystem/devfs/inode.tests.cpp
+++ b/kernel/kernel/filesystem/devfs/inode.tests.cpp
@@ -1,16 +1,42 @@
#include <kernel/filesystem/devfs/inode.hpp>
+#include <kernel/filesystem/device_inode.hpp>
+#include <kernel/filesystem/directory_listing_cursor.hpp>
+#include <kernel/filesystem/directory_listing_entry.hpp>
+#include <kernel/test_support/devices/block_device.hpp>
+#include <kernel/test_support/devices/driver.hpp>
+
+#include <kapi/devices.hpp>
+#include <kapi/filesystem.hpp>
+
#include <kstd/memory.hpp>
#include <kstd/print.hpp>
+#include <kstd/string.hpp>
#include <kstd/units.hpp>
#include <kstd/vector.hpp>
#include <catch2/catch_test_macros.hpp>
+#include <algorithm>
#include <cstddef>
+#include <tuple>
using namespace kstd::units_literals;
+namespace
+{
+ auto make_and_publish(kstd::shared_ptr<kapi::devices::bus> const & bus, kstd::string name)
+ -> kstd::shared_ptr<kapi::devices::device>
+
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>(name, 512_B);
+ bus->add_child(device);
+ REQUIRE(kernel::tests::devices::bind(*device));
+ REQUIRE(kapi::devices::publish_facet<kapi::filesystem::block_special_file>(device, name));
+ return device;
+ }
+} // namespace
+
SCENARIO("Devfs inode creation", "[filesystem][devfs][inode]")
{
GIVEN("a devfs inode")
@@ -56,3 +82,154 @@ SCENARIO("Devfs inode read/write", "[filesystem][devfs][inode]")
}
}
}
+
+SCENARIO("Devfs inode directory listing", "[filesystem][devfs][inode]")
+{
+ GIVEN("a devfs inode and no registered devices")
+ {
+ auto root = kernel::filesystem::devfs::inode{};
+
+ THEN("the directory listing is empty")
+ {
+ auto entries = kstd::vector<kernel::filesystem::directory_listing_entry>{4};
+ auto result = root.read_directory(kernel::filesystem::directory_listing_cursor{}, entries);
+
+ REQUIRE(result);
+ auto [count, next] = *result;
+ REQUIRE(count == 0);
+ }
+ }
+
+ GIVEN("a devfs inode and three registered devices")
+ {
+ auto root = kernel::filesystem::devfs::inode{};
+
+ auto bus = kstd::make_shared<kapi::devices::bus>("devfs_readdir_bus");
+ kapi::devices::get_root_bus()->add_child(bus);
+
+ auto device1 = make_and_publish(bus, "devfs_readdir_device1");
+ auto device2 = make_and_publish(bus, "devfs_readdir_device2");
+ auto device3 = make_and_publish(bus, "devfs_readdir_device3");
+
+ WHEN("the directory listing is requested with a buffer large enough for all of them")
+ {
+ auto entries = kstd::vector<kernel::filesystem::directory_listing_entry>{4};
+ auto result = root.read_directory(kernel::filesystem::directory_listing_cursor{}, entries);
+
+ THEN("all devices are listed")
+ {
+ REQUIRE(result);
+ auto [count, next] = *result;
+ REQUIRE(count == 3);
+
+ auto names = kstd::vector<kstd::string>{3};
+ for (auto i = 0uz; i < count; ++i)
+ {
+ REQUIRE(entries[i].type == kapi::filesystem::file_type::block_device);
+ names.push_back(entries[i].name);
+ }
+
+ REQUIRE(std::ranges::find(names, "devfs_readdir_device1") != names.end());
+ REQUIRE(std::ranges::find(names, "devfs_readdir_device2") != names.end());
+ REQUIRE(std::ranges::find(names, "devfs_readdir_device3") != names.end());
+
+ AND_THEN("listing again with the returned cursor reports 'end-of-directory'")
+ {
+ auto result = root.read_directory(next, entries);
+
+ REQUIRE(result);
+ auto [count, cursor] = *result;
+ REQUIRE(count == 0);
+ REQUIRE(cursor == next);
+ }
+ }
+ }
+
+ WHEN("read_directory is called one entry at a time, chaining the returned cursor")
+ {
+ auto seen = kstd::vector<kstd::string>{};
+ auto position = kernel::filesystem::directory_listing_cursor{};
+
+ for (auto guard = 0; guard < 4; ++guard)
+ {
+ auto one = kstd::vector<kernel::filesystem::directory_listing_entry>(1);
+ auto result = root.read_directory(position, one);
+ REQUIRE(result);
+ auto [count, next] = *result;
+ if (count == 0)
+ {
+ break;
+ }
+ seen.push_back(one[0].name);
+ position = next;
+ }
+
+ THEN("nothing is skipped or duplicated across the paginated calls")
+ {
+ REQUIRE(seen.size() == 3);
+ auto sorted = seen;
+ std::ranges::sort(sorted);
+ std::ignore = std::ranges::unique(sorted);
+ REQUIRE(sorted.size() == 3);
+ }
+ }
+
+ REQUIRE(kapi::devices::remove_device(*bus));
+ }
+}
+
+SCENARIO("Devfs inode read_directory orders by device number, not registration order",
+ "[filesystem][devfs][inode][readdir]")
+{
+ GIVEN("a device that frees the lowest minor number before a later device reuses it")
+ {
+ auto root = kernel::filesystem::devfs::inode{};
+
+ auto bus = kstd::make_shared<kapi::devices::bus>("devfs_readdir_reuse_bus");
+ kapi::devices::get_root_bus()->add_child(bus);
+
+ auto first_out = make_and_publish(bus, "readdir_reuse_first_out");
+ auto still_in = make_and_publish(bus, "readdir_reuse_still_in");
+ REQUIRE(kapi::devices::remove_device(*first_out));
+ auto later_in = make_and_publish(bus, "readdir_reuse_later_in");
+
+ WHEN("read_directory is called with room for both remaining entries")
+ {
+ auto entries = kstd::vector<kernel::filesystem::directory_listing_entry>(2);
+ auto result = root.read_directory(kernel::filesystem::directory_listing_cursor{}, entries);
+
+ THEN("later_in (the reused, lower device number) comes back before still_in, by number, not by "
+ "when each was registered")
+ {
+ REQUIRE(result);
+ auto [count, next] = *result;
+ REQUIRE(count == 2);
+ REQUIRE(entries[0].name == "readdir_reuse_later_in");
+ REQUIRE(entries[1].name == "readdir_reuse_still_in");
+ }
+ }
+
+ REQUIRE(kapi::devices::remove_device(*bus));
+ }
+}
+
+SCENARIO("Devfs read_directory rejects non-directory inodes", "[filesystem][devfs][inode][readdir]")
+{
+ GIVEN("a device_inode, which does not represent a directory")
+ {
+ auto bus = kstd::make_shared<kapi::devices::bus>("devfs_readdir_nondir_bus");
+ kapi::devices::get_root_bus()->add_child(bus);
+ auto device = make_and_publish(bus, "readdir_nondir_device");
+ auto node = kernel::filesystem::device_inode{device};
+
+ THEN("read_directory falls through to the base class default rather than enumerating anything")
+ {
+ auto entries = kstd::vector<kernel::filesystem::directory_listing_entry>(1);
+ auto result = node.read_directory(kernel::filesystem::directory_listing_cursor{}, entries);
+
+ REQUIRE_FALSE(result);
+ }
+
+ REQUIRE(kapi::devices::remove_device(*bus));
+ }
+} \ No newline at end of file
diff --git a/kernel/kernel/filesystem/devfs/module.cpp b/kernel/kernel/filesystem/devfs/module.cpp
new file mode 100644
index 00000000..a7a1aca9
--- /dev/null
+++ b/kernel/kernel/filesystem/devfs/module.cpp
@@ -0,0 +1,28 @@
+#include <kernel/filesystem/devfs/filesystem.hpp>
+#include <kernel/filesystem/driver_registry.hpp>
+#include <kernel/filesystem/filesystem.hpp>
+
+#include <kstd/memory.hpp>
+
+#include <string_view>
+
+namespace
+{
+
+ struct descriptor final : kernel::filesystem::driver_descriptor
+ {
+ [[nodiscard]] auto name() const noexcept -> std::string_view override
+ {
+ return "devfs";
+ }
+
+ [[nodiscard]] auto make_instance() const -> kernel::filesystem::filesystem::filesystem_ptr override
+ {
+ return kstd::make_shared<kernel::filesystem::devfs::filesystem>();
+ }
+ };
+
+ [[gnu::used]]
+ constexpr auto registration = kernel::filesystem::driver_module<descriptor>{};
+
+} // namespace \ No newline at end of file
diff --git a/kernel/kernel/filesystem/device_number_registry.cpp b/kernel/kernel/filesystem/device_number_registry.cpp
index 42663dba..64cdb062 100644
--- a/kernel/kernel/filesystem/device_number_registry.cpp
+++ b/kernel/kernel/filesystem/device_number_registry.cpp
@@ -1,7 +1,6 @@
#include <kernel/filesystem/device_number_registry.hpp>
#include <kernel/filesystem/error.hpp>
-#include <kernel/filesystem/file_type.hpp>
#include <kernel/filesystem/file_type_bindings.hpp>
#include <kapi/devices.hpp>
diff --git a/kernel/kernel/filesystem/device_number_registry.hpp b/kernel/kernel/filesystem/device_number_registry.hpp
index 2391f179..2f0f0226 100644
--- a/kernel/kernel/filesystem/device_number_registry.hpp
+++ b/kernel/kernel/filesystem/device_number_registry.hpp
@@ -1,8 +1,6 @@
#ifndef TEACHOS_KERNEL_FILESYSTEM_DEVICE_NUMBER_REGISTRY_HPP
#define TEACHOS_KERNEL_FILESYSTEM_DEVICE_NUMBER_REGISTRY_HPP
-#include <kernel/filesystem/file_type.hpp>
-
#include <kapi/devices.hpp>
#include <kapi/filesystem.hpp>
#include <kapi/tracked_mutex.hpp>
@@ -23,6 +21,8 @@ namespace kernel::filesystem
//! A registry to assign stable device numbers to published devices.
struct device_number_registry final : kapi::devices::facet_registry_observer
{
+ using file_type = kapi::filesystem::file_type;
+
//! A single numbered device.
//!
//! Theses entries are used, for example, in devfs to provide access to devices.
diff --git a/kernel/kernel/filesystem/device_number_registry.tests.cpp b/kernel/kernel/filesystem/device_number_registry.tests.cpp
index 913acf9a..60e17337 100644
--- a/kernel/kernel/filesystem/device_number_registry.tests.cpp
+++ b/kernel/kernel/filesystem/device_number_registry.tests.cpp
@@ -1,6 +1,5 @@
#include <kernel/filesystem/device_number_registry.hpp>
-#include <kernel/filesystem/file_type.hpp>
#include <kernel/test_support/devices/block_device.hpp>
#include <kernel/test_support/devices/driver.hpp>
@@ -66,7 +65,7 @@ SCENARIO("Device number registry resolves and numbers published block devices",
AND_THEN("resolve() finds the device by number")
{
- REQUIRE(registry.resolve(all[0].number, kernel::filesystem::file_type::block));
+ REQUIRE(registry.resolve(all[0].number, kapi::filesystem::file_type::block_device));
}
AND_THEN("number_of() finds the device number")
@@ -79,7 +78,7 @@ SCENARIO("Device number registry resolves and numbers published block devices",
auto number = registry.number_of(*device);
CHECK(number);
- auto resolved = registry.resolve(*number, kernel::filesystem::file_type::block);
+ auto resolved = registry.resolve(*number, kapi::filesystem::file_type::block_device);
CHECK(resolved);
REQUIRE(resolved == device);
@@ -105,7 +104,7 @@ SCENARIO("Device number registry resolves and numbers published block devices",
THEN("resolve() does not report it anymore")
{
- REQUIRE(!registry.resolve(*number, kernel::filesystem::file_type::block));
+ REQUIRE(!registry.resolve(*number, kapi::filesystem::file_type::block_device));
}
}
}
diff --git a/kernel/kernel/filesystem/directory_listing_cursor.hpp b/kernel/kernel/filesystem/directory_listing_cursor.hpp
new file mode 100644
index 00000000..5eb36c7a
--- /dev/null
+++ b/kernel/kernel/filesystem/directory_listing_cursor.hpp
@@ -0,0 +1,23 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_DIRECTORY_LISTING_CURSOR_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_DIRECTORY_LISTING_CURSOR_HPP
+
+#include <compare>
+#include <cstdint>
+
+namespace kernel::filesystem
+{
+
+ //! A cursor used to keep track of positions in directory listings.
+ struct directory_listing_cursor
+ {
+ constexpr auto friend operator<=>(directory_listing_cursor const &, directory_listing_cursor const &) noexcept
+ -> std::strong_ordering = default;
+ constexpr auto friend operator==(directory_listing_cursor const &, directory_listing_cursor const &) noexcept
+ -> bool = default;
+
+ std::uint64_t value;
+ };
+
+}; // namespace kernel::filesystem
+
+#endif \ No newline at end of file
diff --git a/kernel/kernel/filesystem/directory_listing_entry.hpp b/kernel/kernel/filesystem/directory_listing_entry.hpp
new file mode 100644
index 00000000..f3dcf367
--- /dev/null
+++ b/kernel/kernel/filesystem/directory_listing_entry.hpp
@@ -0,0 +1,23 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_DIRECTORY_LISTING_ENTRY_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_DIRECTORY_LISTING_ENTRY_HPP
+
+#include <kapi/filesystem.hpp>
+
+#include <kstd/string.hpp>
+
+#include <cstdint>
+
+namespace kernel::filesystem
+{
+
+ //! An entry in a directory listing.
+ struct directory_listing_entry
+ {
+ kstd::string name;
+ kapi::filesystem::file_type type;
+ std::uint64_t inode_number;
+ };
+
+}; // namespace kernel::filesystem
+
+#endif \ No newline at end of file
diff --git a/kernel/kernel/filesystem/driver_registry.cpp b/kernel/kernel/filesystem/driver_registry.cpp
new file mode 100644
index 00000000..9fa2d474
--- /dev/null
+++ b/kernel/kernel/filesystem/driver_registry.cpp
@@ -0,0 +1,130 @@
+#include <kernel/filesystem/driver_registry.hpp>
+
+#include <kernel/filesystem/error.hpp>
+#include <kernel/filesystem/filesystem.hpp>
+#include <kernel/filesystem/inode.hpp>
+
+#include <kapi/system.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/print.hpp>
+#include <kstd/result.hpp>
+#include <kstd/vector.hpp>
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <optional>
+#include <ranges>
+#include <span>
+#include <string_view>
+#include <utility>
+
+namespace kernel::filesystem
+{
+
+ extern "C"
+ {
+ // We need to suppress clang-tidy linting warnings here, since these symbols are generated by the linker and we
+ // cannot choose their names, unless we wanted to extend the linker script needlessly.
+ // NOLINTBEGIN(readability-identifier-naming)
+ extern kstd::observer_ptr<driver_descriptor const> const __start_filesystem_drivers;
+ extern kstd::observer_ptr<driver_descriptor const> const __stop_filesystem_drivers;
+ // NOLINTEND(readability-identifier-naming)
+ }
+
+ namespace
+ {
+ auto constinit instance = std::optional<driver_registry>{};
+ }
+
+ auto driver_registry::init() -> void
+ {
+ if (instance)
+ {
+ kapi::system::panic("[OS:FS] The filesystem driver registry was already initialized!");
+ }
+
+ instance.emplace();
+
+ auto instances =
+ std::span{&__start_filesystem_drivers, &__stop_filesystem_drivers} |
+ std::views::filter([](auto descriptor) { return descriptor != nullptr; }) |
+ std::views::transform([](auto descriptor) { return std::pair{descriptor, descriptor->make_instance()}; }) |
+ std::views::filter([](auto entry) { return entry.second != nullptr; });
+
+ std::ranges::for_each(instances, [](auto entry) {
+ auto [descriptor, driver] = entry;
+ kstd::println("[OS:FS] Registering filesystem driver '{}'", descriptor->name());
+ instance->add(descriptor->name(), driver);
+ });
+ }
+
+ auto driver_registry::get() -> driver_registry &
+ {
+ if (!instance)
+ {
+ kapi::system::panic("[OS:FS] The filesystem driver registry has not been initialized!");
+ }
+
+ return *instance;
+ }
+
+ auto driver_registry::find(std::string_view name) const -> std::optional<kstd::shared_ptr<filesystem>>
+ {
+ if (auto it = m_drivers.find(name); it != m_drivers.end())
+ {
+ return it->second;
+ }
+ return std::nullopt;
+ }
+
+ auto driver_registry::add(std::string_view name, kstd::shared_ptr<filesystem> driver) -> bool
+ {
+ auto result = m_drivers.emplace(name, driver);
+ if (!result.second)
+ {
+ kstd::println(kstd::print_sink::stderr, "[OS:FS] Tried to register duplicate filesystem driver '{}'!", name);
+ }
+
+ return result.second;
+ }
+
+ auto driver_registry::match(kstd::shared_ptr<inode> const & inode) const -> kstd::result<kstd::shared_ptr<filesystem>>
+ {
+ struct candidate
+ {
+ std::uint32_t priority;
+ kstd::shared_ptr<filesystem> driver;
+ };
+
+ auto candidates = kstd::vector<candidate>{};
+
+ for (auto const & entry : m_drivers)
+ {
+ auto const & [name, driver] = entry;
+ auto priority = driver->probe(inode);
+ if (priority)
+ {
+ candidates.push_back({*priority, driver});
+ }
+ }
+
+ std::ranges::stable_sort(candidates, std::ranges::greater{}, &candidate::priority);
+
+ if (candidates.empty())
+ {
+ return kstd::failure(vfs_errc::unsupported_filesystem);
+ }
+
+ return candidates.front().driver;
+ }
+} // namespace kernel::filesystem
+
+namespace kernel::tests::filesystem::driver_registry
+{
+ auto deinit() -> void
+ {
+ kernel::filesystem::instance.reset();
+ }
+} // namespace kernel::tests::filesystem::driver_registry \ No newline at end of file
diff --git a/kernel/kernel/filesystem/driver_registry.hpp b/kernel/kernel/filesystem/driver_registry.hpp
new file mode 100644
index 00000000..1a6cd47e
--- /dev/null
+++ b/kernel/kernel/filesystem/driver_registry.hpp
@@ -0,0 +1,73 @@
+#ifndef TEACHOS_KERNEL_DRIVER_REGISTRY_HPP
+#define TEACHOS_KERNEL_DRIVER_REGISTRY_HPP
+
+#include <kernel/filesystem/filesystem.hpp>
+#include <kernel/filesystem/inode.hpp>
+
+#include <kstd/flat_map.hpp>
+#include <kstd/memory.hpp>
+#include <kstd/result.hpp>
+#include <kstd/string.hpp>
+#include <kstd/vector.hpp>
+
+#include <concepts>
+#include <optional>
+#include <string_view>
+
+namespace kernel::filesystem
+{
+
+ struct driver_descriptor
+ {
+ virtual ~driver_descriptor() = default;
+
+ //! Get the name of the driver.
+ [[nodiscard]] virtual auto name() const noexcept -> std::string_view = 0;
+
+ //! Create a new instance of the driver represented by this descriptor.
+ [[nodiscard]] virtual auto make_instance() const -> kstd::shared_ptr<filesystem> = 0;
+ };
+
+ template<std::derived_from<driver_descriptor> Type>
+ struct driver_module
+ {
+ constexpr auto static instance = Type{};
+ [[using gnu: section("filesystem_drivers"), used, visibility("hidden")]] constexpr auto static pointer{
+ kstd::make_observer<driver_descriptor const>(&instance),
+ };
+ };
+
+ struct driver_registry
+ {
+ constexpr driver_registry() noexcept = default;
+
+ auto static init() -> void;
+
+ auto static get() -> driver_registry &;
+
+ //! Register a new filesystem.
+ //!
+ //! @param name The name of the filesystem.
+ //! @param driver The filesystem driver to register.
+ //! @return @c true iff. the driver was successfully added, @c false if not.
+ auto add(std::string_view name, kstd::shared_ptr<filesystem> driver) -> bool;
+
+ //! Find a registered filesystem driver
+ //!
+ //! @param name The name of the filesystem
+ [[nodiscard]] auto find(std::string_view name) const -> std::optional<kstd::shared_ptr<filesystem>>;
+
+ //! Match all currently registered filesystem drivers against a given inode.
+ //!
+ //! @param inode The inode to match on, if any.
+ //! @return a match priority if th
+ [[nodiscard]] auto match(kstd::shared_ptr<inode> const & inode) const -> kstd::result<kstd::shared_ptr<filesystem>>;
+
+ private:
+ //! A map from filesystem names to filesystem drivers.
+ kstd::flat_map<std::string_view, kstd::shared_ptr<filesystem>> m_drivers{};
+ };
+
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/kernel/filesystem/error.cpp b/kernel/kernel/filesystem/error.cpp
index 80ad1097..0d6ae01e 100644
--- a/kernel/kernel/filesystem/error.cpp
+++ b/kernel/kernel/filesystem/error.cpp
@@ -54,6 +54,10 @@ namespace kernel::filesystem
return "no such device";
case vfs_errc::invalid_argument:
return "invalid argument";
+ case vfs_errc::not_mounted:
+ return "filesystem is not mounted";
+ case vfs_errc::invalid_driver_data:
+ return "invalid driver data";
default:
return "unknown VFS error";
}
@@ -67,6 +71,7 @@ namespace kernel::filesystem
case vfs_errc::invalid_filesystem:
case vfs_errc::invalid_inode:
case vfs_errc::invalid_argument:
+ case vfs_errc::invalid_driver_data:
return make_error_condition(kstd::errc::invalid_argument);
case vfs_errc::no_such_file_or_directory:
case vfs_errc::mount_point_not_found:
@@ -91,6 +96,8 @@ namespace kernel::filesystem
return make_error_condition(kstd::errc::file_exists);
case vfs_errc::no_such_device:
return make_error_condition(kstd::errc::no_such_device);
+ case vfs_errc::not_mounted:
+ return make_error_condition(kstd::errc::not_connected);
default:
return kstd::error_condition{value, *this};
}
diff --git a/kernel/kernel/filesystem/error.hpp b/kernel/kernel/filesystem/error.hpp
index 83b11902..6b167355 100644
--- a/kernel/kernel/filesystem/error.hpp
+++ b/kernel/kernel/filesystem/error.hpp
@@ -27,6 +27,8 @@ namespace kernel::filesystem
file_exists,
no_such_device,
invalid_argument,
+ not_mounted,
+ invalid_driver_data,
};
[[nodiscard]] auto vfs_category() noexcept -> kstd::error_category const &;
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.cpp b/kernel/kernel/filesystem/ext2/directory_iterator.cpp
new file mode 100644
index 00000000..3e614f26
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.cpp
@@ -0,0 +1,91 @@
+#include <kernel/filesystem/ext2/directory_iterator.hpp>
+
+#include <kernel/filesystem/ext2/filesystem.hpp>
+#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
+
+#include <kapi/system.hpp>
+
+#include <kstd/units.hpp>
+#include <kstd/vector.hpp>
+
+namespace kernel::filesystem::ext2
+{
+
+ directory_iterator::directory_iterator(inode const & inode, filesystem const & filesystem, mount_state & state)
+ : m_inode{&inode}
+ , m_filesystem{&filesystem}
+ , m_state{&state}
+ , 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_filesystem->data_size(*m_inode, *m_state))
+ {
+ 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_filesystem->do_read(*m_inode, m_buffer, m_file_offset, *m_state); !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_filesystem->do_read(*m_inode, m_buffer, m_file_offset, *m_state); !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..4e2fd9d8
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.hpp
@@ -0,0 +1,51 @@
+#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 <kernel/filesystem/ext2/mount_state.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;
+ directory_iterator(inode const & inode, filesystem const & filesystem, mount_state & state);
+
+ 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::observer_ptr<filesystem const> m_filesystem{};
+ kstd::observer_ptr<mount_state> m_state{};
+ 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..2d817e0b
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp
@@ -0,0 +1,123 @@
+#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/ext2/mount_state.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);
+
+ auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>((*mount)->driver_data());
+
+ 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, *fs, *mount_state};
+ 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 mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>((*mount)->driver_data());
+
+ auto original = kernel::filesystem::ext2::directory_iterator{root_ext2_inode, *fs, *mount_state};
+ 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/error.cpp b/kernel/kernel/filesystem/ext2/error.cpp
index ffe49460..73921379 100644
--- a/kernel/kernel/filesystem/ext2/error.cpp
+++ b/kernel/kernel/filesystem/ext2/error.cpp
@@ -42,6 +42,8 @@ namespace kernel::filesystem::ext2
return "not enough inodes";
case ext2_errc::unsupported_features_present:
return "unsupported features present";
+ case ext2_errc::name_too_long:
+ return "name too long";
default:
return "unknown ext2 error";
};
@@ -54,6 +56,7 @@ namespace kernel::filesystem::ext2
{
case ext2_errc::invalid_magic_number:
case ext2_errc::invalid_root_inode:
+ case ext2_errc::name_too_long:
if (condition.category() == kernel::filesystem::vfs_category())
{
return condition.value() == static_cast<int>(kernel::filesystem::vfs_errc::invalid_filesystem);
diff --git a/kernel/kernel/filesystem/ext2/error.hpp b/kernel/kernel/filesystem/ext2/error.hpp
index 59df1669..edd0d017 100644
--- a/kernel/kernel/filesystem/ext2/error.hpp
+++ b/kernel/kernel/filesystem/ext2/error.hpp
@@ -21,7 +21,8 @@ namespace kernel::filesystem::ext2
failed_to_read_block_group_descriptors,
not_enough_free_blocks,
not_enough_inodes,
- unsupported_features_present
+ unsupported_features_present,
+ name_too_long,
};
[[nodiscard]] auto ext2_category() noexcept -> kstd::error_category const &;
diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp
index fcccad8f..bf977c46 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.cpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.cpp
@@ -2,24 +2,29 @@
#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>
+#include <kernel/filesystem/ext2/mount_state.hpp>
#include <kernel/filesystem/ext2/superblock.hpp>
+#include <kernel/filesystem/ext2/write_batch.hpp>
#include <kernel/filesystem/filesystem.hpp>
#include <kernel/filesystem/inode.hpp>
-#include <kernel/filesystem/type.hpp>
#include <kapi/filesystem.hpp>
#include <kapi/system.hpp>
#include <kstd/cstring.hpp>
#include <kstd/memory.hpp>
+#include <kstd/mutex.hpp>
#include <kstd/result.hpp>
+#include <kstd/span.hpp>
#include <kstd/system_error.hpp>
#include <kstd/units.hpp>
#include <kstd/vector.hpp>
+#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
@@ -27,178 +32,201 @@
#include <optional>
#include <span>
#include <string_view>
-#include <tuple>
#include <utility>
using namespace kstd::units_literals;
namespace kernel::filesystem::ext2
{
+
+ //! Check that this driver class is stateless.
+ static_assert(sizeof(kernel::filesystem::filesystem) == sizeof(kernel::filesystem::ext2::filesystem));
+
namespace
{
- [[nodiscard]] auto bitmap_is_set(kstd::vector<uint8_t> const & bitmap, size_t index) -> bool
+ struct indirect_level
{
- auto const byte_index = index / 8;
- auto const bit_index = index % 8;
- return (bitmap.at(byte_index) & static_cast<uint8_t>(1u << bit_index)) != 0;
- }
+ uint32_t slot_index;
+ size_t capacity;
+ };
- auto bitmap_set(kstd::vector<uint8_t> & bitmap, size_t index) -> void
+ //! Check if the given bit has the bit at the given index set.
+ //!
+ //! @warning This function will panic if the bit index is out-of-bounds for the given bitmap.
+ //!
+ //! @param bitmap The bitmap to check.
+ //! @param index The index of the bit to check.
+ [[nodiscard]] auto bitmap_is_set(kstd::vector<std::byte> const & bitmap, size_t index) -> bool
{
auto const byte_index = index / 8;
auto const bit_index = index % 8;
- bitmap.at(byte_index) |= static_cast<uint8_t>(1u << bit_index);
+ return (bitmap.at(byte_index) & static_cast<std::byte>(1u << bit_index)) != std::byte{};
}
- auto supported_incompatible_features = incompatible_features::file_types_in_directory_entries;
- auto supported_ro_compatible_features = read_only_compatible_features::sparse_superblock_copies | //
- read_only_compatible_features::large_file_support;
- } // namespace
-
- struct type final : kernel::filesystem::type
- {
- [[nodiscard]] auto name() const noexcept -> std::string_view override
+ //! Set the bit at the given index in the given bitmap.
+ //!
+ //! @warning This function will panic if the bit index is out-of-bounds for the given bitmap.
+ //!
+ //! @param bitmap The bitmap to manipulate.
+ //! @param index The index of the bit to set.
+ auto bitmap_set(kstd::vector<std::byte> & bitmap, size_t index) -> void
{
- return "ext2";
+ auto const byte_index = index / 8;
+ auto const bit_index = index % 8;
+ bitmap.at(byte_index) |= static_cast<std::byte>(1u << bit_index);
}
- [[nodiscard]] auto probe(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) const noexcept
- -> kstd::result<std::uint32_t> override
+ //! Get the number of block numbers storable on each level
+ //!
+ //! @param level The level to check.
+ //! @param block_size The size of a single block.
+ [[nodiscard]] constexpr auto block_numbers(std::size_t level, kstd::bytes block_size) noexcept -> std::size_t
{
- auto superblock = ext2::superblock{};
- auto bytes = std::as_writable_bytes(std::span{&superblock, 1});
- auto read_result = backing_inode->read(bytes, constants::superblock_offset);
-
- if (!read_result)
- {
- return kstd::failure(read_result.error());
- }
-
- if (superblock.magic != constants::magic_number)
+ if (level == 0)
{
- return kstd::failure(ext2_errc::invalid_magic_number);
+ return constants::direct_block_count;
}
- if (superblock.feature_incompat & std::to_underlying(~supported_incompatible_features) ||
- superblock.feature_ro_compat & std::to_underlying(~supported_ro_compatible_features))
+ auto const numbers_per_block = block_size / kstd::size_of<std::uint32_t>();
+ auto result = numbers_per_block;
+ for (auto current_level = level; current_level > 1; --current_level)
{
- return kstd::failure(ext2_errc::unsupported_features_present);
+ result *= numbers_per_block;
}
- return 0;
+ return result;
}
- [[nodiscard]] auto make_instance() const -> kstd::shared_ptr<kernel::filesystem::filesystem> override
+ [[nodiscard]] constexpr auto indirect_levels(kstd::bytes block_size) -> std::array<indirect_level, 3>
{
- return kstd::make_shared<filesystem>();
+ return {
+ {{constants::singly_indirect_block_index, block_numbers(1, block_size)},
+ {constants::doubly_indirect_block_index, block_numbers(2, block_size)},
+ {constants::triply_indirect_block_index, block_numbers(3, block_size)}}
+ };
}
- };
- [[gnu::used]]
- constexpr auto registration = type_registration<type>{};
+ //! The set of "incompatible" Extended Filesystem features supported by this driver.
+ constexpr auto supported_incompatible_features = incompatible_features::file_types_in_directory_entries;
- auto filesystem::indirect_levels() const -> std::array<indirect_level, 3>
+ //! The set of "read-only" Extended Filesystem features supported by this driver.
+ constexpr auto supported_ro_compatible_features = read_only_compatible_features::sparse_superblock_copies | //
+ read_only_compatible_features::large_file_support;
+
+ //! A set of permissions allowing all access to everyone.
+ constexpr auto no_access_restrictions = std::uint16_t{0x01FF};
+ } // namespace
+
+ auto filesystem::probe(inode_ptr const & backing_inode) const -> kstd::result<std::uint32_t>
{
- return {
- {{constants::singly_indirect_block_index, block_numbers_per_singly_indirect_block()},
- {constants::doubly_indirect_block_index, block_numbers_per_doubly_indirect_block()},
- {constants::triply_indirect_block_index, block_numbers_per_triply_indirect_block()}}
- };
+ auto superblock = ext2::superblock{};
+ auto const bytes = kstd::raw_bytes(superblock);
+ auto const read_result = backing_inode->read(bytes, constants::superblock_offset);
+
+ if (!read_result)
+ {
+ return kstd::failure(read_result.error());
+ }
+
+ if (superblock.magic != constants::magic_number)
+ {
+ return kstd::failure(ext2_errc::invalid_magic_number);
+ }
+
+ if (superblock.feature_incompat & std::to_underlying(~supported_incompatible_features) ||
+ superblock.feature_ro_compat & std::to_underlying(~supported_ro_compatible_features))
+ {
+ return kstd::failure(ext2_errc::unsupported_features_present);
+ }
+
+ return 0;
}
- auto filesystem::mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) -> kstd::result<void>
+ auto filesystem::mount(inode_ptr const & backing_inode) -> kstd::result<mount_result>
{
- std::ignore = kernel::filesystem::filesystem::mount(backing_inode);
+ auto const mount_state = kstd::make_shared<struct mount_state>();
+ auto & [superblock, block_group_descriptors, backing, _] = *mount_state;
- if (auto read_result =
- m_backing_inode->read(std::as_writable_bytes(std::span{&m_superblock, 1}), constants::superblock_offset);
- !read_result)
+ backing = backing_inode;
+
+ auto const superblock_bytes = kstd::raw_bytes(superblock);
+ if (auto read_result = backing->read(superblock_bytes, constants::superblock_offset); !read_result)
{
return kstd::failure(read_result.error());
}
- if (m_superblock.magic != constants::magic_number)
+ if (superblock.magic != constants::magic_number)
{
return kstd::failure(ext2_errc::invalid_magic_number);
}
- auto const blocks_per_group = m_superblock.blocks_per_group;
- auto const num_block_groups = (m_superblock.blocks_count + blocks_per_group - 1) / blocks_per_group;
+ auto const blocks_per_group = superblock.blocks_per_group;
+ auto const num_block_groups = (superblock.blocks_count + blocks_per_group - 1) / blocks_per_group;
- m_block_group_descriptors = kstd::vector<block_group_descriptor>(num_block_groups);
-
- if (auto read_result = m_backing_inode->read(std::as_writable_bytes(std::span{m_block_group_descriptors}),
- block_group_descriptor_table_offset());
+ block_group_descriptors = kstd::vector<block_group_descriptor>(num_block_groups);
+ auto const block_group_descriptors_bytes = kstd::raw_bytes(block_group_descriptors);
+ if (auto read_result =
+ backing->read(block_group_descriptors_bytes, block_group_descriptor_table_offset(*mount_state));
!read_result)
{
return kstd::failure(read_result.error());
}
- if (auto root_inode = read_inode(constants::root_inode_number))
+ auto const root = read_inode(constants::root_inode_number, *mount_state);
+
+ if (!root)
{
- m_root_inode = root_inode.value();
+ return kstd::failure(root.error());
}
- if (!m_root_inode || !m_root_inode->is_directory())
+ if (!(*root)->is_directory())
{
return kstd::failure(ext2_errc::invalid_root_inode);
}
- return {};
+ return mount_result{*root, mount_state};
}
- auto filesystem::lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
+ auto filesystem::lookup(inode_ptr const & parent, std::string_view name, driver_data_ptr driver_data) const
+ -> kstd::result<inode_ptr>
{
- if (!parent)
+ auto const directory = static_pointer_cast<inode>(parent);
+ if (!directory)
{
return kstd::failure(vfs_errc::invalid_inode);
}
- if (!parent->is_directory())
+ if (!directory->is_directory())
{
return kstd::failure(vfs_errc::not_a_directory);
}
- auto * ext2_parent = static_cast<inode *>(parent.get());
- auto const & inode_data = ext2_parent->data();
- kstd::vector<uint8_t> buffer(block_size().value);
-
- for (uint32_t i = 0; i < inode_block_count(inode_data); ++i)
+ auto const mount_state = static_pointer_cast<struct mount_state>(driver_data);
+ if (!mount_state)
{
- auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data);
- if (!global_block_number)
- {
- return kstd::failure(global_block_number.error());
- }
+ return kstd::failure(vfs_errc::not_mounted);
+ }
+
+ auto guard = kstd::lock_guard{mount_state->lock};
- if (auto read_result = read_block(*global_block_number, buffer.data()); !read_result)
+ for (auto it = directory_iterator{*directory, *this, *mount_state}; it != directory_iterator{}; ++it)
+ {
+ if (it->inode == 0)
{
- return kstd::failure(read_result.error());
+ continue;
}
- auto const * entry = reinterpret_cast<linked_directory_entry const *>(buffer.data());
- auto bytes_read = 0_B;
-
- while (bytes_read < block_size() && entry->inode != 0)
+ if (it->name() == name)
{
- auto const entry_name = std::string_view{entry->name.data(), entry->name_len};
- if (entry_name == name)
- {
- return read_inode(entry->inode);
- }
-
- bytes_read += kstd::bytes{entry->rec_len};
- entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + bytes_read);
+ return read_inode(it->inode, *mount_state);
}
}
return kstd::failure(vfs_errc::no_such_file_or_directory);
}
- auto filesystem::create_inode(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name,
- kapi::filesystem::file_type type,
- std::optional<kapi::filesystem::device_number> raw_device)
+ auto filesystem::create_inode(inode_ptr const & parent, std::string_view name, kapi::filesystem::file_type type,
+ driver_data_ptr driver_data, std::optional<kapi::filesystem::device_number> raw_device)
-> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
{
if (!parent)
@@ -211,20 +239,28 @@ namespace kernel::filesystem::ext2
return kstd::failure(vfs_errc::not_a_directory);
}
- auto is_device = kapi::filesystem::is_device(type);
-
+ auto const is_device = kapi::filesystem::is_device(type);
if (is_device && !raw_device)
{
return kstd::failure(vfs_errc::invalid_argument);
}
- auto * ext2_parent = static_cast<inode *>(parent.get());
+ auto const ext2_parent = static_pointer_cast<inode>(parent);
if (!ext2_parent)
{
- return nullptr;
+ return kstd::failure(vfs_errc::invalid_argument);
}
- auto maybe_inode_number = allocate_inode();
+ auto const mount_state = static_pointer_cast<struct mount_state>(driver_data);
+ if (!mount_state)
+ {
+ return kstd::failure(vfs_errc::invalid_driver_data);
+ }
+
+ auto guard = kstd::lock_guard{mount_state->lock};
+ auto batch = write_batch{*this, *mount_state};
+
+ auto maybe_inode_number = allocate_inode(*mount_state, batch);
if (!maybe_inode_number)
{
return kstd::failure(maybe_inode_number.error());
@@ -232,63 +268,105 @@ namespace kernel::filesystem::ext2
auto inode_number = maybe_inode_number.value();
- inode_data new_inode_data{};
+ auto data = inode_data{};
- uint16_t const full_access_mode = 0x01FF; // TODO use correct access rights
- new_inode_data.mode = map_vfs_inode_type_into_inode_mode(type, full_access_mode);
- new_inode_data.size = 0;
- new_inode_data.dir_acl = 0;
- new_inode_data.links_count = 1;
- new_inode_data.blocks = 0;
- new_inode_data.block.fill(0);
+ // TODO: use correct access rights
+ data.mode = map_vfs_inode_type_into_inode_mode(type, no_access_restrictions);
+ data.size = 0;
+ data.dir_acl = 0;
+ data.links_count = 1;
+ data.blocks = 0;
+ data.block.fill(0);
if (is_device)
{
- new_inode_data.block[0] = static_cast<std::uint32_t>(raw_device->minor) | //
- (static_cast<std::uint32_t>(raw_device->major) << 8);
+ data.block[0] = static_cast<std::uint32_t>(raw_device->minor) | //
+ (static_cast<std::uint32_t>(raw_device->major) << 8);
}
- // TODO preallocate blocks depending on type and m_superblock.prealloc_dir_blocks or m_superblock.prealloc_blocks;
+ auto created = kstd::make_shared<inode>(inode_number, data);
+ created->set_owning_mount(ext2_parent->owning_mount().lock());
- if (auto result = add_directory_entry(ext2_parent, name, inode_number, new_inode_data.mode); !result)
+ // TODO: preallocate blocks depending on type and m_superblock.prealloc_dir_blocks or m_superblock.prealloc_blocks;
+ if (auto result = add_directory_entry(*ext2_parent, name, *created, data.mode, *mount_state, batch); !result)
{
return kstd::failure(result.error());
}
- auto new_inode = kstd::make_shared<inode>(this, inode_number, new_inode_data);
-
- if (new_inode->is_directory())
+ if (created->is_directory())
{
// TODO increment used_dirs_count in block group descriptor
- if (auto result = init_directory(new_inode, ext2_parent); !result)
+ if (auto result = init_directory(*created, *ext2_parent, *mount_state, batch); !result)
{
return kstd::failure(result.error());
}
+ set_data_size(*created, block_size(*mount_state), *mount_state);
+ }
+
+ batch.mark_inode(*created);
+
+ return created;
+ }
+
+ auto filesystem::block_size(mount_state const & state) const -> kstd::bytes
+ {
+ return kstd::bytes{constants::base_block_size.value << state.superblock.log_block_size};
+ }
+
+ auto filesystem::revision_level(mount_state const & state) const -> uint32_t
+ {
+ return state.superblock.rev_level;
+ }
+
+ auto filesystem::inode_size(mount_state const & state) const -> kstd::bytes
+ {
+ return kstd::bytes(revision_level(state) == constants::good_old_revision ? 128 : state.superblock.inode_size);
+ }
+
+ auto filesystem::block_count(inode const & inode, mount_state const & state) const -> uint32_t
+ {
+ return inode.data().blocks / (2 << state.superblock.log_block_size);
+ }
+
+ auto filesystem::read_block(uint32_t block_number, std::span<std::byte> buffer, mount_state const & state) const
+ -> kstd::result<kstd::bytes>
+ {
+ if (buffer.size() < block_size(state).value)
+ {
+ return kstd::failure(vfs_errc::invalid_argument);
}
- if (auto write_result = write_inode(inode_number, new_inode->data()); !write_result)
+ auto const block_offset = static_cast<size_t>(block_number) * block_size(state);
+ return state.backing_inode->read(buffer, block_offset);
+ }
+
+ auto filesystem::write_block(uint32_t block_number, std::span<std::byte const> buffer, mount_state & state)
+ -> kstd::result<kstd::bytes>
+ {
+ if (buffer.size() < block_size(state).value)
{
- return kstd::failure(write_result.error());
+ return kstd::failure(vfs_errc::invalid_argument);
}
- return new_inode;
+ auto const block_offset = static_cast<size_t>(block_number) * block_size(state);
+ return state.backing_inode->write(buffer, block_offset);
}
- auto filesystem::add_directory_entry(inode * ext2_parent, std::string_view name, uint32_t child_inode_number,
- [[maybe_unused]] uint16_t mode) -> kstd::result<void>
+ auto filesystem::add_directory_entry(inode & directory, std::string_view name, inode & child, std::uint16_t mode,
+ mount_state & state, write_batch & batch) -> kstd::result<void>
{
- auto const last_block_index = inode_block_count(ext2_parent->data()) - 1;
- auto const global_block = map_inode_block_index_to_global_block_number(last_block_index, ext2_parent->data());
+ auto const last_block_index = block_count(directory, state) - 1;
+ auto const global_block = inode_block_number(last_block_index, directory, state);
if (!global_block)
{
return kstd::failure(global_block.error());
}
- kstd::vector<uint8_t> buffer(block_size().value);
- if (auto read_result = read_block(*global_block, buffer.data()); !read_result)
+ auto buffer = kstd::vector<std::byte>{block_size(state).value};
+ if (auto result = read_block(*global_block, buffer, state); !result)
{
- return kstd::failure(read_result.error());
+ return kstd::failure(result.error());
}
// TODO handle "gaps" in directory entries (e.g., after deletions) in the current implementation, we only
@@ -297,7 +375,7 @@ namespace kernel::filesystem::ext2
while (true)
{
auto const * entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + offset);
- if (offset + kstd::bytes{entry->rec_len} >= block_size())
+ if (offset + kstd::bytes{entry->rec_len} >= block_size(state))
{
break;
}
@@ -314,98 +392,154 @@ namespace kernel::filesystem::ext2
if (kstd::bytes{last_entry->rec_len} - last_entry_actual_len >= needed_rec_len)
{
last_entry->rec_len = last_entry_actual_len.value;
- write_directory_entry_to_buffer(buffer.data(), offset + last_entry_actual_len, child_inode_number, name,
- file_type);
- return global_block.and_then([&](auto block_number) { return write_block(block_number, buffer.data()); })
+ auto entry_offset = offset + last_entry_actual_len;
+ auto entry = write_directory_entry_to_buffer(buffer, entry_offset, child, name, file_type);
+ if (!entry)
+ {
+ return kstd::failure(entry.error());
+ }
+
+ auto remainder = block_size(state) - entry_offset;
+ (*entry)->rec_len = remainder.value;
+
+ return global_block.and_then([&](auto block_number) { return write_block(block_number, buffer, state); })
.transform([](auto) {});
}
else
{
- if (!ext2_parent->append_blocks(1))
+ if (!directory.append_blocks(1, batch))
{
return kstd::failure(ext2_errc::not_enough_free_blocks);
}
- auto const new_block_index = inode_block_count(ext2_parent->data()) - 1;
- auto const new_global_block = map_inode_block_index_to_global_block_number(new_block_index, ext2_parent->data());
+ auto const new_block_index = block_count(directory, state) - 1;
+ auto const new_global_block = inode_block_number(new_block_index, directory, state);
if (!new_global_block)
{
return kstd::failure(new_global_block.error());
}
- kstd::libc::memset(buffer.data(), 0, block_size().value);
- write_directory_entry_to_buffer(buffer.data(), 0_B, child_inode_number, name, file_type);
- if (auto update_result =
- new_global_block.and_then([&](auto block_number) { return write_block(block_number, buffer.data()); });
- !update_result)
+ kstd::libc::memset(buffer.data(), 0, block_size(state).value);
+ auto entry = write_directory_entry_to_buffer(buffer, 0_B, child, name, file_type);
+ if (!entry)
+ {
+ return kstd::failure(entry.error());
+ }
+
+ auto remainder = block_size(state);
+ (*entry)->rec_len = remainder.value;
+
+ if (auto result =
+ new_global_block.and_then([&](auto block_number) { return write_block(block_number, buffer, state); });
+ !result)
{
- return kstd::failure(update_result.error());
+ return kstd::failure(result.error());
}
}
- ext2_parent->data_mutable().size = (inode_block_count(ext2_parent->data()) * block_size()).value;
- return write_inode(ext2_parent->number(), ext2_parent->data());
+ auto size = block_count(directory, state) * block_size(state);
+ directory.data().size = size.value;
+ batch.mark_inode(directory);
+ return kstd::success();
}
- auto filesystem::init_directory(kstd::shared_ptr<kernel::filesystem::ext2::inode> const & new_inode,
- kernel::filesystem::ext2::inode * parent_inode) -> kstd::result<void>
+ auto filesystem::init_directory(inode & directory, inode & parent, mount_state & state, write_batch & batch)
+ -> kstd::result<void>
{
- if (!new_inode->append_blocks(1))
+ if (!directory.append_blocks(1, batch))
{
return kstd::failure(ext2_errc::not_enough_free_blocks);
}
- auto const global_block = map_inode_block_index_to_global_block_number(0, new_inode->data());
+ auto const global_block = inode_block_number(0, directory, state);
if (!global_block)
{
return kstd::failure(global_block.error());
}
- kstd::vector<uint8_t> buffer(block_size().value, 0);
- write_directory_entry_to_buffer(buffer.data(), 0_B, new_inode->number(), ".", 2);
- auto const dot_actual_len = static_cast<kstd::bytes>((8u + 1 + 3u) & ~3u);
- auto * entry = reinterpret_cast<linked_directory_entry *>(buffer.data());
- entry->rec_len = dot_actual_len.value;
+ auto buffer = kstd::vector<std::byte>{block_size(state).value};
+ auto dot = write_directory_entry_to_buffer(buffer, 0_B, directory, ".", kapi::filesystem::file_type::directory);
- write_directory_entry_to_buffer(buffer.data(), dot_actual_len, parent_inode->number(), "..", 2);
+ if (!dot)
+ {
+ return kstd::failure(dot.error());
+ }
+
+ auto dot_dot_offset = kstd::bytes{(*dot)->rec_len};
+ auto dot_dot =
+ write_directory_entry_to_buffer(buffer, dot_dot_offset, parent, "..", kapi::filesystem::file_type::directory);
+ if (!dot_dot)
+ {
+ return kstd::failure(dot_dot.error());
+ }
+ auto remainder = block_size(state) - dot_dot_offset;
+ (*dot_dot)->rec_len = remainder.value;
// '..' inside the new dir counts as an extra hard link to the parent
- auto & parent_inode_data = parent_inode->data_mutable();
+ auto & parent_inode_data = parent.data();
parent_inode_data.links_count++;
- return write_inode(parent_inode->number(), parent_inode_data)
- .and_then([&]() { return global_block; })
- .and_then([&](auto block_number) { return write_block(block_number, buffer.data()); })
- .transform([](auto) {});
+ batch.mark_inode(parent);
+
+ return global_block.and_then([&](auto number) { return write_block(number, buffer, state); }).transform([](auto) {
+ });
}
- auto filesystem::write_directory_entry_to_buffer(uint8_t * block_buffer, kstd::bytes offset,
- uint32_t child_inode_number, std::string_view name,
- uint8_t file_type) -> void
+ auto filesystem::write_directory_entry_to_buffer(std::span<std::byte> buffer, kstd::bytes offset, inode & target,
+ std::string_view name, kapi::filesystem::file_type type)
+ -> kstd::result<linked_directory_entry *>
{
- auto const name_len = static_cast<uint8_t>(name.size());
- auto * entry = reinterpret_cast<linked_directory_entry *>(block_buffer + offset);
- entry->inode = child_inode_number;
- entry->rec_len = static_cast<uint16_t>(block_size() - offset);
- entry->name_len = name_len;
- entry->file_type = file_type;
- kstd::libc::memcpy(entry->name.data(), name.data(), name_len);
+ auto const name_length = static_cast<uint8_t>(name.size());
+ auto const record_size = (sizeof(linked_directory_entry) - 1 + name_length + 3u) & ~3u;
+ if (buffer.size() - offset.value < record_size)
+ {
+ // TODO: improve error code.
+ return kstd::failure(vfs_errc::invalid_argument);
+ }
+
+ auto * entry = reinterpret_cast<linked_directory_entry *>(buffer.data() + offset);
+ entry->inode = target.number();
+ entry->rec_len = record_size;
+ entry->name_len = name_length;
+ entry->file_type = [=] {
+ switch (type)
+ {
+ case kapi::filesystem::file_type::regular:
+ return 1;
+ case kapi::filesystem::file_type::directory:
+ return 2;
+ case kapi::filesystem::file_type::character_device:
+ return 3;
+ case kapi::filesystem::file_type::block_device:
+ return 4;
+ case kapi::filesystem::file_type::fifo:
+ return 5;
+ case kapi::filesystem::file_type::socket:
+ return 6;
+ case kapi::filesystem::file_type::symbolic_link:
+ return 7;
+ default:
+ return 0;
+ }
+ }();
+ kstd::libc::memcpy(&entry->name_start, name.data(), name_length);
+ return kstd::success(entry);
}
- auto filesystem::read_inode(uint32_t inode_number) const -> kstd::result<kstd::shared_ptr<inode>>
+ auto filesystem::read_inode(uint32_t inode_number, mount_state const & state) const
+ -> kstd::result<kstd::shared_ptr<inode>>
{
- if (auto inode_offset = calculate_inode_offset(inode_number))
+ if (auto inode_offset = calculate_inode_offset(inode_number, state))
{
auto new_inode_data = inode_data{};
- if (auto read_result = m_backing_inode->read(as_writable_bytes(std::span{&new_inode_data, 1}), *inode_offset);
- !read_result)
+ if (auto read_result = state.backing_inode->read(kstd::raw_bytes(new_inode_data), *inode_offset); !read_result)
{
return kstd::failure(read_result.error());
}
else
{
- return kstd::make_shared<inode>(const_cast<filesystem *>(this), inode_number, new_inode_data);
+ return kstd::make_shared<struct inode>(inode_number, new_inode_data);
}
}
else
@@ -414,11 +548,12 @@ namespace kernel::filesystem::ext2
}
}
- auto filesystem::write_inode(uint32_t inode_number, inode_data const & data) -> kstd::result<void>
+ auto filesystem::write_inode(uint32_t inode_number, inode_data const & data, mount_state & state)
+ -> kstd::result<void>
{
- if (auto inode_offset = calculate_inode_offset(inode_number))
+ if (auto inode_offset = calculate_inode_offset(inode_number, state))
{
- return m_backing_inode->write(as_bytes(std::span{&data, 1}), *inode_offset).transform([](auto) {});
+ return state.backing_inode->write(kstd::raw_bytes(data), *inode_offset).transform([](auto) {});
}
else
{
@@ -426,146 +561,208 @@ namespace kernel::filesystem::ext2
}
}
- auto filesystem::calculate_inode_offset(uint32_t inode_number) const -> kstd::result<kstd::bytes>
+ auto filesystem::calculate_inode_offset(uint32_t inode_number, mount_state const & state) const
+ -> kstd::result<kstd::bytes>
{
- auto const inodes_per_group = m_superblock.inodes_per_group;
+ auto const inodes_per_group = state.superblock.inodes_per_group;
auto const block_group_index = (inode_number - 1) / inodes_per_group;
auto const inode_index_within_group = (inode_number - 1) % inodes_per_group;
- if (block_group_index >= m_block_group_descriptors.size())
+ if (block_group_index >= state.block_group_descriptors.size())
{
return kstd::failure(ext2_errc::invalid_block_group_index);
}
- auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index);
+ auto const & block_group_descriptor = state.block_group_descriptors.at(block_group_index);
auto const inode_table_start_block = block_group_descriptor.inode_table;
- auto const inode_table_offset = inode_table_start_block * block_size();
- auto const inode_offset = inode_table_offset + inode_index_within_group * inode_size();
+ auto const inode_table_offset = inode_table_start_block * block_size(state);
+ auto const inode_offset = inode_table_offset + inode_index_within_group * inode_size(state);
return inode_offset;
}
- auto filesystem::allocate_blocks(size_t count) -> kstd::result<kstd::vector<uint32_t>>
+ auto filesystem::allocate_blocks(size_t count, mount_state & state, write_batch & batch)
+ -> kstd::result<kstd::vector<uint32_t>>
{
- if (m_block_group_descriptors.empty() || count > m_superblock.free_blocks_count)
+ auto & [superblock, block_group_descriptors, backing_inode, _] = state;
+
+ if (block_group_descriptors.empty() || count > superblock.free_blocks_count)
{
return kstd::failure(ext2_errc::not_enough_free_blocks);
}
- kstd::vector<uint32_t> allocated_blocks{};
+ struct pending_group
+ {
+ std::size_t index;
+ std::size_t claimed;
+ kstd::vector<std::byte> bitmap;
+ };
- for (auto block_group_descriptor_index = 0uz; block_group_descriptor_index < m_block_group_descriptors.size();
- ++block_group_descriptor_index)
+ auto pending = kstd::vector<pending_group>{};
+ auto allocated_blocks = kstd::vector<uint32_t>{};
+
+ for (auto group_index = 0uz; group_index < block_group_descriptors.size() && allocated_blocks.size() < count;
+ ++group_index)
{
- auto & block_group_descriptor = m_block_group_descriptors.at(block_group_descriptor_index);
- if (block_group_descriptor.free_blocks_count == 0)
+ auto & descriptor = block_group_descriptors.at(group_index);
+ if (descriptor.free_blocks_count == 0)
{
continue;
}
- kstd::vector<uint8_t> block_bitmap(block_size().value, 0);
- if (auto read_result = read_block(block_group_descriptor.block_bitmap, block_bitmap.data()); !read_result)
+ auto block_bitmap = kstd::vector<std::byte>{block_size(state).value};
+ if (auto read_result = read_block(descriptor.block_bitmap, block_bitmap, state); !read_result)
{
return kstd::failure(read_result.error());
}
- bool is_bitmap_dirty = false;
- for (auto i = 0uz; i < static_cast<size_t>(m_superblock.blocks_per_group) && allocated_blocks.size() < count; ++i)
+ auto claimed = 0uz;
+ for (auto i = 0uz; i < static_cast<size_t>(superblock.blocks_per_group) && allocated_blocks.size() < count; ++i)
{
if (!bitmap_is_set(block_bitmap, i))
{
- is_bitmap_dirty = true;
-
bitmap_set(block_bitmap, i);
- block_group_descriptor.free_blocks_count--;
- m_superblock.free_blocks_count--;
-
- allocated_blocks.push_back(i + block_group_descriptor_index * m_superblock.blocks_per_group);
+ ++claimed;
+ allocated_blocks.push_back(i + group_index * superblock.blocks_per_group);
}
}
- if (is_bitmap_dirty)
+ if (claimed > 0)
{
- if (auto write_result = write_block(block_group_descriptor.block_bitmap, block_bitmap.data()); !write_result)
- {
- return kstd::failure(write_result.error());
- }
-
- // TODO update all block group descriptors
- if (auto write_result =
- m_backing_inode->write(as_bytes(std::span{&block_group_descriptor, 1}),
- block_group_descriptor_table_offset() +
- block_group_descriptor_index * kstd::size_of(block_group_descriptor));
- !write_result)
- {
- return kstd::failure(write_result.error());
- }
+ pending.push_back({group_index, claimed, std::move(block_bitmap)});
}
}
- // TODO update all superblocks
- if (auto write_result = m_backing_inode->write(as_bytes(std::span{&m_superblock, 1}), constants::superblock_offset);
- !write_result)
+ if (allocated_blocks.size() != count)
{
- return kstd::failure(write_result.error());
+ return kstd::failure(ext2_errc::not_enough_free_blocks);
}
- if (allocated_blocks.size() != count)
+ for (auto & group : pending)
{
- return kstd::failure(ext2_errc::not_enough_free_blocks);
+ auto & descriptor = block_group_descriptors.at(group.index);
+ descriptor.free_blocks_count -= group.claimed;
+ superblock.free_blocks_count -= group.claimed;
+
+ if (auto result = write_block(descriptor.block_bitmap, group.bitmap, state); !result)
+ {
+ return kstd::failure(result.error());
+ }
+ batch.mark_group(group.index);
}
return allocated_blocks;
}
- auto filesystem::allocate_inode() -> kstd::result<uint32_t>
+ auto filesystem::data_size(inode const & inode, mount_state const & state) const -> kstd::bytes
+ {
+ uint64_t size = inode.data().size;
+
+ if (revision_level(state) > constants::good_old_revision && inode.is_regular())
+ {
+ size |= static_cast<uint64_t>(inode.data().dir_acl) << 32;
+ }
+
+ return kstd::bytes{size};
+ }
+
+ auto filesystem::set_data_size(inode & inode, kstd::bytes new_size, mount_state const & state) const -> void
+ {
+ if (revision_level(state) > constants::good_old_revision && inode.is_regular())
+ {
+ inode.data().dir_acl = static_cast<uint32_t>(new_size.value >> 32);
+ }
+ inode.data().size = static_cast<uint32_t>(new_size.value);
+ }
+
+ auto filesystem::do_read(inode const & inode, std::span<std::byte> buffer, kstd::bytes offset,
+ mount_state const & state) const -> kstd::result<kstd::bytes>
{
- if (m_block_group_descriptors.empty() || m_superblock.free_inodes_count == 0)
+ auto const max_readable = data_size(inode, state) - offset;
+ auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable);
+
+ auto const block_size = this->block_size(state);
+ auto block_index = offset / block_size;
+ auto in_block_offset = offset % block_size;
+
+ auto bytes_read = 0_B;
+
+ while (bytes_read < requested_size)
+ {
+ auto const block_number = inode_block_number(block_index, inode, state);
+ if (!block_number)
+ {
+ break;
+ }
+
+ auto const bytes_to_read = std::min(requested_size - bytes_read, block_size - in_block_offset);
+ if (block_number == 0)
+ {
+ kstd::libc::memset(buffer.data() + bytes_read, 0, bytes_to_read.value);
+ bytes_read += bytes_to_read;
+ }
+ else
+ {
+ auto const block_start_offset = block_number.value() * block_size;
+ auto const read_offset = block_start_offset + in_block_offset;
+
+ auto const read_result =
+ state.backing_inode->read(buffer.subspan(bytes_read.value, bytes_to_read.value), read_offset);
+ if (!read_result)
+ {
+ return kstd::failure(read_result.error());
+ }
+
+ bytes_read += read_result.value();
+ }
+
+ block_index++;
+ in_block_offset = 0_B;
+ }
+
+ return bytes_read;
+ }
+
+ auto filesystem::allocate_inode(mount_state & state, write_batch & batch) -> kstd::result<uint32_t>
+ {
+ auto & [superblock, block_group_descriptors, _, __] = state;
+
+ if (block_group_descriptors.empty() || superblock.free_inodes_count == 0)
{
return kstd::failure(ext2_errc::not_enough_inodes);
}
- for (auto block_group_descriptor_index = 0uz; block_group_descriptor_index < m_block_group_descriptors.size();
+ for (auto block_group_descriptor_index = 0uz; block_group_descriptor_index < block_group_descriptors.size();
++block_group_descriptor_index)
{
- auto & block_group_descriptor = m_block_group_descriptors.at(block_group_descriptor_index);
+ auto & block_group_descriptor = block_group_descriptors.at(block_group_descriptor_index);
if (block_group_descriptor.free_inodes_count == 0)
{
continue;
}
- kstd::vector<uint8_t> inode_bitmap(block_size().value, 0);
- if (auto read_result = read_block(block_group_descriptor.inode_bitmap, inode_bitmap.data()); !read_result)
+ auto inode_bitmap = kstd::vector<std::byte>{block_size(state).value};
+ if (auto read_result = read_block(block_group_descriptor.inode_bitmap, inode_bitmap, state); !read_result)
{
return kstd::failure(read_result.error());
}
- for (auto i = 0uz; i < static_cast<size_t>(m_superblock.inodes_per_group); ++i)
+ for (auto i = 0uz; i < static_cast<size_t>(superblock.inodes_per_group); ++i)
{
if (!bitmap_is_set(inode_bitmap, i))
{
bitmap_set(inode_bitmap, i);
block_group_descriptor.free_inodes_count--;
- m_superblock.free_inodes_count--;
+ superblock.free_inodes_count--;
- if (auto write_result = write_block(block_group_descriptor.inode_bitmap, inode_bitmap.data()); !write_result)
- {
- return kstd::failure(write_result.error());
- }
-
- if (auto write_result = write_block_group_descriptor(block_group_descriptor, block_group_descriptor_index);
- !write_result)
+ if (auto write_result = write_block(block_group_descriptor.inode_bitmap, inode_bitmap, state); !write_result)
{
return kstd::failure(write_result.error());
}
- // TODO update all superblocks
- if (auto write_result = write_superblock(); !write_result)
- {
- return kstd::failure(write_result.error());
- }
+ batch.mark_group(block_group_descriptor_index);
- return block_group_descriptor_index * m_superblock.inodes_per_group + i + 1;
+ return block_group_descriptor_index * superblock.inodes_per_group + i + 1;
}
}
}
@@ -573,36 +770,40 @@ namespace kernel::filesystem::ext2
return kstd::failure(ext2_errc::not_enough_inodes);
}
- auto filesystem::map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const
+ auto filesystem::inode_block_number(size_t index, inode const & inode, mount_state const & state) const
-> kstd::result<std::size_t>
{
- if (inode_block_index < constants::direct_block_count)
+ auto const & block_array = inode.data().block;
+
+ if (index < constants::direct_block_count)
{
- return data.block.at(inode_block_index);
+ return block_array.at(index);
}
- inode_block_index -= constants::direct_block_count;
-
- for (auto const & level : indirect_levels())
+ index -= constants::direct_block_count;
+ auto const block_size = this->block_size(state);
+ auto const numbers_per_block = block_numbers(1, block_size);
+ for (auto const & level : indirect_levels(block_size))
{
- if (inode_block_index >= level.capacity)
+ auto const capacity = level.capacity;
+ if (index >= capacity)
{
- inode_block_index -= level.capacity;
+ index -= capacity;
continue;
}
- auto block_number = data.block[level.slot_index];
+ auto block_number = block_array[level.slot_index];
if (block_number == 0)
{
return 0;
}
- for (auto stride = level.capacity / block_numbers_per_block();; stride /= block_numbers_per_block())
+ for (auto stride = capacity / numbers_per_block;; stride /= numbers_per_block)
{
- auto const idx = inode_block_index / stride;
- inode_block_index %= stride;
+ auto const idx = index / stride;
+ index %= stride;
- if (auto read_result = read_block_number_at_index(block_number, idx); !read_result)
+ if (auto read_result = read_block_number_at_index(block_number, idx, state); !read_result)
{
return kstd::failure(read_result.error());
}
@@ -628,14 +829,14 @@ namespace kernel::filesystem::ext2
return kstd::failure(ext2_errc::invalid_block_index);
}
- auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> kstd::result<uint32_t>
+ auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index, mount_state const & state) const
+ -> kstd::result<uint32_t>
{
uint32_t block_number_buffer = 0;
- auto const block_start_offset = block_number * block_size();
+ auto const block_start_offset = block_number * block_size(state);
auto const number_start_address = block_start_offset + index * kstd::size_of<uint32_t>();
- if (auto read_result =
- m_backing_inode->read(as_writable_bytes(std::span{&block_number_buffer, 1}), number_start_address);
+ if (auto read_result = state.backing_inode->read(kstd::raw_bytes(block_number_buffer), number_start_address);
!read_result)
{
return kstd::failure(read_result.error());
@@ -645,7 +846,8 @@ namespace kernel::filesystem::ext2
}
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>
+ uint32_t global_block_number, mount_state & state,
+ write_batch & batch) -> kstd::result<void>
{
if (inode_block_index < constants::direct_block_count)
{
@@ -654,7 +856,9 @@ namespace kernel::filesystem::ext2
}
inode_block_index -= constants::direct_block_count;
- for (auto const & level : indirect_levels())
+ auto const block_size = this->block_size(state);
+ auto const numbers_per_block = block_numbers(1, block_size);
+ for (auto const & level : indirect_levels(block_size))
{
if (inode_block_index >= level.capacity)
{
@@ -662,18 +866,19 @@ namespace kernel::filesystem::ext2
continue;
}
+ auto & backing_inode = state.backing_inode;
auto block_number = data.block[level.slot_index];
auto parent_byte_offset = 0_B;
bool is_root = true;
- for (auto stride = level.capacity / block_numbers_per_block();; stride /= block_numbers_per_block())
+ for (auto stride = level.capacity / numbers_per_block;; stride /= 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);
+ auto allocated = allocate_blocks(1, state, batch);
if (!allocated)
{
return kstd::failure(allocated.error());
@@ -687,7 +892,7 @@ namespace kernel::filesystem::ext2
}
else
{
- if (auto write_result = m_backing_inode->write(as_bytes(std::span{&block_number, 1}), parent_byte_offset);
+ if (auto write_result = backing_inode->write(kstd::raw_bytes(block_number), parent_byte_offset);
!write_result)
{
return kstd::failure(write_result.error());
@@ -695,11 +900,11 @@ namespace kernel::filesystem::ext2
}
}
- auto const byte_offset = block_number * block_size() + idx * kstd::size_of<uint32_t>();
+ auto const byte_offset = block_number * block_size + idx * kstd::size_of<uint32_t>();
if (stride == 1)
{
- if (auto write_result = m_backing_inode->write(as_bytes(std::span{&global_block_number, 1}), byte_offset);
+ if (auto write_result = backing_inode->write(kstd::raw_bytes(global_block_number), byte_offset);
!write_result)
{
return kstd::failure(write_result.error());
@@ -709,7 +914,7 @@ namespace kernel::filesystem::ext2
parent_byte_offset = byte_offset;
is_root = false;
- if (auto read_result = read_block_number_at_index(block_number, idx); !read_result)
+ if (auto read_result = read_block_number_at_index(block_number, idx, state); !read_result)
{
return kstd::failure(read_result.error());
}
@@ -723,118 +928,81 @@ namespace kernel::filesystem::ext2
return kstd::success();
}
- auto filesystem::read_block(uint32_t block_number, void * buffer) const -> kstd::result<kstd::bytes>
- {
- auto const block_offset = static_cast<size_t>(block_number) * block_size();
- return m_backing_inode->read(std::span{static_cast<std::byte *>(buffer), block_size().value}, block_offset);
- }
-
- auto filesystem::write_block(uint32_t block_number, void const * buffer) -> kstd::result<kstd::bytes>
- {
- auto const block_offset = static_cast<size_t>(block_number) * block_size();
- return m_backing_inode->write(std::span{static_cast<std::byte const *>(buffer), block_size().value}, block_offset);
- }
-
auto filesystem::write_block_group_descriptor(block_group_descriptor const & block_group_descriptor,
- size_t block_group_descriptor_index) const -> kstd::result<void>
+ size_t block_group_descriptor_index, mount_state const & state) const
+ -> kstd::result<void>
{
// TODO update all block group descriptors
- return m_backing_inode
- ->write(as_bytes(std::span{&block_group_descriptor, 1}),
- block_group_descriptor_table_offset() +
+ return state.backing_inode
+ ->write(kstd::raw_bytes(block_group_descriptor),
+ block_group_descriptor_table_offset(state) +
block_group_descriptor_index * kstd::size_of(block_group_descriptor))
.transform([](auto) {});
}
- auto filesystem::write_superblock() const -> kstd::result<void>
+ auto filesystem::write_superblock(mount_state & state) const -> kstd::result<void>
{
- // TODO update all superblocks
- return m_backing_inode->write(as_bytes(std::span{&m_superblock, 1}), constants::superblock_offset)
+ // TODO update all superblock copies
+ return state.backing_inode->write(kstd::raw_bytes(state.superblock), constants::superblock_offset)
.transform([](auto) {});
}
- auto filesystem::map_inode_mode_to_file_type(uint16_t mode) -> uint8_t
+ auto filesystem::map_inode_mode_to_file_type(uint16_t mode) -> kapi::filesystem::file_type
{
switch (mode & constants::mode_mask)
{
case constants::mode_regular:
- return 1;
+ return kapi::filesystem::file_type::regular;
case constants::mode_directory:
- return 2;
+ return kapi::filesystem::file_type::directory;
+ case constants::mode_block_device:
+ return kapi::filesystem::file_type::block_device;
+ case constants::mode_character_device:
+ return kapi::filesystem::file_type::character_device;
case constants::mode_symbolic_link:
- return 7;
+ return kapi::filesystem::file_type::symbolic_link;
+ case constants::mode_fifo:
+ return kapi::filesystem::file_type::fifo;
+ case constants::mode_socket:
+ return kapi::filesystem::file_type::socket;
default:
- return 0;
+ return kapi::filesystem::file_type{0};
}
}
auto filesystem::map_vfs_inode_type_into_inode_mode(kapi::filesystem::file_type type, uint16_t mode) -> uint16_t
{
- switch (type)
- {
- case kapi::filesystem::file_type::directory:
- return mode | constants::mode_directory;
- case kapi::filesystem::file_type::regular:
- return mode | constants::mode_regular;
- case kapi::filesystem::file_type::block_device:
- return mode | constants::mode_block_device;
- case kapi::filesystem::file_type::character_device:
- return mode | constants::mode_character_device;
- case kapi::filesystem::file_type::symbolic_link:
- // return mode | constants::mode_symbolic_link; // TODO implement
- default:
- kapi::system::panic("[EXT2] Not implemented.");
- }
- }
-
- auto filesystem::block_numbers_per_block() const -> size_t
- {
- return block_size() / kstd::size_of<uint32_t>();
- }
-
- auto filesystem::block_numbers_per_singly_indirect_block() const -> size_t
- {
- return block_numbers_per_block();
- }
-
- auto filesystem::block_numbers_per_doubly_indirect_block() const -> size_t
- {
- return block_numbers_per_singly_indirect_block() * block_numbers_per_block();
- }
-
- auto filesystem::block_numbers_per_triply_indirect_block() const -> size_t
- {
- return block_numbers_per_doubly_indirect_block() * block_numbers_per_block();
- }
-
- auto filesystem::block_size() const -> kstd::bytes
- {
- return kstd::bytes{constants::base_block_size.value << m_superblock.log_block_size};
- }
-
- auto filesystem::revision_level() const -> uint32_t
- {
- return m_superblock.rev_level;
- }
-
- auto filesystem::inode_size() const -> kstd::bytes
- {
- return kstd::bytes(revision_level() == constants::good_old_revision ? 128 : m_superblock.inode_size);
- }
-
- auto filesystem::inode_block_count(inode_data const & data) const -> uint32_t
- {
- return data.blocks / (2 << m_superblock.log_block_size);
+ return mode | [=] {
+ switch (type)
+ {
+ case kapi::filesystem::file_type::directory:
+ return constants::mode_directory;
+ case kapi::filesystem::file_type::regular:
+ return constants::mode_regular;
+ case kapi::filesystem::file_type::block_device:
+ return constants::mode_block_device;
+ case kapi::filesystem::file_type::character_device:
+ return constants::mode_character_device;
+ case kapi::filesystem::file_type::symbolic_link:
+ return constants::mode_symbolic_link; // TODO implement
+ case kapi::filesystem::file_type::fifo:
+ return constants::mode_fifo;
+ case kapi::filesystem::file_type::socket:
+ return constants::mode_socket;
+ default:
+ kapi::system::panic("[EXT2] Not implemented.");
+ }
+ }();
}
- auto filesystem::update_inode_block_count(inode_data & data, uint32_t delta) -> void
+ auto filesystem::update_inode_block_count(inode_data & data, uint32_t delta, mount_state & state) -> void
{
- data.blocks += delta * (2 << m_superblock.log_block_size);
+ data.blocks += delta * (2 << state.superblock.log_block_size);
}
- auto filesystem::block_group_descriptor_table_offset() const -> kstd::bytes
+ auto filesystem::block_group_descriptor_table_offset(mount_state const & state) const -> kstd::bytes
{
- return block_size() == 1024_B ? 2 * block_size() : block_size();
+ return block_size(state) == 1024_B ? 2 * block_size(state) : block_size(state);
}
} // namespace kernel::filesystem::ext2
diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp
index d70358fd..6d5720a2 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.hpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.hpp
@@ -2,8 +2,12 @@
#define TEACHOS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP
#include <kernel/filesystem/ext2/block_group_descriptor.hpp>
+#include <kernel/filesystem/ext2/directory_iterator.hpp>
#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/linked_directory_entry.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
#include <kernel/filesystem/ext2/superblock.hpp>
+#include <kernel/filesystem/ext2/write_batch.hpp>
#include <kernel/filesystem/filesystem.hpp>
#include <kernel/filesystem/inode.hpp>
@@ -16,10 +20,10 @@
#include <kstd/units.hpp>
#include <kstd/vector.hpp>
-#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
+#include <span>
#include <string_view>
#include <type_traits>
@@ -48,6 +52,9 @@ namespace kernel::filesystem::ext2
constexpr uint16_t inline mode_symbolic_link = 0xA000;
constexpr uint16_t inline mode_block_device = 0x6000;
constexpr uint16_t inline mode_character_device = 0x2000;
+ constexpr uint16_t inline mode_fifo = 0x1000;
+ constexpr uint16_t inline mode_socket = 0xC000;
+
} // namespace constants
//! Ext2 incompatibility features
@@ -75,75 +82,106 @@ namespace kernel::filesystem::ext2
//! The Second Extended Filesystem (ext2)
struct filesystem final : kernel::filesystem::filesystem
{
- auto mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) -> kstd::result<void> override;
+ //! @name Driver Interface Implementation
+ //! @{
+
+ [[nodiscard]] auto probe(inode_ptr const & backing_inode) const -> kstd::result<std::uint32_t> override;
+
+ auto mount(inode_ptr const & backing_inode) -> kstd::result<mount_result> override;
- [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
+ [[nodiscard]] auto lookup(inode_ptr const & parent, std::string_view name, driver_data_ptr driver_data) const
+ -> kstd::result<inode_ptr> override;
- [[nodiscard]] auto create_inode(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name,
- kapi::filesystem::file_type type,
+ [[nodiscard]] auto create_inode(inode_ptr const & parent, std::string_view name, kapi::filesystem::file_type type,
+ driver_data_ptr driver_data,
std::optional<kapi::filesystem::device_number> raw_device = std::nullopt)
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
+ -> kstd::result<inode_ptr> override;
+
+ //! @}
+
+ //! @name Filesystem Information Queries
+ //! @{
//! Get the size of a block in the filesystem.
//!
+ //! @param state The driver state to operate on.
//! @return The size of a block in bytes.
- [[nodiscard]] auto block_size() const -> kstd::bytes;
+ [[nodiscard]] auto block_size(mount_state const & state) const -> kstd::bytes;
//! Get the revision level of the filesystem.
//!
+ //! @param state The driver state to operate on.
//! @return The revision level.
- [[nodiscard]] auto revision_level() const -> uint32_t;
+ [[nodiscard]] auto revision_level(mount_state const & state) const -> uint32_t;
+
+ //! Get the size of an inode in the filesystem.
+ //!
+ //! @param state The driver state to operate on.
+ //! @return The size of an inode in bytes.
+ [[nodiscard]] auto inode_size(mount_state const & state) const -> kstd::bytes;
+
+ //! Get the number of blocks allocated to an inode.
+ //!
+ //! @param inode The inode whose block count to query.
+ //! @param state The driver state to operate on.
+ //! @return The number of blocks allocated to the inode.
+ [[nodiscard]] auto block_count(inode const & inode, mount_state const & state) const -> uint32_t;
+
+ //! @}
+
+ //! @name Block I/O
+ //! @{
//! Read a block from the backing inode into the provided buffer.
//!
//! @param block_number The number of the block to read.
//! @param buffer The buffer to read the block data into.
+ //! @param state The state to operate on.
//! @return The number of bytes read.
- auto read_block(uint32_t block_number, void * buffer) const -> kstd::result<kstd::bytes>;
+ [[nodiscard]] auto read_block(uint32_t block_number, std::span<std::byte> buffer, mount_state const & state) const
+ -> kstd::result<kstd::bytes>;
//! Write a block of data from the provided buffer to the backing inode.
//!
//! @param block_number The number of the block to write.
//! @param buffer The buffer containing the data to write.
+ //! @param state The state to operate on.
//! @return The number of bytes written.
- auto write_block(uint32_t block_number, void const * buffer) -> kstd::result<kstd::bytes>;
+ [[nodiscard]] auto write_block(uint32_t block_number, std::span<std::byte const> buffer, mount_state & state)
+ -> kstd::result<kstd::bytes>;
+
+ //! @}
//! Allocate a specified number of blocks.
//!
//! @param count The number of blocks to allocate.
+ //! @param state The state to operate on.
+ //! @param batch The active write batch.
//! @return A vector of the allocated block numbers.
- auto allocate_blocks(size_t count) -> kstd::result<kstd::vector<uint32_t>>;
-
- //! Get the size of an inode in the filesystem.
- //!
- //! @return The size of an inode in bytes.
- [[nodiscard]] auto inode_size() const -> kstd::bytes;
-
- //! Get the number of blocks allocated to an inode.
- //!
- //! @param data The inode data.
- //! @return The number of blocks allocated to the inode.
- [[nodiscard]] auto inode_block_count(inode_data const & data) const -> uint32_t;
+ auto allocate_blocks(size_t count, mount_state & state, write_batch & batch)
+ -> kstd::result<kstd::vector<uint32_t>>;
//! Update the number of blocks allocated to an inode.
//!
//! @param data The inode data.
+ //! @param state The state to operate on.
//! @param delta The change in the number of blocks.
- auto update_inode_block_count(inode_data & data, uint32_t delta) -> void;
+ auto update_inode_block_count(inode_data & data, uint32_t delta, mount_state & state) -> void;
//! Write an inode to the backing inode.
//!
//! @param inode_number The number of the inode to write.
+ //! @param state The state to operate on.
//! @param data The inode data to write.
- auto write_inode(uint32_t inode_number, inode_data const & data) -> kstd::result<void>;
+ auto write_inode(uint32_t inode_number, inode_data const & data, mount_state & state) -> kstd::result<void>;
- //! Map an inode block index to a global block number.
+ //! Get the block number associated with the nth block of an inode.
//!
- //! @param inode_block_index The index of the block within the inode.
- //! @param data The inode data.
+ //! @param index The index of the block within the inode.
+ //! @param inode The inode.
+ //! @param state The state to operate on.
//! @return The global block number on success, an error otherwise.
- [[nodiscard]] auto map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const
+ [[nodiscard]] auto inode_block_number(size_t index, inode const & inode, mount_state const & state) const
-> kstd::result<std::size_t>;
//! Write a global block number to an inode block index.
@@ -151,46 +189,101 @@ namespace kernel::filesystem::ext2
//! @param block_index The index of the block within the inode.
//! @param data The inode data.
//! @param global_block_number The global block number to write.
+ //! @param state The state to operate on.
+ //! @param batch The active write batch.
auto write_global_block_number_to_inode_block_index(size_t block_index, inode_data & data,
- uint32_t global_block_number) -> kstd::result<void>;
+ uint32_t global_block_number, mount_state & state,
+ write_batch & batch) -> kstd::result<void>;
private:
- struct indirect_level
- {
- uint32_t slot_index;
- size_t capacity;
- };
+ friend write_batch;
+ friend inode;
+ friend directory_iterator;
- [[nodiscard]] auto indirect_levels() const -> std::array<indirect_level, 3>;
+ //! Perform the actual read of the requested data.
+ //!
+ //! @param inode The inode whose data to read.
+ //! @param buffer The buffer to read into.
+ //! @param offset The offset inside the inode's data to start at.
+ //! @param state The driver state to operate on.
+ //! @return The number of bytes read on success, an error otherwise.
+ [[nodiscard]] auto do_read(inode const & inode, std::span<std::byte> buffer, kstd::bytes offset,
+ mount_state const & state) const -> kstd::result<kstd::bytes>;
+
+ //! Determine the size of an inode's data on disk.
+ //!
+ //! @param inode The inode whose data size to determine.
+ //! @param state The driver state to operate on.
+ //! @return The size of the data referenced by the inode.
+ [[nodiscard]] auto data_size(inode const & inode, mount_state const & state) const -> kstd::bytes;
- auto allocate_inode() -> kstd::result<uint32_t>;
- auto add_directory_entry(inode * ext2_parent, std::string_view name, uint32_t child_inode_number, uint16_t mode)
- -> kstd::result<void>;
- auto init_directory(kstd::shared_ptr<kernel::filesystem::ext2::inode> const & new_inode,
- kernel::filesystem::ext2::inode * parent_inode) -> kstd::result<void>;
- auto write_directory_entry_to_buffer(uint8_t * block_buffer, kstd::bytes offset, uint32_t child_inode_number,
- std::string_view name, uint8_t file_type) -> void;
+ //! Set the size of an inode's data on disk.
+ //!
+ //! @param inode The inode whose data size to set.
+ //! @param new_size The size to set.
+ //! @param state The driver state to operate on.
+ auto set_data_size(inode & inode, kstd::bytes new_size, mount_state const & state) const -> void;
- [[nodiscard]] auto calculate_inode_offset(uint32_t inode_number) const -> kstd::result<kstd::bytes>;
- [[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>;
+ //! Allocate a single inode in the file system.
+ //!
+ //! @param state The driver state to operate on.
+ //! @param batch The active write batch.
+ //! @return An inode number on success, an error otherwise.
+ auto allocate_inode(mount_state & state, write_batch & batch) -> kstd::result<uint32_t>;
- [[nodiscard]] auto block_group_descriptor_table_offset() const -> kstd::bytes;
+ //! Add a new directory entry to a given directory.
+ //!
+ //! @param directory The directory to add the entry to.
+ //! @param name The name for the entry.
+ //! @param child The inode number of the child.
+ //! @param mode The file mode of the entry.
+ //! @param state The driver state to operate on.
+ //! @param batch The active write batch.
+ //! @return Nothing on success, an error code otherwise.
+ auto add_directory_entry(inode & directory, std::string_view name, inode & child, std::uint16_t mode,
+ mount_state & state, write_batch & batch) -> kstd::result<void>;
+
+ //! Initialize a new directory inode.
+ //!
+ //! This function initializes an inode to be an empty directory. On success, the inode will be a directory with only
+ //! two children, "." and "..".
+ //!
+ //! @param directory The inode to initialize.
+ //! @param parent The parent of this newly initialized directory.
+ //! @param state The driver state to operate on.
+ //! @param batch The active write batch.
+ //! @return Nothing on success, an error otherwise.
+ auto init_directory(inode & directory, inode & parent, mount_state & state, write_batch & batch)
+ -> kstd::result<void>;
- [[nodiscard]] auto block_numbers_per_block() const -> size_t;
- [[nodiscard]] auto block_numbers_per_singly_indirect_block() const -> size_t;
- [[nodiscard]] auto block_numbers_per_doubly_indirect_block() const -> size_t;
- [[nodiscard]] auto block_numbers_per_triply_indirect_block() const -> size_t;
+ //! Write a new directory entry into the given buffer.
+ //!
+ //! @param buffer The buffer to write to.
+ //! @param offset The offset inside to the buffer.
+ //! @param target The inode the entry targets.
+ //! @param name The name for the entry.
+ //! @param type The filet type of the entry.
+ //! @return A pointer to the written entry on success, an error otherwise.
+ auto write_directory_entry_to_buffer(std::span<std::byte> buffer, kstd::bytes offset, inode & target,
+ std::string_view name, kapi::filesystem::file_type type)
+ -> kstd::result<linked_directory_entry *>;
+
+ [[nodiscard]] auto calculate_inode_offset(uint32_t inode_number, mount_state const & state) const
+ -> kstd::result<kstd::bytes>;
+ [[nodiscard]] auto read_inode(uint32_t inode_number, mount_state const & state) const
+ -> kstd::result<kstd::shared_ptr<inode>>;
+ [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index, mount_state const & state) const
+ -> kstd::result<uint32_t>;
+
+ [[nodiscard]] auto block_group_descriptor_table_offset(mount_state const & state) const -> kstd::bytes;
[[nodiscard]] auto write_block_group_descriptor(block_group_descriptor const & block_group_descriptor,
- size_t block_group_descriptor_index) const -> kstd::result<void>;
- [[nodiscard]] auto write_superblock() const -> kstd::result<void>;
+ size_t block_group_descriptor_index,
+ mount_state const & state) const -> kstd::result<void>;
+ [[nodiscard]] auto write_superblock(mount_state & state) const -> kstd::result<void>;
- auto map_inode_mode_to_file_type(uint16_t mode) -> uint8_t;
+ auto map_inode_mode_to_file_type(uint16_t mode) -> kapi::filesystem::file_type;
auto map_vfs_inode_type_into_inode_mode(kapi::filesystem::file_type type, uint16_t mode) -> uint16_t;
-
- superblock m_superblock{};
- kstd::vector<block_group_descriptor> m_block_group_descriptors;
};
} // namespace kernel::filesystem::ext2
diff --git a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp
index 51ba2f30..28aabed7 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.tests.cpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.tests.cpp
@@ -5,8 +5,12 @@
#include <kernel/filesystem/ext2/block_group_descriptor.hpp>
#include <kernel/filesystem/ext2/error.hpp>
#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
#include <kernel/filesystem/ext2/superblock.hpp>
+#include <kernel/filesystem/ext2/write_batch.hpp>
#include <kernel/filesystem/filesystem.hpp>
+#include <kernel/filesystem/inode.hpp>
+#include <kernel/filesystem/mount.hpp>
#include <kernel/test_support/cpu.hpp>
#include <kernel/test_support/devices/block_device.hpp>
#include <kernel/test_support/filesystem/ext2.hpp>
@@ -47,37 +51,44 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(boot_device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ auto driver_data = (*mount)->driver_data();
+ REQUIRE(mount);
THEN("the root inode is available and is a directory")
{
- REQUIRE(fs.root_inode());
- REQUIRE(fs.root_inode()->is_directory());
+ REQUIRE(root);
+ REQUIRE((root)->is_directory());
}
THEN("lookup resolves known entries from the image")
{
- auto information = fs.lookup(fs.root_inode(), "information");
+ 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");
+ 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")
{
- REQUIRE(!fs.lookup(nullptr, "information"));
+ REQUIRE(!fs->lookup(nullptr, "information", driver_data));
- auto information = fs.lookup(fs.root_inode(), "information");
+ auto information = fs->lookup(root, "information", driver_data);
REQUIRE(information);
- auto info_1 = fs.lookup(*information, "info_1.txt");
+ (*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"));
- REQUIRE(!fs.lookup(fs.root_inode(), "does_not_exist"));
+ REQUIRE(!fs->lookup(*info_1, "anything", driver_data));
+ REQUIRE(!fs->lookup(root, "does_not_exist", driver_data));
}
}
}
@@ -97,51 +108,59 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(boot_device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ auto driver_data = (*mount)->driver_data();
+ REQUIRE(mount);
THEN("a file can be created")
{
- auto lookup_result = fs.lookup(fs.root_inode(), "blub");
+ auto lookup_result = fs->lookup(root, "blub", driver_data);
REQUIRE(!lookup_result);
- auto new_inode = fs.create_inode(fs.root_inode(), "blub", kapi::filesystem::file_type::regular);
+ 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(fs.root_inode(), "blub");
+ lookup_result = fs->lookup(root, "blub", driver_data);
REQUIRE(lookup_result);
+ (*lookup_result)->set_owning_mount(*mount);
}
THEN("a directory can be created")
{
- auto lookup_result = fs.lookup(fs.root_inode(), "blub");
+ auto lookup_result = fs->lookup(root, "blub", driver_data);
REQUIRE(!lookup_result);
- auto new_directory = fs.create_inode(fs.root_inode(), "blub", kapi::filesystem::file_type::directory);
+ auto new_directory = fs->create_inode(root, "blub", kapi::filesystem::file_type::directory, driver_data);
REQUIRE(new_directory);
REQUIRE(new_directory.value()->is_directory());
- lookup_result = fs.lookup(fs.root_inode(), "blub");
+ lookup_result = fs->lookup(root, "blub", driver_data);
REQUIRE(lookup_result);
}
THEN("a directory and a file within can be created")
{
- auto new_directory = fs.create_inode(fs.root_inode(), "blub", kapi::filesystem::file_type::directory);
+ 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);
+ 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");
+ auto lookup_result = fs->lookup(new_directory.value(), "blub_file", driver_data);
REQUIRE(lookup_result);
REQUIRE(lookup_result.value()->is_regular());
}
THEN("a file can be created and written to")
{
- auto new_inode = fs.create_inode(fs.root_inode(), "blub", kapi::filesystem::file_type::regular);
+ auto new_inode = fs->create_inode(root, "blub", kapi::filesystem::file_type::regular, driver_data);
REQUIRE(new_inode);
auto buffer =
@@ -157,12 +176,6 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
std::string_view result{reinterpret_cast<char *>(read_buffer.data()), static_cast<size_t>(*bytes_read)};
REQUIRE(result == "Hello");
}
-
- THEN("a symbolic link is not implemented yet")
- {
- REQUIRE_THROWS_AS(fs.create_inode(fs.root_inode(), "blub", kapi::filesystem::file_type::symbolic_link),
- kernel::tests::cpu::halt);
- }
}
}
@@ -176,11 +189,11 @@ SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
THEN("mount fails with invalid_magic_number")
{
- REQUIRE(fs.mount(dev_inode).error() == kernel::filesystem::ext2::ext2_errc::invalid_magic_number);
+ REQUIRE(fs->mount(dev_inode).error() == kernel::filesystem::ext2::ext2_errc::invalid_magic_number);
}
}
}
@@ -198,8 +211,11 @@ SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesys
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = fs->mount(dev_inode);
+ REQUIRE(mount);
+ auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>(mount->second);
+ REQUIRE(mount_state);
auto inode_data = kernel::filesystem::ext2::inode_data{};
inode_data.block[0] = 7;
@@ -221,18 +237,20 @@ SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesys
auto const doubly_start = singly_start + numbers_per_block;
auto const triply_start = doubly_start + numbers_per_block * numbers_per_block;
+ auto inode = kernel::filesystem::ext2::inode{42, inode_data};
+
THEN("mapping resolves direct, singly, doubly and triply indirect indexes")
{
- REQUIRE(fs.map_inode_block_index_to_global_block_number(0, inode_data) == 7);
- REQUIRE(fs.map_inode_block_index_to_global_block_number(singly_start, inode_data) == 31);
- REQUIRE(fs.map_inode_block_index_to_global_block_number(doubly_start, inode_data) == 42);
- REQUIRE(fs.map_inode_block_index_to_global_block_number(triply_start, inode_data) == 53);
+ REQUIRE(fs->inode_block_number(0, inode, *mount_state) == 7);
+ REQUIRE(fs->inode_block_number(singly_start, inode, *mount_state) == 31);
+ REQUIRE(fs->inode_block_number(doubly_start, inode, *mount_state) == 42);
+ REQUIRE(fs->inode_block_number(triply_start, inode, *mount_state) == 53);
}
THEN("mapping returns error for out-of-range indexes")
{
auto const beyond_triply = triply_start + numbers_per_block * numbers_per_block * numbers_per_block;
- REQUIRE(!fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data));
+ REQUIRE(!fs->inode_block_number(beyond_triply, inode, *mount_state));
}
}
}
@@ -270,8 +288,11 @@ SCENARIO("Ext2 block writing includes direct and all indirect levels", "[filesys
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 fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = fs->mount(dev_inode);
+ REQUIRE(mount);
+ auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>(mount->second);
+ REQUIRE(mount_state);
auto read_u32 = [&](kstd::bytes offset) -> uint32_t {
auto value = uint32_t{0};
@@ -293,26 +314,32 @@ SCENARIO("Ext2 block writing includes direct and all indirect levels", "[filesys
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));
+ auto batch = kernel::filesystem::ext2::write_batch{*fs, *mount_state};
+
+ REQUIRE(fs->write_global_block_number_to_inode_block_index(0, inode_data, direct_global_block_number,
+ *mount_state, batch));
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(fs->write_global_block_number_to_inode_block_index(singly_start, inode_data, singly_global_block_number,
+ *mount_state, batch));
REQUIRE(inode_data.block[12] == 16);
REQUIRE(read_u32(static_cast<kstd::bytes>(inode_data.block[12]) * block_size.value) ==
singly_global_block_number);
- REQUIRE(fs.write_global_block_number_to_inode_block_index(singly_start + 1, inode_data,
- singly_global_block_number + 1));
+ REQUIRE(fs->write_global_block_number_to_inode_block_index(singly_start + 1, inode_data,
+ singly_global_block_number + 1, *mount_state, batch));
REQUIRE(read_u32(static_cast<kstd::bytes>(inode_data.block[12]) * block_size.value +
1 * kstd::size_of<uint32_t>()) == singly_global_block_number + 1);
- REQUIRE(fs.write_global_block_number_to_inode_block_index(doubly_start, inode_data, doubly_global_block_number));
+ REQUIRE(fs->write_global_block_number_to_inode_block_index(doubly_start, inode_data, doubly_global_block_number,
+ *mount_state, batch));
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(fs->write_global_block_number_to_inode_block_index(triply_start, inode_data, triply_global_block_number,
+ *mount_state, batch));
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);
@@ -320,10 +347,12 @@ SCENARIO("Ext2 block writing includes direct and all indirect levels", "[filesys
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);
+ auto inode = kernel::filesystem::ext2::inode{42, inode_data};
+
+ REQUIRE(fs->inode_block_number(0, inode, *mount_state) == direct_global_block_number);
+ REQUIRE(fs->inode_block_number(singly_start, inode, *mount_state) == singly_global_block_number);
+ REQUIRE(fs->inode_block_number(doubly_start, inode, *mount_state) == doubly_global_block_number);
+ REQUIRE(fs->inode_block_number(triply_start, inode, *mount_state) == triply_global_block_number);
}
}
}
diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp
index 8da80191..075d6c5f 100644
--- a/kernel/kernel/filesystem/ext2/inode.cpp
+++ b/kernel/kernel/filesystem/ext2/inode.cpp
@@ -1,12 +1,19 @@
#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/error.hpp>
+#include <kernel/filesystem/ext2/error.hpp>
#include <kernel/filesystem/ext2/filesystem.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
+#include <kernel/filesystem/ext2/write_batch.hpp>
#include <kernel/filesystem/inode.hpp>
+#include <kernel/filesystem/mount.hpp>
#include <kapi/filesystem.hpp>
#include <kapi/system.hpp>
#include <kstd/cstring.hpp>
+#include <kstd/memory.hpp>
+#include <kstd/mutex.hpp>
#include <kstd/result.hpp>
#include <kstd/units.hpp>
@@ -20,67 +27,42 @@ using namespace kstd::units_literals;
namespace kernel::filesystem::ext2
{
- inode::inode(filesystem * fs, uint32_t inode_number, inode_data const & data)
- : m_filesystem(fs)
- , m_inode_number(inode_number)
+ inode::inode(uint32_t inode_number, inode_data const & data)
+ : m_inode_number(inode_number)
, m_data(data)
- {
- if (!m_filesystem)
- {
- kapi::system::panic("[EXT2] ext2::inode constructed with filesystem null pointer");
- }
- }
+ {}
auto inode::read(std::span<std::byte> buffer, kstd::bytes offset) const -> kstd::result<kstd::bytes>
{
- auto const max_readable = this->size() - offset;
- auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable);
-
if (is_symbolic_link() && this->size() <= kstd::bytes{sizeof(m_data.block)})
{
+ auto const max_readable = this->size() - offset;
+ auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable);
auto inline_target = reinterpret_cast<uint8_t const *>(m_data.block.data());
kstd::libc::memcpy(buffer.data(), inline_target + offset, requested_size.value);
return requested_size;
}
- auto block_index = offset / m_filesystem->block_size();
- auto in_block_offset = offset % m_filesystem->block_size();
-
- auto bytes_read = 0_B;
-
- while (bytes_read < requested_size)
+ auto filesystem = get_filesystem();
+ if (!filesystem)
{
- auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data);
- if (!block_number)
- {
- break;
- }
+ return kstd::failure(filesystem.error());
+ }
- auto const bytes_to_read = std::min(requested_size - bytes_read, m_filesystem->block_size() - in_block_offset);
- if (block_number == 0)
- {
- kstd::libc::memset(buffer.data() + bytes_read, 0, bytes_to_read.value);
- bytes_read += bytes_to_read;
- }
- else
- {
- auto const block_start_offset = block_number.value() * m_filesystem->block_size();
- auto const read_offset = block_start_offset + in_block_offset;
- auto const read_result =
- m_filesystem->backing_inode()->read(buffer.subspan(bytes_read.value, bytes_to_read.value), read_offset);
- if (!read_result)
- {
- return kstd::failure(read_result.error());
- }
-
- bytes_read += read_result.value();
- }
+ auto state = get_driver_data();
+ if (!state)
+ {
+ return kstd::failure(state.error());
+ }
- block_index++;
- in_block_offset = 0_B; // After the first block, we always start at the beginning of the block
+ auto backing_inode = (*state)->backing_inode;
+ if (!(*state)->backing_inode)
+ {
+ return kstd::failure(vfs_errc::not_mounted);
}
- return bytes_read;
+ auto guard = kstd::lock_guard{(*state)->lock};
+ return (*filesystem)->do_read(*this, buffer, offset, **state);
}
auto inode::write(std::span<std::byte const> buffer, kstd::bytes offset) -> kstd::result<kstd::bytes>
@@ -93,46 +75,65 @@ namespace kernel::filesystem::ext2
// TODO check maximum file size of filesystem
// TODO handle sparse files
- auto const new_inode_size = std::max(this->size(), offset + kstd::bytes{buffer.size()});
+ auto filesystem = get_filesystem();
+ if (!filesystem)
+ {
+ return kstd::failure(filesystem.error());
+ }
+
+ auto state = get_driver_data();
+ if (!state)
+ {
+ return kstd::failure(state.error());
+ }
+
+ auto backing_inode = (*state)->backing_inode;
+ if (!(*state)->backing_inode)
+ {
+ return kstd::failure(vfs_errc::not_mounted);
+ }
+
+ auto guard = kstd::lock_guard{(*state)->lock};
+ auto batch = write_batch{**filesystem, **state};
+
+ auto const new_inode_size = std::max((*filesystem)->data_size(*this, **state), offset + kstd::bytes{buffer.size()});
- auto const current_block_count = m_filesystem->inode_block_count(m_data);
- auto const max_new_inode_size_without_new_blocks = current_block_count * m_filesystem->block_size();
+ auto block_size = (*filesystem)->block_size(**state);
+ auto const current_block_count = (*filesystem)->block_count(*this, **state);
+ auto const max_new_inode_size_without_new_blocks = current_block_count * block_size;
if (new_inode_size > max_new_inode_size_without_new_blocks)
{
auto const additional_blocks_needed =
- (new_inode_size - max_new_inode_size_without_new_blocks + m_filesystem->block_size() - 1_B) /
- m_filesystem->block_size();
+ (new_inode_size - max_new_inode_size_without_new_blocks + block_size - 1_B) / block_size;
- if (!append_blocks(additional_blocks_needed))
+ if (!append_blocks(additional_blocks_needed, batch))
{
- // TODO handle out of space
- kapi::system::panic("[EXT2] Failed to allocate blocks for inode write");
+ return kstd::failure(ext2_errc::not_enough_free_blocks);
}
}
- auto block_index = offset / m_filesystem->block_size();
- auto in_block_offset = offset % m_filesystem->block_size();
+ auto block_index = offset / block_size;
+ auto in_block_offset = offset % block_size;
auto bytes_written = 0_B;
while (bytes_written < kstd::bytes{buffer.size()})
{
- auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data);
+ auto const block_number = (*filesystem)->inode_block_number(block_index, *this, **state);
// TODO BA-FS26 if blocknumber == 0 --> handle sparse file
if (!block_number)
{
return kstd::failure(block_number.error());
}
- auto const bytes_to_write =
- std::min(kstd::bytes{buffer.size()} - bytes_written, m_filesystem->block_size() - in_block_offset);
+ auto const bytes_to_write = std::min(kstd::bytes{buffer.size()} - bytes_written, block_size - in_block_offset);
- auto const block_start_offset = block_number.value() * m_filesystem->block_size();
+ auto const block_start_offset = block_number.value() * block_size;
auto const write_offset = block_start_offset + in_block_offset;
auto const write_result =
- m_filesystem->backing_inode()->write(buffer.subspan(bytes_written.value, bytes_to_write.value), write_offset);
+ backing_inode->write(buffer.subspan(bytes_written.value, bytes_to_write.value), write_offset);
if (!write_result)
{
return kstd::failure(write_result.error());
@@ -144,48 +145,25 @@ namespace kernel::filesystem::ext2
in_block_offset = 0_B;
}
- set_size(std::max(this->size(), offset + bytes_written));
- if (auto write_result = m_filesystem->write_inode(m_inode_number, m_data); !write_result)
- {
- return kstd::failure(write_result.error());
- }
-
+ (*filesystem)
+ ->set_data_size(*this, std::max((*filesystem)->data_size(*this, **state), offset + bytes_written), **state);
+ batch.mark_inode(*this);
return bytes_written;
}
- auto inode::append_blocks(size_t count) -> bool
+ auto inode::is_block_device() const -> bool
{
- auto new_blocks = m_filesystem->allocate_blocks(count);
- if (!new_blocks)
- {
- return false;
- }
-
- for (auto i = 0uz; i < new_blocks->size(); ++i)
- {
- auto const block_index = m_filesystem->inode_block_count(m_data) + i;
- auto const global_block_number = new_blocks->at(i);
-
- 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);
- return true;
+ return (m_data.mode & constants::mode_mask) == constants::mode_block_device;
}
- [[nodiscard]] auto inode::data() const -> inode_data const &
+ auto inode::is_character_device() const -> bool
{
- return m_data;
+ return (m_data.mode & constants::mode_mask) == constants::mode_character_device;
}
- [[nodiscard]] auto inode::data_mutable() -> inode_data &
+ auto inode::is_directory() const -> bool
{
- return m_data;
+ return (m_data.mode & constants::mode_mask) == constants::mode_directory;
}
auto inode::is_regular() const -> bool
@@ -193,24 +171,24 @@ namespace kernel::filesystem::ext2
return (m_data.mode & constants::mode_mask) == constants::mode_regular;
}
- auto inode::is_directory() const -> bool
- {
- return (m_data.mode & constants::mode_mask) == constants::mode_directory;
- }
-
auto inode::is_symbolic_link() const -> bool
{
return (m_data.mode & constants::mode_mask) == constants::mode_symbolic_link;
}
- auto inode::is_block_device() const -> bool
+ auto inode::raw_device() const -> std::optional<kapi::filesystem::device_number>
{
- return (m_data.mode & constants::mode_mask) == constants::mode_block_device;
- }
+ if (!is_device())
+ {
+ return std::nullopt;
+ }
- auto inode::is_character_device() const -> bool
- {
- return (m_data.mode & constants::mode_mask) == constants::mode_character_device;
+ auto const raw_device_number = m_data.block[0];
+
+ return kapi::filesystem::device_number{
+ .major = static_cast<std::uint8_t>((raw_device_number >> 8) & 0xff),
+ .minor = static_cast<std::uint8_t>(raw_device_number & 0xff),
+ };
}
auto inode::status() const -> kstd::result<kapi::filesystem::file_status>
@@ -229,40 +207,89 @@ namespace kernel::filesystem::ext2
return result;
}
- auto inode::raw_device() const -> std::optional<kapi::filesystem::device_number>
+ auto inode::append_blocks(size_t count, write_batch & batch) -> bool
{
- if (!is_device())
+ auto filesystem = get_filesystem();
+ if (!filesystem)
{
- return std::nullopt;
+ return false;
}
- auto const raw_device_number = m_data.block[0];
+ auto state = get_driver_data();
+ if (!state)
+ {
+ return false;
+ }
- return kapi::filesystem::device_number{
- .major = static_cast<std::uint8_t>((raw_device_number >> 8) & 0xff),
- .minor = static_cast<std::uint8_t>(raw_device_number & 0xff),
- };
+ auto new_blocks = (*filesystem)->allocate_blocks(count, **state, batch);
+ if (!new_blocks)
+ {
+ return false;
+ }
+
+ for (auto i = 0uz; i < new_blocks->size(); ++i)
+ {
+ auto const block_index = (*filesystem)->block_count(*this, **state) + i;
+ auto const global_block_number = new_blocks->at(i);
+
+ if (auto write_result = (*filesystem)
+ ->write_global_block_number_to_inode_block_index(block_index, m_data,
+ global_block_number, **state, batch);
+ !write_result)
+ {
+ return false;
+ }
+ }
+
+ (*filesystem)->update_inode_block_count(m_data, count, **state);
+ return true;
}
- auto inode::size() const -> kstd::bytes
+ auto inode::set_size(kstd::bytes new_size) -> void
{
- uint64_t size = m_data.size;
+ auto filesystem = get_filesystem();
+ if (!filesystem)
+ {
+ return;
+ }
- if (m_filesystem->revision_level() > constants::good_old_revision && is_regular())
+ auto driver_data = get_driver_data();
+ if (!driver_data)
{
- size |= static_cast<uint64_t>(m_data.dir_acl) << 32;
+ return;
}
- return kstd::bytes{size};
+ auto state = static_pointer_cast<mount_state>(*driver_data);
+ auto guard = kstd::lock_guard{state->lock};
+ return (*filesystem)->set_data_size(*this, new_size, *state);
}
- auto inode::set_size(kstd::bytes new_size) -> void
+ [[nodiscard]] auto inode::data() -> inode_data &
{
- if (m_filesystem->revision_level() > constants::good_old_revision && is_regular())
+ return m_data;
+ }
+
+ [[nodiscard]] auto inode::data() const -> inode_data const &
+ {
+ return m_data;
+ }
+
+ auto inode::size() const -> kstd::bytes
+ {
+ auto filesystem = get_filesystem();
+ if (!filesystem)
{
- m_data.dir_acl = static_cast<uint32_t>(new_size.value >> 32);
+ return 0_B;
}
- m_data.size = static_cast<uint32_t>(new_size.value);
+
+ auto state = get_driver_data();
+ if (!state)
+ {
+ return 0_B;
+ }
+
+ auto guard = kstd::lock_guard{(*state)->lock};
+ return (*filesystem)->data_size(*this, **state);
}
[[nodiscard]] auto inode::number() const -> uint32_t
@@ -270,4 +297,44 @@ namespace kernel::filesystem::ext2
return m_inode_number;
}
+ auto inode::get_filesystem() -> kstd::result<kstd::shared_ptr<filesystem>>
+ {
+ if (auto mount = owning_mount().lock())
+ {
+ return static_pointer_cast<filesystem>(mount->filesystem());
+ }
+
+ return kstd::failure(vfs_errc::not_mounted);
+ }
+
+ auto inode::get_filesystem() const -> kstd::result<kstd::shared_ptr<filesystem const>>
+ {
+ if (auto mount = owning_mount().lock())
+ {
+ return static_pointer_cast<filesystem>(mount->filesystem());
+ }
+
+ return kstd::failure(vfs_errc::not_mounted);
+ }
+
+ auto inode::get_driver_data() -> kstd::result<kstd::shared_ptr<mount_state>>
+ {
+ if (auto mount = owning_mount().lock())
+ {
+ return static_pointer_cast<mount_state>(mount->driver_data());
+ }
+
+ return kstd::failure(vfs_errc::not_mounted);
+ }
+
+ auto inode::get_driver_data() const -> kstd::result<kstd::shared_ptr<mount_state const>>
+ {
+ if (auto mount = owning_mount().lock())
+ {
+ return static_pointer_cast<mount_state const>(mount->driver_data());
+ }
+
+ return kstd::failure(vfs_errc::not_mounted);
+ }
+
} // namespace kernel::filesystem::ext2
diff --git a/kernel/kernel/filesystem/ext2/inode.hpp b/kernel/kernel/filesystem/ext2/inode.hpp
index 61961f6c..5393a853 100644
--- a/kernel/kernel/filesystem/ext2/inode.hpp
+++ b/kernel/kernel/filesystem/ext2/inode.hpp
@@ -1,6 +1,8 @@
#ifndef TEACHOS_KERNEL_FILESYSTEM_EXT2_INODE_HPP
#define TEACHOS_KERNEL_FILESYSTEM_EXT2_INODE_HPP
+#include <kernel/filesystem/ext2/mount_state.hpp>
+#include <kernel/filesystem/ext2/write_batch.hpp>
#include <kernel/filesystem/inode.hpp>
#include <kapi/filesystem.hpp>
@@ -44,33 +46,33 @@ namespace kernel::filesystem::ext2
struct inode : kernel::filesystem::inode
{
+ //! @name Construction/Destruction
+ //! @{
+
//! Create an ext2 inode associated with the given filesystem.
//!
- //! @param fs The ext2 filesystem that this inode belongs to.
//! @param inode_number The inode number on disk.
//! @param data The data associated with this inode, read from the disk.
- explicit inode(filesystem * fs, uint32_t inode_number, inode_data const & data);
+ explicit inode(uint32_t inode_number, inode_data const & data);
+
+ //! @}
+
+ //! @name I/O Operations
+ //! @{
[[nodiscard]] auto read(std::span<std::byte> buffer, kstd::bytes offset) const
-> kstd::result<kstd::bytes> override;
auto write(std::span<std::byte const> buffer, kstd::bytes offset) -> kstd::result<kstd::bytes> override;
- //! Append the specified number of blocks to this inode.
- //!
- //! @param count The number of blocks to append.
- //! @return true if the blocks were successfully appended, false otherwise.
- auto append_blocks(size_t count) -> bool;
+ //! @}
- //! Get the data associated with this inode.
- //!
- //! @return A const reference to the inode data.
- [[nodiscard]] auto data() const -> inode_data const &;
+ //! @name Property Checking
+ //! @{
- //! Get the data associated with this inode.
- //!
- //! @return A reference to the inode data.
- [[nodiscard]] auto data_mutable() -> inode_data &;
+ [[nodiscard]] auto is_block_device() const -> bool override;
+
+ [[nodiscard]] auto is_character_device() const -> bool override;
[[nodiscard]] auto is_directory() const -> bool override;
@@ -78,32 +80,80 @@ namespace kernel::filesystem::ext2
[[nodiscard]] auto is_symbolic_link() const -> bool override;
- [[nodiscard]] auto is_block_device() const -> bool override;
+ //! @}
- [[nodiscard]] auto is_character_device() const -> bool override;
+ //! @name POSIX Information Access
+ //! @{
+
+ [[nodiscard]] auto raw_device() const -> std::optional<kapi::filesystem::device_number> override;
[[nodiscard]] auto status() const -> kstd::result<kapi::filesystem::file_status> override;
- [[nodiscard]] auto raw_device() const -> std::optional<kapi::filesystem::device_number> override;
+ //! @}
- //! Get the size of the file represented by this inode.
+ //! @name Property Manipulation
+ //! @{
+
+ //! Append the specified number of blocks to this inode.
//!
- //! @return The size of the file in bytes.
- [[nodiscard]] auto size() const -> kstd::bytes;
+ //! @param count The number of blocks to append.
+ //! @param batch The active write batch.
+ //! @return true if the blocks were successfully appended, false otherwise.
+ auto append_blocks(size_t count, write_batch & batch) -> bool;
//! Set the size of the file represented by this inode.
//!
//! @param new_size The new size of the file in bytes.
auto set_size(kstd::bytes new_size) -> void;
+ //! @}
+
+ //! @name Ext2 Information Queries
+ //! @{
+
+ //! Get the data associated with this inode.
+ //!
+ //! @return A reference to the inode data.
+ [[nodiscard]] auto data() -> inode_data &;
+
+ //! Get the data associated with this inode.
+ //!
+ //! @return A const reference to the inode data.
+ [[nodiscard]] auto data() const -> inode_data const &;
+
+ //! Get the size of the file represented by this inode.
+ //!
+ //! @return The size of the file in bytes.
+ [[nodiscard]] auto size() const -> kstd::bytes;
+
//! @brief Get the inode number of this inode.
//!
//! @return The inode number.
[[nodiscard]] auto number() const -> uint32_t;
+ //! @}
+
private:
- //! The filesystem this inode belongs to.
- filesystem * m_filesystem;
+ //! Get the filesystem driver associated with this inode.
+ //!
+ //! @return The filesystem driver on success, and error otherwise.
+ [[nodiscard]] auto get_filesystem() -> kstd::result<kstd::shared_ptr<filesystem>>;
+
+ //! Get the filesystem driver associated with this inode.
+ //!
+ //! @return The filesystem driver on success, and error otherwise.
+ [[nodiscard]] auto get_filesystem() const -> kstd::result<kstd::shared_ptr<filesystem const>>;
+
+ //! Get the filesystem driver data associated with this inode.
+ //!
+ //! @return The filesystem driver data on success, and error otherwise.
+ [[nodiscard]] auto get_driver_data() -> kstd::result<kstd::shared_ptr<mount_state>>;
+
+ //! Get the filesystem driver data associated with this inode.
+ //!
+ //! @return The filesystem driver data on success, and error otherwise.
+ [[nodiscard]] auto get_driver_data() const -> kstd::result<kstd::shared_ptr<mount_state const>>;
+
//! The inode number on disk.
uint32_t m_inode_number{};
//! The inode data.
diff --git a/kernel/kernel/filesystem/ext2/inode.tests.cpp b/kernel/kernel/filesystem/ext2/inode.tests.cpp
index 13e9d414..eab0aabd 100644
--- a/kernel/kernel/filesystem/ext2/inode.tests.cpp
+++ b/kernel/kernel/filesystem/ext2/inode.tests.cpp
@@ -4,8 +4,10 @@
#include <kernel/filesystem/device_inode.hpp>
#include <kernel/filesystem/ext2/block_group_descriptor.hpp>
#include <kernel/filesystem/ext2/filesystem.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
#include <kernel/filesystem/ext2/superblock.hpp>
#include <kernel/filesystem/filesystem.hpp>
+#include <kernel/filesystem/mount.hpp>
#include <kernel/test_support/cpu.hpp>
#include <kernel/test_support/devices/block_device.hpp>
#include <kernel/test_support/filesystem/ext2.hpp>
@@ -14,6 +16,7 @@
#include <kapi/filesystem.hpp>
#include <kstd/memory.hpp>
+#include <kstd/span.hpp>
#include <kstd/units.hpp>
#include <kstd/vector.hpp>
@@ -42,7 +45,7 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]"
THEN("the inode is initialized with regular file mode in data and has the kind regular")
{
data.mode = kernel::filesystem::ext2::constants::mode_regular;
- auto inode = kernel::filesystem::ext2::inode(&fs, 42, data);
+ auto inode = kernel::filesystem::ext2::inode(42, data);
REQUIRE(inode.is_regular());
REQUIRE_FALSE(inode.is_directory());
@@ -53,7 +56,7 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]"
THEN("the inode is initialized with directory mode in data and has the kind directory")
{
data.mode = kernel::filesystem::ext2::constants::mode_directory;
- auto inode = kernel::filesystem::ext2::inode(&fs, 42, data);
+ auto inode = kernel::filesystem::ext2::inode(42, data);
REQUIRE_FALSE(inode.is_regular());
REQUIRE(inode.is_directory());
@@ -64,7 +67,7 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]"
THEN("the inode is initialized with symbolic link mode in data and has the kind symbolic link")
{
data.mode = kernel::filesystem::ext2::constants::mode_symbolic_link;
- auto inode = kernel::filesystem::ext2::inode(&fs, 42, data);
+ auto inode = kernel::filesystem::ext2::inode(42, data);
REQUIRE_FALSE(inode.is_regular());
REQUIRE_FALSE(inode.is_directory());
@@ -75,7 +78,7 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]"
THEN("the inode is initialized with zero mode in data and has no specific kind")
{
data.mode = 0;
- auto inode = kernel::filesystem::ext2::inode(&fs, 42, data);
+ auto inode = kernel::filesystem::ext2::inode(42, data);
REQUIRE_FALSE(inode.is_regular());
REQUIRE_FALSE(inode.is_directory());
@@ -83,14 +86,6 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]"
REQUIRE_FALSE(inode.is_symbolic_link());
}
}
-
- GIVEN("no filesystem (null pointer)")
- {
- THEN("constructing an inode with a null filesystem pointer panics")
- {
- REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode(nullptr, 42, {}), kernel::tests::cpu::halt);
- }
- }
}
SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 inode reads from real image",
@@ -108,15 +103,22 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(boot_device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ auto driver_data = (*mount)->driver_data();
+ REQUIRE(mount);
- auto information = fs.lookup(fs.root_inode(), "information");
+ auto information = fs->lookup(root, "information", driver_data);
REQUIRE(information);
- auto file = fs.lookup(*information, "info_1.txt");
+ (*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);
+
THEN("reading from offset zero returns expected file prefix")
{
auto buffer = kstd::vector<std::byte>(6);
@@ -150,8 +152,10 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ REQUIRE(mount);
auto data = kernel::filesystem::ext2::inode_data{};
data.block[0] = 30;
@@ -162,7 +166,8 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste
kernel::tests::filesystem::ext2::write_bytes(*device, 30 * block_size, "Hello", 5_B);
kernel::tests::filesystem::ext2::write_bytes(*device, 31 * block_size, "World!", 6_B);
- auto inode = kernel::filesystem::ext2::inode{&fs, 42, data};
+ auto inode = kernel::filesystem::ext2::inode{42, data};
+ inode.set_owning_mount(*mount);
auto buffer = kstd::vector<std::byte>(data.size, std::byte{0xAB});
@@ -188,8 +193,10 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ REQUIRE(mount);
auto data = kernel::filesystem::ext2::inode_data{};
data.block[0] = 30;
@@ -204,7 +211,8 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste
kernel::tests::filesystem::ext2::write_bytes(*device, 50 * block_size, "Blub", 4_B);
kernel::tests::filesystem::ext2::write_bytes(*device, 51 * block_size, "World!", 6_B);
- auto inode = kernel::filesystem::ext2::inode{&fs, 42, data};
+ auto inode = kernel::filesystem::ext2::inode{42, data};
+ inode.set_owning_mount(*mount);
auto buffer = kstd::vector<std::byte>(data.size, std::byte{0xAB});
@@ -233,14 +241,17 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ REQUIRE(mount);
auto data = kernel::filesystem::ext2::inode_data{};
data.block[12] = 0;
data.size = block_size.value * 15;
- auto inode = kernel::filesystem::ext2::inode{&fs, 42, data};
+ auto inode = kernel::filesystem::ext2::inode{42, data};
+ inode.set_owning_mount(*mount);
auto buffer = kstd::vector<std::byte>(block_size.value * 15, std::byte{0xAB});
@@ -264,8 +275,10 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]")
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ REQUIRE(mount);
auto inode_data = kernel::filesystem::ext2::inode_data{};
inode_data.size = block_size.value * 2;
@@ -273,7 +286,8 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]")
kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6_B, "Hello ", 6_B);
inode_data.block[1] = 21;
kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size, "World!", 6_B);
- auto inode = kernel::filesystem::ext2::inode{&fs, 42, inode_data};
+ auto inode = kernel::filesystem::ext2::inode{42, inode_data};
+ inode.set_owning_mount(*mount);
auto buffer = kstd::vector<std::byte>(12, std::byte{0x00});
@@ -302,14 +316,23 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(boot_device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ auto driver_data = (*mount)->driver_data();
+ REQUIRE(mount);
- auto information = fs.lookup(fs.root_inode(), "information");
+ auto information = fs->lookup(root, "information", driver_data);
REQUIRE(information);
- auto file = fs.lookup(*information, "info_1.txt");
+ (*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);
+ REQUIRE(mount_state);
THEN("writing to the inode updates backing device and the inode size")
{
@@ -333,24 +356,23 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in
THEN("writing allocates new blocks and updates the inode size")
{
- auto inode_data = static_cast<kernel::filesystem::ext2::inode *>(file.value().get())->data();
+ auto inode = static_pointer_cast<kernel::filesystem::ext2::inode>(file.value());
- auto const block_size = fs.block_size();
- auto const expected_allocated_blocks = 32 * fs.inode_block_count(inode_data);
+ auto const block_size = fs->block_size(*mount_state);
+ auto const expected_allocated_blocks = 32 * fs->block_count(*inode, *mount_state);
auto write_buffer = kstd::vector<std::byte>(block_size.value * expected_allocated_blocks, std::byte{'A'});
- auto const bytes_written = file.value()->write(write_buffer, 0_B);
+ auto const bytes_written = inode->write(write_buffer, 0_B);
REQUIRE(bytes_written == block_size * expected_allocated_blocks);
auto read_buffer = kstd::vector<std::byte>(block_size.value * expected_allocated_blocks, std::byte{0x00});
- auto const bytes_read = file.value()->read(read_buffer, 0_B);
+ auto const bytes_read = inode->read(read_buffer, 0_B);
REQUIRE(bytes_read == block_size * expected_allocated_blocks);
REQUIRE(std::ranges::all_of(read_buffer, [](std::byte c) { return c == std::byte{'A'}; }));
- auto new_inode_data = static_cast<kernel::filesystem::ext2::inode *>(file.value().get())->data();
- REQUIRE(new_inode_data.size == write_buffer.size());
- REQUIRE(fs.inode_block_count(new_inode_data) == expected_allocated_blocks);
+ REQUIRE(inode->size().value == write_buffer.size());
+ REQUIRE(fs->block_count(*inode, *mount_state) == expected_allocated_blocks);
}
THEN("writing to a an inode of type directory panics")
@@ -394,8 +416,12 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]"
REQUIRE(dev_inode->write(block_bitmap, block_group_descriptor.block_bitmap * block_size));
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ REQUIRE(mount);
+ auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>((*mount)->driver_data());
+ REQUIRE(mount_state);
auto inode_data = kernel::filesystem::ext2::inode_data{};
inode_data.size = 5;
@@ -405,10 +431,12 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]"
inode_data.mode = kernel::filesystem::ext2::constants::mode_regular;
uint32_t inode_number = 3;
- auto const inode_data_offset = block_group_descriptor.inode_table * block_size + (inode_number - 1) * fs.inode_size();
+ auto const inode_data_offset =
+ block_group_descriptor.inode_table * block_size + (inode_number - 1) * fs->inode_size(*mount_state);
- auto inode = kernel::filesystem::ext2::inode{&fs, inode_number, inode_data};
- REQUIRE(dev_inode->write(as_bytes(std::span{&inode, 1}), inode_data_offset));
+ auto inode = kernel::filesystem::ext2::inode{inode_number, inode_data};
+ inode.set_owning_mount(*mount);
+ REQUIRE(dev_inode->write(kstd::raw_bytes(inode), inode_data_offset));
auto const buffer =
kstd::vector{std::byte{'H'}, std::byte{'e'}, std::byte{'l'}, std::byte{'l'}, std::byte{'o'}, std::byte{' '},
@@ -418,8 +446,9 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]"
{
auto do_read_inode = [&]() {
auto inode_data = kernel::filesystem::ext2::inode_data{};
- REQUIRE(dev_inode->read(as_writable_bytes(std::span{&inode_data, 1}), inode_data_offset));
- auto read_inode = kernel::filesystem::ext2::inode{&fs, inode_number, inode_data};
+ REQUIRE(dev_inode->read(kstd::raw_bytes(inode_data), inode_data_offset));
+ auto read_inode = kernel::filesystem::ext2::inode{inode_number, inode_data};
+ read_inode.set_owning_mount(*mount);
return read_inode;
};
@@ -500,7 +529,7 @@ SCENARIO("Ext2 inode write across block boundaries", "[filesystem][ext2][inode]"
THEN("writing over the current size and over the number of free blocks")
{
auto const offset = 65 * block_size;
- REQUIRE_THROWS_AS(inode.write(buffer, offset), kernel::tests::cpu::halt);
+ REQUIRE_FALSE(inode.write(buffer, offset));
}
}
}
@@ -528,8 +557,10 @@ SCENARIO("Ext2 inode get_size() and set_size() handles size correctly depending
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ REQUIRE(mount);
auto data = kernel::filesystem::ext2::inode_data{};
data.size = 256;
@@ -539,7 +570,8 @@ SCENARIO("Ext2 inode get_size() and set_size() handles size correctly depending
uint32_t expected_dir_acl_after_set_size) {
data.mode = mode;
- auto inode = kernel::filesystem::ext2::inode{&fs, 42, data};
+ auto inode = kernel::filesystem::ext2::inode{42, data};
+ inode.set_owning_mount(*mount);
REQUIRE(inode.size() == expected_initial_size);
@@ -571,8 +603,10 @@ SCENARIO("Ext2 inode get_size() and set_size() handles size correctly depending
auto dev_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
- auto fs = kernel::filesystem::ext2::filesystem{};
- REQUIRE(fs.mount(dev_inode));
+ auto fs = kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ auto mount = kernel::filesystem::mount::create(nullptr, fs, nullptr, nullptr, dev_inode);
+ auto root = (*mount)->root_dentry()->inode();
+ REQUIRE(mount);
auto data = kernel::filesystem::ext2::inode_data{};
data.size = 256;
@@ -582,7 +616,8 @@ SCENARIO("Ext2 inode get_size() and set_size() handles size correctly depending
uint32_t expected_dir_acl_after_set_size) {
data.mode = mode;
- auto inode = kernel::filesystem::ext2::inode{&fs, 42, data};
+ auto inode = kernel::filesystem::ext2::inode{42, data};
+ inode.set_owning_mount(*mount);
REQUIRE(inode.size() == expected_initial_size);
@@ -615,7 +650,7 @@ SCENARIO("Ext2 inode status()", "[filesystem][ext2][inode]")
data.block[0] = (1 << 8);
data.links_count = 1;
- auto inode = kernel::filesystem::ext2::inode(&fs, 42, data);
+ auto inode = kernel::filesystem::ext2::inode(42, data);
THEN("status() reports the real inode number, link count, and decoded raw device")
{
diff --git a/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp
index 9c9d162d..2d364f05 100644
--- a/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp
+++ b/kernel/kernel/filesystem/ext2/linked_directory_entry.hpp
@@ -1,19 +1,24 @@
#ifndef TEACHOS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP
#define TEACHOS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP
-#include <array>
#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;
uint8_t file_type;
- std::array<char, 255> name; // NOLINT(readability-magic-numbers)
+ char name_start; // NOLINT(readability-magic-numbers)
};
} // namespace kernel::filesystem::ext2
diff --git a/kernel/kernel/filesystem/ext2/module.cpp b/kernel/kernel/filesystem/ext2/module.cpp
new file mode 100644
index 00000000..e6b14307
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/module.cpp
@@ -0,0 +1,26 @@
+#include <kernel/filesystem/driver_registry.hpp>
+#include <kernel/filesystem/ext2/filesystem.hpp>
+#include <kernel/filesystem/filesystem.hpp>
+
+#include <kstd/memory.hpp>
+
+#include <string_view>
+
+namespace
+{
+ struct descriptor final : kernel::filesystem::driver_descriptor
+ {
+ [[nodiscard]] auto name() const noexcept -> std::string_view override
+ {
+ return "ext2";
+ }
+
+ [[nodiscard]] auto make_instance() const -> kernel::filesystem::filesystem::filesystem_ptr override
+ {
+ return kstd::make_shared<kernel::filesystem::ext2::filesystem>();
+ }
+ };
+
+ [[gnu::used]]
+ constexpr auto registration = kernel::filesystem::driver_module<descriptor>{};
+} // namespace
diff --git a/kernel/kernel/filesystem/ext2/mount_state.hpp b/kernel/kernel/filesystem/ext2/mount_state.hpp
new file mode 100644
index 00000000..ba705382
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/mount_state.hpp
@@ -0,0 +1,26 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_EXT2_MOUNT_STATE_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_EXT2_MOUNT_STATE_HPP
+
+#include <kernel/filesystem/ext2/block_group_descriptor.hpp>
+#include <kernel/filesystem/ext2/superblock.hpp>
+#include <kernel/filesystem/inode.hpp>
+
+#include <kapi/tracked_mutex.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/vector.hpp>
+
+namespace kernel::filesystem::ext2
+{
+
+ struct mount_state
+ {
+ struct superblock superblock{};
+ kstd::vector<block_group_descriptor> block_group_descriptors{};
+ kstd::shared_ptr<kernel::filesystem::inode> backing_inode{};
+ mutable kapi::tracked_mutex lock{};
+ };
+
+} // namespace kernel::filesystem::ext2
+
+#endif \ No newline at end of file
diff --git a/kernel/kernel/filesystem/ext2/write_batch.cpp b/kernel/kernel/filesystem/ext2/write_batch.cpp
new file mode 100644
index 00000000..e8663d21
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/write_batch.cpp
@@ -0,0 +1,67 @@
+#include <kernel/filesystem/ext2/write_batch.hpp>
+
+#include <kernel/filesystem/ext2/filesystem.hpp>
+#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
+
+#include <kapi/system.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <ranges>
+
+namespace kernel::filesystem::ext2
+{
+
+ write_batch::write_batch(filesystem & filesystem, mount_state & state)
+ : m_filesystem{filesystem}
+ , m_state{state}
+ {}
+
+ write_batch::~write_batch()
+ {
+ std::ranges::for_each(m_dirty_groups, [this](auto index) {
+ auto & descriptor = m_state.block_group_descriptors.at(index);
+ auto result = m_filesystem.write_block_group_descriptor(descriptor, index, m_state);
+
+ if (!result)
+ {
+ kapi::system::panic("[FS:ext2] failed to write block groups descriptor", result.error());
+ }
+ });
+
+ if (!m_dirty_groups.empty())
+ {
+ auto result = m_filesystem.write_superblock(m_state);
+ if (!result)
+ {
+ kapi::system::panic("[FS:ext2] failed to write superblock", result.error());
+ }
+ }
+
+ std::ranges::for_each(m_dirty_inodes, [this](auto inode) {
+ auto result = m_filesystem.write_inode(inode->number(), inode->data(), m_state);
+ if (!result)
+ {
+ kapi::system::panic("[FS:ext2] failed to write inode", result.error());
+ }
+ });
+ }
+
+ auto write_batch::mark_group(std::size_t index) -> void
+ {
+ if (std::ranges::find(m_dirty_groups, index) == std::ranges::end(m_dirty_groups))
+ {
+ m_dirty_groups.push_back(index);
+ }
+ }
+
+ auto write_batch::mark_inode(inode & inode) -> void
+ {
+ if (std::ranges::find(m_dirty_inodes, &inode) == std::ranges::end(m_dirty_inodes))
+ {
+ m_dirty_inodes.push_back(&inode);
+ }
+ }
+
+} // namespace kernel::filesystem::ext2
diff --git a/kernel/kernel/filesystem/ext2/write_batch.hpp b/kernel/kernel/filesystem/ext2/write_batch.hpp
new file mode 100644
index 00000000..f4000391
--- /dev/null
+++ b/kernel/kernel/filesystem/ext2/write_batch.hpp
@@ -0,0 +1,53 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_EXT2_WRITE_BATCH_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_EXT2_WRITE_BATCH_HPP
+
+#include <kstd/vector.hpp>
+
+#include <cstddef>
+
+namespace kernel::filesystem::ext2
+{
+
+ struct filesystem;
+ struct inode;
+ struct mount_state;
+
+ //! A RAII write batching type.
+ //!
+ //! Any metadata writes are first appended to the batch. On destruction, the write is committed to the filesystem.
+ struct write_batch
+ {
+ //! Construct a new write batch for the given filesystem and mount state.
+ //!
+ //! @param filesystem The filesystem driver to use for writes.
+ //! @param state The driver state to operate on.
+ write_batch(filesystem & filesystem, mount_state & state);
+
+ write_batch(write_batch const &) = delete;
+ write_batch(write_batch &&) = delete;
+ auto operator=(write_batch const &) = delete;
+ auto operator=(write_batch &&) = delete;
+
+ //! Commit and destroy the batch.
+ ~write_batch();
+
+ //! Mark the group with the given index as dirty.
+ //!
+ //! @param index The index of the group to mark.
+ auto mark_group(std::size_t index) -> void;
+
+ //! Mark the given inode as dirty.
+ //!
+ //! @param inode The inode to mark.
+ auto mark_inode(inode & inode) -> void;
+
+ private:
+ filesystem & m_filesystem;
+ mount_state & m_state;
+ kstd::vector<std::size_t> m_dirty_groups{};
+ kstd::vector<inode *> m_dirty_inodes{};
+ };
+
+} // namespace kernel::filesystem::ext2
+
+#endif
diff --git a/kernel/kernel/filesystem/file_type.hpp b/kernel/kernel/filesystem/file_type.hpp
deleted file mode 100644
index 71370ea6..00000000
--- a/kernel/kernel/filesystem/file_type.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef TEACHOS_KERNEL_FILESYSTEM_FILE_TYPE_HPP
-#define TEACHOS_KERNEL_FILESYSTEM_FILE_TYPE_HPP
-
-namespace kernel::filesystem
-{
-
- //! POSIX file types.
- enum struct file_type
- {
- block,
- character,
- pipe,
- socket,
- };
-
-} // namespace kernel::filesystem
-
-#endif
diff --git a/kernel/kernel/filesystem/file_type_bindings.hpp b/kernel/kernel/filesystem/file_type_bindings.hpp
index b4338024..da8e4efc 100644
--- a/kernel/kernel/filesystem/file_type_bindings.hpp
+++ b/kernel/kernel/filesystem/file_type_bindings.hpp
@@ -1,7 +1,6 @@
#ifndef TEACHOS_KERNEL_FILESYSTEM_FILE_TYPE_BINDINGS_HPP
#define TEACHOS_KERNEL_FILESYSTEM_FILE_TYPE_BINDINGS_HPP
-#include <kernel/filesystem/file_type.hpp>
#include <kernel/filesystem/reserved_numbers.hpp>
#include <kapi/filesystem.hpp>
@@ -15,15 +14,15 @@ namespace kernel::filesystem
struct file_type_binding // NOLINT(cppcoreguidelines-pro-type-member-init)
{
kapi::capabilities::facet_id facet;
- file_type posix_type;
+ kapi::filesystem::file_type posix_type;
};
//! Predefined bindings for facet + file type
//!
//! This array associates published facets with files types.
constexpr auto inline file_type_bindings = std::array{
- file_type_binding{kapi::filesystem::character_special_file::id, file_type::character},
- file_type_binding{ kapi::filesystem::block_special_file::id, file_type::block},
+ file_type_binding{kapi::filesystem::character_special_file::id, kapi::filesystem::file_type::character_device},
+ file_type_binding{ kapi::filesystem::block_special_file::id, kapi::filesystem::file_type::block_device},
};
} // namespace kernel::filesystem
diff --git a/kernel/kernel/filesystem/filesystem.cpp b/kernel/kernel/filesystem/filesystem.cpp
deleted file mode 100644
index 84bbdef6..00000000
--- a/kernel/kernel/filesystem/filesystem.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include <kernel/filesystem/filesystem.hpp>
-
-#include <kernel/filesystem/error.hpp>
-#include <kernel/filesystem/inode.hpp>
-#include <kernel/filesystem/type_registry.hpp>
-
-#include <kstd/memory.hpp>
-#include <kstd/result.hpp>
-#include <kstd/system_error.hpp>
-
-namespace kernel::filesystem
-{
-
- auto filesystem::probe_and_mount(kstd::shared_ptr<inode> const & backing_inode)
- -> kstd::result<kstd::shared_ptr<filesystem>>
- {
- for (auto & type : type_registry::get().all())
- {
- if (!type || !type->probe(backing_inode))
- {
- continue;
- }
-
- auto fs = type->make_instance();
-
- if (fs->mount(backing_inode))
- {
- return fs;
- }
- }
-
- return kstd::failure(vfs_errc::unsupported_filesystem);
- }
-
- auto filesystem::mount(kstd::shared_ptr<inode> const & backing_inode) -> kstd::result<void>
- {
- m_backing_inode = backing_inode;
- return kstd::success();
- }
-
- auto filesystem::root_inode() const -> kstd::shared_ptr<inode> const &
- {
- return m_root_inode;
- }
-
- auto filesystem::backing_inode() const -> kstd::shared_ptr<inode> const &
- {
- return m_backing_inode;
- }
-} // namespace kernel::filesystem \ No newline at end of file
diff --git a/kernel/kernel/filesystem/filesystem.hpp b/kernel/kernel/filesystem/filesystem.hpp
index 1a66c11c..f9fb2859 100644
--- a/kernel/kernel/filesystem/filesystem.hpp
+++ b/kernel/kernel/filesystem/filesystem.hpp
@@ -10,8 +10,10 @@
#include <kstd/system_error.hpp>
#include <kstd/vector.hpp>
+#include <cstdint>
#include <optional>
#include <string_view>
+#include <utility>
namespace kernel::filesystem
{
@@ -21,17 +23,19 @@ namespace kernel::filesystem
//! Filesystem implementations must derive from this class.
struct filesystem
{
+ using filesystem_ptr = kstd::shared_ptr<filesystem>;
+ using driver_data_ptr = kstd::shared_ptr<void>;
+ using inode_ptr = kstd::shared_ptr<inode>;
+ using mount_result = std::pair<inode_ptr, driver_data_ptr>;
+
//! Virtual destructor enabling polymorphic destruction.
virtual ~filesystem() = default;
- //! Probes the given inode to determine if it contains a recognizable filesystem, and if so, mount it.
- //!
- //! @warning This function panics if @p backing_inode is null.
+ //! Check if the data represented by the given inode is recognized as this filesystem.
//!
- //! @param backing_inode The inode to probe and mount.
- //! @return A pointer to the mounted filesystem instance on success, an error otherwise.
- auto static probe_and_mount(kstd::shared_ptr<inode> const & backing_inode)
- -> kstd::result<kstd::shared_ptr<filesystem>>;
+ //! @param inode The inode to probe, if any.
+ //! @return the priority (higher is better) of the match on success, an error otherwise.
+ [[nodiscard]] virtual auto probe(inode_ptr const & inode) const -> kstd::result<std::uint32_t> = 0;
//! Initializes the filesystem with the given inode.
//!
@@ -39,8 +43,8 @@ namespace kernel::filesystem
//! filesystem data (e.g. a file on an already mounted filesystem).
//!
//! @param backing_inode The inode to use as the backing inode for the filesystem.
- //! @return Nothing on success, and error otherwise.
- virtual auto mount(kstd::shared_ptr<inode> const & backing_inode) -> kstd::result<void>;
+ //! @return The root inode of the mounted filesystem on success, and error otherwise.
+ virtual auto mount(inode_ptr const & backing_inode) -> kstd::result<mount_result> = 0;
//! Find a child inode below the given parent inode with the specified name.
//!
@@ -49,35 +53,23 @@ namespace kernel::filesystem
//!
//! @param parent The parent inode.
//! @param name The name of the child inode to look up.
+ //! @param driver_data The per-mount data used by the driver.
//! @return A pointer to the requested child inode on success, an error otherwise.
- [[nodiscard]] virtual auto lookup(kstd::shared_ptr<inode> const & parent, std::string_view name) const
- -> kstd::result<kstd::shared_ptr<inode>> = 0;
+ [[nodiscard]] virtual auto lookup(inode_ptr const & parent, std::string_view name,
+ driver_data_ptr driver_data) const -> kstd::result<inode_ptr> = 0;
//! Create a new inode with the given name below a given parent inode.
//!
//! @param parent The parent inode.
//! @param name The name of the inode to create.
//! @param type The file type of inode to be created.
+ //! @param driver_data The per-mount data used by the driver.
//! @param raw_device The device number the new inode should represent, if any.
//! @return A pointer to the created inode on success, an error otherwise.
- [[nodiscard]] virtual auto create_inode(kstd::shared_ptr<inode> const & parent, std::string_view name,
- kapi::filesystem::file_type type,
+ [[nodiscard]] virtual auto create_inode(inode_ptr const & parent, std::string_view name,
+ kapi::filesystem::file_type type, driver_data_ptr driver_data,
std::optional<kapi::filesystem::device_number> raw_device = std::nullopt)
-> kstd::result<kstd::shared_ptr<inode>> = 0;
-
- //! Get the root inode of the file system.
- //!
- //! @return A reference to the root inode.
- [[nodiscard]] auto root_inode() const -> kstd::shared_ptr<inode> const &;
-
- //! Get the backing inode of the file system.
- //!
- //! @return A reference to the backing inode.
- [[nodiscard]] auto backing_inode() const -> kstd::shared_ptr<inode> const &;
-
- protected:
- kstd::shared_ptr<inode> m_root_inode{};
- kstd::shared_ptr<inode> m_backing_inode{};
};
} // namespace kernel::filesystem
diff --git a/kernel/kernel/filesystem/inode.cpp b/kernel/kernel/filesystem/inode.cpp
index d0762472..7b290479 100644
--- a/kernel/kernel/filesystem/inode.cpp
+++ b/kernel/kernel/filesystem/inode.cpp
@@ -1,10 +1,17 @@
#include <kernel/filesystem/inode.hpp>
+#include <kernel/filesystem/directory_listing_cursor.hpp>
+#include <kernel/filesystem/directory_listing_entry.hpp>
+#include <kernel/filesystem/error.hpp>
+
#include <kapi/filesystem.hpp>
+#include <kstd/memory.hpp>
#include <kstd/result.hpp>
+#include <cstddef>
#include <optional>
+#include <span>
#include <utility>
namespace kernel::filesystem
@@ -77,4 +84,20 @@ namespace kernel::filesystem
return std::nullopt;
}
+ auto inode::read_directory(directory_listing_cursor, std::span<directory_listing_entry>) const
+ -> kstd::result<std::pair<std::size_t, directory_listing_cursor>>
+ {
+ return kstd::failure(vfs_errc::not_a_directory);
+ }
+
+ auto inode::owning_mount() const noexcept -> kstd::weak_ptr<mount>
+ {
+ return m_owning_mount;
+ }
+
+ auto inode::set_owning_mount(kstd::shared_ptr<mount> const & source) noexcept -> void
+ {
+ m_owning_mount = source;
+ }
+
} // namespace kernel::filesystem \ No newline at end of file
diff --git a/kernel/kernel/filesystem/inode.hpp b/kernel/kernel/filesystem/inode.hpp
index afe15e7d..e7de9276 100644
--- a/kernel/kernel/filesystem/inode.hpp
+++ b/kernel/kernel/filesystem/inode.hpp
@@ -1,17 +1,24 @@
#ifndef TEACHOS_KERNEL_FILESYSTEM_INODE_HPP
#define TEACHOS_KERNEL_FILESYSTEM_INODE_HPP
+#include <kernel/filesystem/directory_listing_cursor.hpp>
+#include <kernel/filesystem/directory_listing_entry.hpp>
+
#include <kapi/filesystem.hpp>
+#include <kstd/memory.hpp>
#include <kstd/result.hpp>
#include <kstd/units.hpp>
#include <cstddef>
#include <optional>
#include <span>
+#include <utility>
namespace kernel::filesystem
{
+ struct mount;
+
//! An inode in the filesystem.
//!
//! Specific filesystems, or other subsystems, may provide their own implementation of this abstract base class. For
@@ -101,7 +108,34 @@ namespace kernel::filesystem
//! @return a device number if this inode refers to a device, an empty optional otherwise.
[[nodiscard]] virtual auto raw_device() const -> std::optional<kapi::filesystem::device_number>;
+ //! List the contents of a directory.
+ //!
+ //! @param position The current directory listing position.
+ //! @param entries An output span to store directory entries.
+ //! @return A pair of the number or read entries and the next listing position on success, an error otherwise.
+ [[nodiscard]] virtual auto read_directory(directory_listing_cursor position,
+ std::span<directory_listing_entry> entries) const
+ -> kstd::result<std::pair<std::size_t, directory_listing_cursor>>;
+
//! @}
+
+ //! @name Mount Resolution
+ //! @{
+
+ //! Get the mount this inode belongs to.
+ //!
+ //! @return The mount this inode belongs to.
+ [[nodiscard]] auto owning_mount() const noexcept -> kstd::weak_ptr<mount>;
+
+ //! Define which mount this inode belongs to.
+ //!
+ //! @param source The mount this inode belongs to.
+ auto set_owning_mount(kstd::shared_ptr<mount> const & source) noexcept -> void;
+
+ //! @}
+
+ private:
+ kstd::weak_ptr<mount> m_owning_mount{};
};
} // namespace kernel::filesystem
diff --git a/kernel/kernel/filesystem/mount.cpp b/kernel/kernel/filesystem/mount.cpp
index 009ab230..7381a160 100644
--- a/kernel/kernel/filesystem/mount.cpp
+++ b/kernel/kernel/filesystem/mount.cpp
@@ -6,6 +6,7 @@
#include <kapi/system.hpp>
#include <kstd/memory.hpp>
+#include <kstd/result.hpp>
#include <kstd/string.hpp>
#include <cstddef>
@@ -13,14 +14,14 @@
namespace kernel::filesystem
{
- mount::mount(kstd::shared_ptr<dentry> const & mount_dentry, kstd::shared_ptr<dentry> const & root_dentry,
- kstd::shared_ptr<filesystem> const & fs, kstd::shared_ptr<mount> const & parent_mount,
- kstd::shared_ptr<mount> const & source_mount)
+ mount::mount(dentry_ptr const & mount_dentry, dentry_ptr const & root_dentry, filesystem_ptr const & fs,
+ mount_ptr const & parent_mount, mount_ptr const & source_mount, kstd::shared_ptr<void> driver_data)
: m_mount_dentry(mount_dentry)
, m_root_dentry(root_dentry)
, m_filesystem(fs)
, m_parent_mount(parent_mount)
, m_source_mount(source_mount)
+ , m_driver_data(driver_data)
, m_ref_count(0)
{
if (!m_filesystem)
@@ -29,17 +30,56 @@ namespace kernel::filesystem
}
}
- auto mount::mount_dentry() const -> kstd::shared_ptr<dentry> const &
+ auto mount::attach(dentry_ptr const & mount_dentry, filesystem_ptr const & fs,
+ kstd::shared_ptr<inode> const & root_inode, kstd::shared_ptr<void> driver_data,
+ mount_ptr const & parent_mount, mount_ptr const & source_mount) -> mount_ptr
+ {
+ auto root_dentry = mount_dentry
+ ? kstd::make_shared<dentry>(mount_dentry->parent(), root_inode, mount_dentry->name())
+ : kstd::make_shared<dentry>(nullptr, root_inode, "/");
+
+ auto instance = mount_ptr{
+ new mount{mount_dentry, root_dentry, fs, parent_mount, source_mount, driver_data}
+ };
+
+ root_inode->set_owning_mount(instance);
+
+ return instance;
+ }
+
+ auto mount::create(dentry_ptr const & mount_dentry, filesystem_ptr const & fs, mount_ptr const & parent_mount,
+ mount_ptr const & source_mount, kstd::shared_ptr<inode> const & backing_inode)
+ -> kstd::result<mount_ptr>
+ {
+ if (!fs)
+ {
+ kapi::system::panic("[OS:FS] Tried to mount a null filesystem!");
+ }
+
+ auto mount_result = fs->mount(backing_inode);
+ if (!mount_result)
+ {
+ return kstd::failure(mount_result.error());
+ }
+
+ auto [root_inode, driver_data] = *mount_result;
+
+ auto instance = attach(mount_dentry, fs, root_inode, driver_data, parent_mount, source_mount);
+
+ return instance;
+ }
+
+ auto mount::mount_dentry() const -> dentry_ptr const &
{
return m_mount_dentry;
}
- auto mount::get_filesystem() const -> kstd::shared_ptr<filesystem> const &
+ auto mount::filesystem() const -> filesystem_ptr const &
{
return m_filesystem;
}
- auto mount::root_dentry() const -> kstd::shared_ptr<dentry> const &
+ auto mount::root_dentry() const -> dentry_ptr const &
{
return m_root_dentry;
}
@@ -53,16 +93,21 @@ namespace kernel::filesystem
return "/";
}
- auto mount::parent_mount() const -> kstd::shared_ptr<mount> const &
+ auto mount::parent_mount() const -> mount_ptr const &
{
return m_parent_mount;
}
- auto mount::source_mount() const -> kstd::shared_ptr<mount>
+ auto mount::source_mount() const -> mount_ptr
{
return m_source_mount.lock();
}
+ auto mount::driver_data() const -> kstd::shared_ptr<void>
+ {
+ return m_driver_data;
+ }
+
auto mount::increment_ref_count() -> void
{
m_ref_count += 1;
diff --git a/kernel/kernel/filesystem/mount.hpp b/kernel/kernel/filesystem/mount.hpp
index b906def5..cc032874 100644
--- a/kernel/kernel/filesystem/mount.hpp
+++ b/kernel/kernel/filesystem/mount.hpp
@@ -5,6 +5,7 @@
#include <kernel/filesystem/filesystem.hpp>
#include <kstd/memory.hpp>
+#include <kstd/result.hpp>
#include <kstd/string.hpp>
#include <atomic>
@@ -12,37 +13,58 @@
namespace kernel::filesystem
{
+ struct inode;
+
//! A mounted filesystem
struct mount
{
+ using dentry_ptr = kstd::shared_ptr<dentry>;
+ using filesystem_ptr = kstd::shared_ptr<struct filesystem>;
+ using mount_ptr = kstd::shared_ptr<struct mount>;
+ using inode_ptr = kstd::shared_ptr<struct inode>;
+
+ //! Attach a mounted filesystem with a known root inode
+ //!
+ //! @param mount_dentry The directory entry where the filesystem is mounted.
+ //! @param fs The filesystem instance being mounted.
+ //! @param root_inode The backing inode for the filesystem, must not be null.
+ //! @param driver_data The filesystem specific driver data.
+ //! @param parent_mount The parent mount which contains the mount_dentry.
+ //! @param source_mount The mount that the filesystem originates from.
+ auto static attach(dentry_ptr const & mount_dentry, filesystem_ptr const & fs, inode_ptr const & root_inode,
+ kstd::shared_ptr<void> driver_data, mount_ptr const & parent_mount,
+ mount_ptr const & source_mount) -> mount_ptr;
+
//! Create a new mount with the given parameters.
//!
//! @param mount_dentry The directory entry where the filesystem is mounted.
- //! @param root_dentry The root directory entry of the mounted filesystem.
//! @param fs The filesystem instance being mounted.
//! @param parent_mount The parent mount which contains the mount_dentry.
//! @param source_mount The mount that the filesystem originates from.
- mount(kstd::shared_ptr<dentry> const & mount_dentry, kstd::shared_ptr<dentry> const & root_dentry,
- kstd::shared_ptr<filesystem> const & fs, kstd::shared_ptr<mount> const & parent_mount,
- kstd::shared_ptr<mount> const & source_mount);
+ //! @param backing_inode The backing inode for the filesystem, if any.
+ auto static create(dentry_ptr const & mount_dentry, filesystem_ptr const & fs, mount_ptr const & parent_mount,
+ mount_ptr const & source_mount, inode_ptr const & backing_inode) -> kstd::result<mount_ptr>;
//! Get the directory entry where the filesystem is mounted.
- [[nodiscard]] auto mount_dentry() const -> kstd::shared_ptr<dentry> const &;
+ [[nodiscard]] auto mount_dentry() const -> dentry_ptr const &;
//! Get the root directory entry of the mounted filesystem.
- [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr<dentry> const &;
+ [[nodiscard]] auto root_dentry() const -> dentry_ptr const &;
//! Get the filesystem instance being mounted.
- [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr<filesystem> const &;
+ [[nodiscard]] auto filesystem() const -> filesystem_ptr const &;
//! Get the path at which the filesystem is mounted.
[[nodiscard]] auto mount_path() const -> kstd::string;
//! Get the parent mount that this mount was attached beneath.
- [[nodiscard]] auto parent_mount() const -> kstd::shared_ptr<mount> const &;
+ [[nodiscard]] auto parent_mount() const -> mount_ptr const &;
//! Get the source mount where this mount originates from.
- [[nodiscard]] auto source_mount() const -> kstd::shared_ptr<mount>;
+ [[nodiscard]] auto source_mount() const -> mount_ptr;
+
+ //! Get the data of the attached filesystem driver.
+ [[nodiscard]] auto driver_data() const -> kstd::shared_ptr<void>;
//! Increment the reference count for this mount.
auto increment_ref_count() -> void;
@@ -63,11 +85,23 @@ namespace kernel::filesystem
[[nodiscard]] auto ref_count() const -> size_t;
private:
- kstd::shared_ptr<dentry> m_mount_dentry{};
- kstd::shared_ptr<dentry> m_root_dentry{};
- kstd::shared_ptr<filesystem> m_filesystem{};
- kstd::shared_ptr<mount> m_parent_mount{};
+ //! Create a new mount with the given parameters.
+ //!
+ //! @param mount_dentry The directory entry where the filesystem is mounted.
+ //! @param root_dentry The root directory entry of the mounted filesystem.
+ //! @param fs The filesystem instance being mounted.
+ //! @param parent_mount The parent mount which contains the mount_dentry.
+ //! @param source_mount The mount that the filesystem originates from.
+ //! @param driver_data The driver specific state data.
+ mount(dentry_ptr const & mount_dentry, dentry_ptr const & root_dentry, filesystem_ptr const & fs,
+ mount_ptr const & parent_mount, mount_ptr const & source_mount, kstd::shared_ptr<void> driver_data);
+
+ dentry_ptr m_mount_dentry{};
+ dentry_ptr m_root_dentry{};
+ filesystem_ptr m_filesystem{};
+ mount_ptr m_parent_mount{};
kstd::weak_ptr<mount> m_source_mount{};
+ kstd::shared_ptr<void> m_driver_data{};
std::atomic_size_t m_ref_count{0};
};
} // namespace kernel::filesystem
diff --git a/kernel/kernel/filesystem/mount.tests.cpp b/kernel/kernel/filesystem/mount.tests.cpp
index 800a0cfc..a77134e5 100644
--- a/kernel/kernel/filesystem/mount.tests.cpp
+++ b/kernel/kernel/filesystem/mount.tests.cpp
@@ -15,29 +15,29 @@
SCENARIO("Mount construction", "[filesystem][mount]")
{
- GIVEN("a filesystem and a root dentry")
+ GIVEN("a filesystem and a root inode")
{
auto fs = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>();
- auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/");
- WHEN("constructing a mount with the filesystem and root dentry")
+ WHEN("attaching a mount with no mount point")
{
- auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr};
+ auto mount = kernel::filesystem::mount::attach(nullptr, fs, root_inode, nullptr, nullptr, nullptr);
THEN("the mount has the correct filesystem, root dentry, mount dentry, and mount path")
{
- REQUIRE(mount.get_filesystem() == fs);
- REQUIRE(mount.root_dentry() == root_dentry);
- REQUIRE(mount.mount_dentry() == root_dentry);
- REQUIRE(mount.mount_path() == "/");
- REQUIRE(mount.is_ready_to_unmount());
+ REQUIRE(mount->filesystem() == fs);
+ REQUIRE(mount->root_dentry()->inode() == root_inode);
+ REQUIRE(mount->root_dentry()->name() == "/");
+ REQUIRE(mount->mount_dentry() == nullptr);
+ REQUIRE(mount->mount_path() == "/");
+ REQUIRE(mount->is_ready_to_unmount());
}
THEN("the mount has no parent mount and no source mount")
{
- REQUIRE(mount.parent_mount() == nullptr);
- REQUIRE(mount.source_mount() == nullptr);
+ REQUIRE(mount->parent_mount() == nullptr);
+ REQUIRE(mount->source_mount() == nullptr);
}
}
@@ -45,7 +45,7 @@ SCENARIO("Mount construction", "[filesystem][mount]")
{
THEN("the constructor panics")
{
- REQUIRE_THROWS_AS((kernel::filesystem::mount{root_dentry, root_dentry, nullptr, nullptr, nullptr}),
+ REQUIRE_THROWS_AS((kernel::filesystem::mount::create(nullptr, nullptr, nullptr, nullptr, nullptr)),
kernel::tests::cpu::halt);
}
}
@@ -63,32 +63,33 @@ SCENARIO("Mount reference counting", "[filesystem][mount]")
THEN("reference count can be incremented and decremented, the mount is ready to unmount when the reference "
"count == 0")
{
- auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr};
+ auto mount = kernel::filesystem::mount::create(root_dentry, fs, nullptr, nullptr, nullptr);
+ REQUIRE(mount);
- mount.increment_ref_count();
- REQUIRE(mount.ref_count() == 1);
- REQUIRE_FALSE(mount.is_ready_to_unmount());
+ (*mount)->increment_ref_count();
+ REQUIRE((*mount)->ref_count() == 1);
+ REQUIRE_FALSE((*mount)->is_ready_to_unmount());
- mount.increment_ref_count();
- REQUIRE(mount.ref_count() == 2);
- REQUIRE_FALSE(mount.is_ready_to_unmount());
+ (*mount)->increment_ref_count();
+ REQUIRE((*mount)->ref_count() == 2);
+ REQUIRE_FALSE((*mount)->is_ready_to_unmount());
- mount.decrement_ref_count();
- REQUIRE(mount.ref_count() == 1);
- REQUIRE_FALSE(mount.is_ready_to_unmount());
+ (*mount)->decrement_ref_count();
+ REQUIRE((*mount)->ref_count() == 1);
+ REQUIRE_FALSE((*mount)->is_ready_to_unmount());
- mount.decrement_ref_count();
- REQUIRE(mount.ref_count() == 0);
- REQUIRE(mount.is_ready_to_unmount());
+ (*mount)->decrement_ref_count();
+ REQUIRE((*mount)->ref_count() == 0);
+ REQUIRE((*mount)->is_ready_to_unmount());
}
THEN("decrementing reference count when it is already zero does not decrement it below zero")
{
- auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr};
+ auto mount = kernel::filesystem::mount::create(root_dentry, fs, nullptr, nullptr, nullptr);
- REQUIRE_THROWS_AS(mount.decrement_ref_count(), std::runtime_error);
- REQUIRE(mount.ref_count() == 0);
- REQUIRE(mount.is_ready_to_unmount());
+ REQUIRE_THROWS_AS((*mount)->decrement_ref_count(), std::runtime_error);
+ REQUIRE((*mount)->ref_count() == 0);
+ REQUIRE((*mount)->is_ready_to_unmount());
}
}
} \ No newline at end of file
diff --git a/kernel/kernel/filesystem/mount_table.tests.cpp b/kernel/kernel/filesystem/mount_table.tests.cpp
index 39ad6b01..bed6cd9d 100644
--- a/kernel/kernel/filesystem/mount_table.tests.cpp
+++ b/kernel/kernel/filesystem/mount_table.tests.cpp
@@ -38,17 +38,17 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem]
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
- auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr);
+ auto mount1 = kernel::filesystem::mount::create(mount_dentry1, fs1, nullptr, nullptr, nullptr);
auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/mnt");
- auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, nullptr, nullptr);
+ auto mount2 = kernel::filesystem::mount::create(mount_dentry2, fs2, nullptr, nullptr, nullptr);
- table.add_mount(mount1);
- table.add_mount(mount2);
+ table.add_mount(*mount1);
+ table.add_mount(*mount2);
THEN("dentry flags are set correctly for mounted dentries")
{
@@ -89,17 +89,17 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem]
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
- auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr);
+ auto mount1 = kernel::filesystem::mount::create(mount_dentry1, fs1, nullptr, nullptr, nullptr);
auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
- auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, nullptr, nullptr);
+ auto mount2 = kernel::filesystem::mount::create(mount_dentry2, fs2, nullptr, nullptr, nullptr);
- table.add_mount(mount1);
- table.add_mount(mount2);
+ table.add_mount(*mount1);
+ table.add_mount(*mount2);
THEN("finding mounts by exact valid path returns the correct mount")
{
@@ -122,25 +122,25 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem]
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
- auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr);
+ auto mount1 = kernel::filesystem::mount::create(mount_dentry1, fs1, nullptr, nullptr, nullptr);
auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
mount_dentry1, kstd::make_shared<kernel::tests::filesystem::inode>(), "mnt");
- auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, mount1, nullptr);
+ auto mount2 = kernel::filesystem::mount::create(mount_dentry2, fs2, *mount1, nullptr, nullptr);
auto fs3 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry3 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry3 = kstd::make_shared<kernel::filesystem::dentry>(
mount_dentry2, kstd::make_shared<kernel::tests::filesystem::inode>(), "submnt");
- auto mount3 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry3, root_dentry3, fs3, mount2, nullptr);
+ auto mount3 = kernel::filesystem::mount::create(mount_dentry3, fs3, *mount2, nullptr, nullptr);
- table.add_mount(mount1);
- table.add_mount(mount2);
- table.add_mount(mount3);
+ table.add_mount(*mount1);
+ table.add_mount(*mount2);
+ table.add_mount(*mount3);
THEN("removing a mount with child mounts returns has_child_mounts")
{
@@ -166,19 +166,19 @@ SCENARIO("Mount reference counting", "[filesystem][mount_table]")
auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>();
auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/");
- auto source_mount = kstd::make_shared<kernel::filesystem::mount>(root_dentry, root_dentry, fs, nullptr, nullptr);
- auto mount = kstd::make_shared<kernel::filesystem::mount>(root_dentry, root_dentry, fs, nullptr, source_mount);
+ auto source_mount = kernel::filesystem::mount::create(root_dentry, fs, nullptr, nullptr, nullptr);
+ auto mount = kernel::filesystem::mount::create(root_dentry, fs, nullptr, *source_mount, nullptr);
THEN("reference count of source mount is incremented when a mount is added to the mount table and decremented when "
"the mount is removed")
{
- REQUIRE(source_mount->ref_count() == 0);
+ REQUIRE((*source_mount)->ref_count() == 0);
- table.add_mount(mount);
- REQUIRE(source_mount->ref_count() == 1);
+ table.add_mount(*mount);
+ REQUIRE((*source_mount)->ref_count() == 1);
REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed);
- REQUIRE(source_mount->ref_count() == 0);
+ REQUIRE((*source_mount)->ref_count() == 0);
}
}
}
diff --git a/kernel/kernel/filesystem/open_file_descriptor.cpp b/kernel/kernel/filesystem/open_file_descriptor.cpp
index 98520aa8..64c47461 100644
--- a/kernel/kernel/filesystem/open_file_descriptor.cpp
+++ b/kernel/kernel/filesystem/open_file_descriptor.cpp
@@ -26,7 +26,7 @@ namespace kernel::filesystem
auto open_file_descriptor::read(std::span<std::byte> buffer) -> kstd::result<kstd::bytes>
{
- if (auto result = m_dentry->get_inode()->read(buffer, m_offset); !result)
+ if (auto result = m_dentry->inode()->read(buffer, m_offset); !result)
{
return kstd::failure(result.error());
}
@@ -40,7 +40,7 @@ namespace kernel::filesystem
auto open_file_descriptor::write(std::span<std::byte const> buffer) -> kstd::result<kstd::bytes>
{
- if (auto result = m_dentry->get_inode()->write(buffer, m_offset); !result)
+ if (auto result = m_dentry->inode()->write(buffer, m_offset); !result)
{
return kstd::failure(result.error());
}
@@ -68,7 +68,7 @@ namespace kernel::filesystem
}
case kapi::filesystem::seek_origin::end:
{
- return m_dentry->get_inode()->status()->size;
+ return m_dentry->inode()->status()->size;
}
default:
{
diff --git a/kernel/kernel/filesystem/rootfs/filesystem.cpp b/kernel/kernel/filesystem/rootfs/filesystem.cpp
index cccfa63c..4618fd69 100644
--- a/kernel/kernel/filesystem/rootfs/filesystem.cpp
+++ b/kernel/kernel/filesystem/rootfs/filesystem.cpp
@@ -2,9 +2,7 @@
#include <kernel/filesystem/error.hpp>
#include <kernel/filesystem/filesystem.hpp>
-#include <kernel/filesystem/inode.hpp>
#include <kernel/filesystem/rootfs/inode.hpp>
-#include <kernel/filesystem/type.hpp>
#include <kapi/filesystem.hpp>
@@ -18,48 +16,31 @@
namespace kernel::filesystem::rootfs
{
- struct type final : kernel::filesystem::type
- {
- [[nodiscard]] auto name() const noexcept -> std::string_view override
- {
- return "rootfs";
- }
-
- [[nodiscard]] auto probe(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) const noexcept
- -> kstd::result<std::uint32_t> override
- {
- if (backing_inode)
- {
- return kstd::failure(vfs_errc::invalid_argument);
- }
+ static_assert(sizeof(kernel::filesystem::filesystem) == sizeof(rootfs::filesystem));
- return 0;
- }
-
- [[nodiscard]] auto make_instance() const -> kstd::shared_ptr<kernel::filesystem::filesystem> override
+ auto filesystem::probe(inode_ptr const & inode) const -> kstd::result<std::uint32_t>
+ {
+ if (inode)
{
- return kstd::make_shared<filesystem>();
+ return kstd::failure(vfs_errc::invalid_argument);
}
- };
- [[gnu::used]]
- constexpr auto registration = type_registration<type>{};
+ return 0;
+ }
- auto filesystem::mount(kstd::shared_ptr<kernel::filesystem::inode> const &) -> kstd::result<void>
+ auto filesystem::mount(inode_ptr const &) -> kstd::result<mount_result>
{
- m_root_inode = kstd::make_shared<inode>();
- return kstd::success();
+ auto root = kstd::make_shared<inode>();
+ return mount_result{root, nullptr};
}
- auto filesystem::lookup(kstd::shared_ptr<kernel::filesystem::inode> const &, std::string_view) const
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
+ auto filesystem::lookup(inode_ptr const &, std::string_view, driver_data_ptr) const -> kstd::result<inode_ptr>
{
return kstd::failure(vfs_errc::no_such_file_or_directory);
}
- auto filesystem::create_inode(kstd::shared_ptr<kernel::filesystem::inode> const &, std::string_view,
- kapi::filesystem::file_type, std::optional<kapi::filesystem::device_number>)
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
+ auto filesystem::create_inode(inode_ptr const &, std::string_view, kapi::filesystem::file_type, driver_data_ptr,
+ std::optional<kapi::filesystem::device_number>) -> kstd::result<inode_ptr>
{
return kstd::failure(vfs_errc::read_only_file_system);
}
diff --git a/kernel/kernel/filesystem/rootfs/filesystem.hpp b/kernel/kernel/filesystem/rootfs/filesystem.hpp
index 2a861747..0195b250 100644
--- a/kernel/kernel/filesystem/rootfs/filesystem.hpp
+++ b/kernel/kernel/filesystem/rootfs/filesystem.hpp
@@ -12,6 +12,7 @@
#include <kstd/system_error.hpp>
#include <kstd/vector.hpp>
+#include <cstdint>
#include <optional>
#include <string_view>
@@ -24,20 +25,22 @@ namespace kernel::filesystem::rootfs
//! hierarchy. It is responsible for providing a stable and consistent interface to the root directory.
struct filesystem : kernel::filesystem::filesystem
{
+ [[nodiscard]] auto probe(inode_ptr const & inode) const -> kstd::result<std::uint32_t> override;
+
//! @copydoc kernel::filesystem::filesystem::mount
- auto mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) -> kstd::result<void> override;
+ auto mount(inode_ptr const & backing_inode) -> kstd::result<mount_result> override;
//! @copydoc kernel::filesystem::filesystem::lookup.
//! @return Always returns an error, as rootfs is always empty.
- [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
+ [[nodiscard]] auto lookup(inode_ptr const & parent, std::string_view name, driver_data_ptr driver_data) const
+ -> kstd::result<inode_ptr> override;
//! @copydoc kernel::filesystem::filesystem::create_inode.
//! @return Always returns an error, as creating new inodes is not supported in rootfs.
- [[nodiscard]] auto create_inode(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name,
- kapi::filesystem::file_type type,
+ [[nodiscard]] auto create_inode(inode_ptr const & parent, std::string_view name, kapi::filesystem::file_type type,
+ driver_data_ptr driver_data,
std::optional<kapi::filesystem::device_number> raw_device = std::nullopt)
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
+ -> kstd::result<inode_ptr> override;
};
} // namespace kernel::filesystem::rootfs
diff --git a/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp b/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp
index ba1aab0e..15c8e313 100644
--- a/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp
+++ b/kernel/kernel/filesystem/rootfs/filesystem.tests.cpp
@@ -14,32 +14,34 @@ SCENARIO("Rootfs filesystem mount and lookup", "[filesystem][rootfs][filesystem]
{
GIVEN("a mounted rootfs filesystem")
{
- auto fs = kernel::filesystem::rootfs::filesystem{};
- auto result = fs.mount(nullptr);
+ auto fs = kstd::make_shared<kernel::filesystem::rootfs::filesystem>();
+ auto mount = fs->mount(nullptr);
+ REQUIRE(mount);
+ auto [root, driver_data] = *mount;
+ REQUIRE(root);
THEN("the filesystem can be mounted successfully")
{
- REQUIRE(result);
- REQUIRE(fs.root_inode());
+ REQUIRE(mount);
}
THEN("looking up a non-existent directory returns an error")
{
- auto non_existent_inode_1 = fs.lookup(fs.root_inode(), "");
+ auto non_existent_inode_1 = fs->lookup(root, "", driver_data);
REQUIRE(!non_existent_inode_1);
- auto non_existent_inode_2 = fs.lookup(fs.root_inode(), "nonexistent");
+ auto non_existent_inode_2 = fs->lookup(root, "nonexistent", driver_data);
REQUIRE(!non_existent_inode_2);
}
THEN("looking up with a null parent inode returns an error")
{
- auto result = fs.lookup(nullptr, "dev");
+ auto result = fs->lookup(nullptr, "dev", driver_data);
REQUIRE(!result);
}
THEN("create_inode always returns an error")
{
- auto result = fs.create_inode(fs.root_inode(), "new_device", kapi::filesystem::file_type::directory);
+ auto result = fs->create_inode(root, "new_device", kapi::filesystem::file_type::directory, driver_data);
REQUIRE(!result);
}
}
diff --git a/kernel/kernel/filesystem/rootfs/module.cpp b/kernel/kernel/filesystem/rootfs/module.cpp
new file mode 100644
index 00000000..464120b2
--- /dev/null
+++ b/kernel/kernel/filesystem/rootfs/module.cpp
@@ -0,0 +1,28 @@
+#include <kernel/filesystem/driver_registry.hpp>
+#include <kernel/filesystem/filesystem.hpp>
+#include <kernel/filesystem/rootfs/filesystem.hpp>
+
+#include <kstd/memory.hpp>
+
+#include <string_view>
+
+namespace
+{
+
+ struct descriptor final : kernel::filesystem::driver_descriptor
+ {
+ [[nodiscard]] auto name() const noexcept -> std::string_view override
+ {
+ return "rootfs";
+ }
+
+ [[nodiscard]] auto make_instance() const -> kernel::filesystem::filesystem::filesystem_ptr override
+ {
+ return kstd::make_shared<kernel::filesystem::rootfs::filesystem>();
+ }
+ };
+
+ [[gnu::used]]
+ constexpr auto registration = kernel::filesystem::driver_module<descriptor>{};
+
+} // namespace \ No newline at end of file
diff --git a/kernel/kernel/filesystem/type.hpp b/kernel/kernel/filesystem/type.hpp
deleted file mode 100644
index c2f2595b..00000000
--- a/kernel/kernel/filesystem/type.hpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef TEACHOS_KERNEL_FILESYSTEM_TYPE_HPP
-#define TEACHOS_KERNEL_FILESYSTEM_TYPE_HPP
-
-#include <kernel/filesystem/filesystem.hpp>
-#include <kernel/filesystem/inode.hpp>
-
-#include <kstd/memory.hpp>
-#include <kstd/result.hpp>
-
-#include <cstdint>
-#include <string_view>
-
-namespace kernel::filesystem
-{
-
- //! A type descriptor for a filesystem driver.
- //!
- //! Each filesystem must expose an instance of a class derived from this type in order to be registered with the vfs
- //! filesystem registry.
- struct type
- {
- virtual ~type() = default;
-
- //! Get the name of the filesystem represented by this descriptor.
- [[nodiscard]] virtual auto name() const noexcept -> std::string_view = 0;
-
- //! Check if the filesystem is applicable.
- //!
- //! @return the priority of this filesystem for the given inode.
- [[nodiscard]] virtual auto probe(kstd::shared_ptr<inode> const & backing_inode) const noexcept
- -> kstd::result<std::uint32_t> = 0;
-
- //! Create a new instance of the filesystem represented by this descriptor.
- [[nodiscard]] virtual auto make_instance() const -> kstd::shared_ptr<filesystem> = 0;
- };
-
- template<typename Type>
- struct type_registration
- {
- constexpr auto static instance = Type{};
- [[using gnu: section("fs_types"), used, visibility("hidden")]] constexpr auto static pointer{
- kstd::make_observer<type const>(&instance),
- };
- };
-
-} // namespace kernel::filesystem
-
-#endif
diff --git a/kernel/kernel/filesystem/type_registry.cpp b/kernel/kernel/filesystem/type_registry.cpp
deleted file mode 100644
index 05fb116a..00000000
--- a/kernel/kernel/filesystem/type_registry.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#include <kernel/filesystem/type_registry.hpp>
-
-#include <kapi/system.hpp>
-
-#include <kstd/memory.hpp>
-#include <kstd/print.hpp>
-#include <kstd/vector.hpp>
-
-#include <algorithm>
-#include <cstddef>
-#include <optional>
-#include <ranges>
-#include <span>
-
-namespace kernel::filesystem
-{
-
- extern "C"
- {
- // We need to suppress clang-tidy linting warnings here, since these symbols are generated by the linker and we
- // cannot choose their names, unless we wanted to extend the linker script needlessly.
- // NOLINTBEGIN(readability-identifier-naming)
- extern type_registry::pointer const __start_fs_types;
- extern type_registry::pointer const __stop_fs_types;
- // NOLINTEND(readability-identifier-naming)
- }
-
- namespace
- {
- auto constinit instance = std::optional<type_registry>{};
- }
-
- auto type_registry::init() -> void
- {
- if (instance)
- {
- kapi::system::panic("[FILESYSTEM] tried to initialize type registry more than once!");
- }
-
- instance.emplace();
-
- auto type_descriptors = std::span{&__start_fs_types, &__stop_fs_types} | //
- std::views::filter([](auto p) { return p != nullptr; });
-
- std::ranges::for_each(type_descriptors, [](auto descriptor) {
- kstd::println("[FILESYSTEM] registering '{}'", descriptor->name());
- instance->add(descriptor);
- });
- }
-
- auto type_registry::get() -> type_registry &
- {
- if (!instance)
- {
- kapi::system::panic("[FILESYSTEM] type registry has not been initialized!");
- }
-
- return *instance;
- }
-
- auto type_registry::find(std::string_view name) const -> std::optional<pointer>
- {
- if (auto it = m_descriptors.find(name); it != m_descriptors.end())
- {
- return it->second;
- }
- return std::nullopt;
- }
-
- auto type_registry::add(pointer descriptor) -> bool
- {
- auto result = m_descriptors.emplace(descriptor->name(), descriptor);
- return result.second;
- }
-
- auto type_registry::all() const noexcept -> kstd::vector<pointer>
- {
- return m_descriptors.values();
- }
-
- auto type_registry::size() const noexcept -> std::size_t
- {
- return m_descriptors.size();
- }
-
-} // namespace kernel::filesystem
-
-namespace kernel::tests::filesystem::type_registry
-{
- auto deinit() -> void
- {
- kernel::filesystem::instance.reset();
- }
-} // namespace kernel::tests::filesystem::type_registry \ No newline at end of file
diff --git a/kernel/kernel/filesystem/type_registry.hpp b/kernel/kernel/filesystem/type_registry.hpp
deleted file mode 100644
index 11669ab3..00000000
--- a/kernel/kernel/filesystem/type_registry.hpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef TEACHOS_KERNEL_TYPE_REGISTRY_HPP
-#define TEACHOS_KERNEL_TYPE_REGISTRY_HPP
-
-#include <kernel/filesystem/type.hpp>
-
-#include <kstd/flat_map.hpp>
-#include <kstd/memory.hpp>
-#include <kstd/string.hpp>
-#include <kstd/vector.hpp>
-
-#include <cstddef>
-#include <optional>
-#include <string_view>
-
-namespace kernel::filesystem
-{
-
- struct type_registry
- {
- using value_type = type;
- using pointer = kstd::observer_ptr<value_type const>;
-
- auto static init() -> void;
- auto static get() -> type_registry &;
-
- constexpr type_registry() noexcept = default;
-
- //! Add a type descriptor to this registry.
- //!
- //! This function will register the given descriptor with this registry, given that no descriptor for a filesystem
- //! with the same name exists in this registry already.
- //!
- //! @param descriptor The filesystem type descriptor to add to the registry.
- //! @return @p true iff. the descriptor was successfully added, @p false if not.
- auto add(pointer descriptor) -> bool;
-
- //! Find a registered type descriptor by name
- //!
- //! @param name The name of the filesystem
- [[nodiscard]] auto find(std::string_view name) const -> std::optional<pointer>;
-
- //! Get all currently registered type descriptors.
- //!
- //! @return A span containing all currently registered filesystem type descriptors.
- [[nodiscard]] auto all() const noexcept -> kstd::vector<pointer>;
-
- //! Get the number of registered filesystem types.
- //!
- //! @return The number of filesystem descriptors currently registered with this registry.
- [[nodiscard]] auto size() const noexcept -> std::size_t;
-
- private:
- //! A map from filesystem names (identifiers) to filesystem type descriptors.
- kstd::flat_map<std::string_view, pointer> m_descriptors{};
- };
-
-} // namespace kernel::filesystem
-
-#endif
diff --git a/kernel/kernel/filesystem/type_registry.tests.cpp b/kernel/kernel/filesystem/type_registry.tests.cpp
deleted file mode 100644
index b38c16f8..00000000
--- a/kernel/kernel/filesystem/type_registry.tests.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-#include <kernel/filesystem/type_registry.hpp>
-
-#include <kernel/filesystem/filesystem.hpp>
-#include <kernel/filesystem/inode.hpp>
-#include <kernel/filesystem/type.hpp>
-
-#include <kstd/memory.hpp>
-#include <kstd/result.hpp>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <cstdint>
-#include <iterator>
-#include <string_view>
-
-struct test_type final : kernel::filesystem::type
-{
- [[nodiscard]] auto name() const noexcept -> std::string_view override
- {
- return "bht_testfs";
- }
-
- [[nodiscard]] auto probe(kstd::shared_ptr<kernel::filesystem::inode> const &) const noexcept
- -> kstd::result<std::uint32_t> override
- {
- return false;
- }
-
- [[nodiscard]] auto make_instance() const -> kstd::shared_ptr<kernel::filesystem::filesystem> override
- {
- return nullptr;
- }
-};
-
-SCENARIO("Filesystem type registry initialization and construction", "[filesystem]")
-{
- GIVEN("A default constructed type_registry")
- {
- auto instance = kernel::filesystem::type_registry{};
-
- WHEN("getting the span of filesystem descriptors")
- {
- auto descriptors = instance.all();
-
- THEN("the span is empty")
- {
- REQUIRE(descriptors.empty());
- }
- }
- }
-}
-
-SCENARIO("Filesystem type registry modifiers", "[filesystem]")
-{
- GIVEN("A default constructed type_registry")
- {
- auto instance = kernel::filesystem::type_registry{};
-
- WHEN("adding a type descriptor")
- {
- auto descriptor = test_type{};
-
- instance.add(kstd::make_observer(&descriptor));
-
- THEN("the size of the registry is one")
- {
- REQUIRE(instance.size() == 1);
- }
-
- THEN("the span is not empty")
- {
- REQUIRE_FALSE(instance.all().empty());
- }
-
- THEN("the span's size is equal to the registry size")
- {
- REQUIRE(std::size(instance.all()) == std::size(instance));
- }
- }
- }
-}
diff --git a/kernel/kernel/filesystem/vfs.cpp b/kernel/kernel/filesystem/vfs.cpp
index 0cb46cac..4645fd54 100644
--- a/kernel/kernel/filesystem/vfs.cpp
+++ b/kernel/kernel/filesystem/vfs.cpp
@@ -4,19 +4,19 @@
#include <kernel/filesystem/dentry.hpp>
#include <kernel/filesystem/device_inode.hpp>
#include <kernel/filesystem/device_number_registry.hpp>
+#include <kernel/filesystem/driver_registry.hpp>
#include <kernel/filesystem/error.hpp>
-#include <kernel/filesystem/file_type.hpp>
#include <kernel/filesystem/filesystem.hpp>
#include <kernel/filesystem/inode.hpp>
#include <kernel/filesystem/mount.hpp>
#include <kernel/filesystem/mount_table.hpp>
#include <kernel/filesystem/path.hpp>
-#include <kernel/filesystem/type_registry.hpp>
#include <kapi/filesystem.hpp>
#include <kapi/system.hpp>
#include <kstd/memory.hpp>
+#include <kstd/print.hpp>
#include <kstd/result.hpp>
#include <kstd/system_error.hpp>
#include <kstd/units.hpp>
@@ -53,49 +53,67 @@ namespace kernel::filesystem
vfs::vfs()
{
// mount rootfs at /
- auto root_fs_descriptor = type_registry::get().find("rootfs");
- if (!root_fs_descriptor)
+ auto rootfs = driver_registry::get().find("rootfs");
+ if (!rootfs)
{
kapi::system::panic("[OS:FS] rootfs driver is missing!");
}
- auto root_fs = (*root_fs_descriptor)->make_instance();
- if (auto result = root_fs->mount(nullptr); !result)
+ auto root_mount_result = (*rootfs)->mount(nullptr);
+ if (!root_mount_result)
{
- kapi::system::panic("[FILESYSTEM] failed to mount root FS", result.error());
+ kapi::system::panic("[FILESYSTEM] failed to mount root FS", root_mount_result.error());
}
- auto root_fs_root_dentry = kstd::make_shared<dentry>(nullptr, root_fs->root_inode(), "/");
- auto root_mount = kstd::make_shared<struct mount>(nullptr, root_fs_root_dentry, root_fs, nullptr, nullptr);
+ auto [root_inode, root_state] = *root_mount_result;
+
+ auto root_mount = mount::attach(nullptr, *rootfs, root_inode, root_state, nullptr, nullptr);
m_mount_table.add_mount(root_mount);
// mount devfs at /dev (inside rootfs, temporary, will be shadowed)
- auto device_fs_descriptor = type_registry::get().find("devfs");
- if (!device_fs_descriptor)
+ auto devfs = driver_registry::get().find("devfs");
+ if (!devfs)
{
kapi::system::panic("[OS:FS] devfs driver is missing!");
}
- auto device_fs = (*device_fs_descriptor)->make_instance();
- if (auto result = device_fs->mount(nullptr); !result)
+ auto device_fs_mount_result = (*devfs)->mount(nullptr);
+ if (!device_fs_mount_result)
{
- kapi::system::panic("[FILESYSTEM] failed to mount device FS", result.error());
+ kapi::system::panic("[FILESYSTEM] failed to mount device FS", device_fs_mount_result.error());
}
- graft_persistent_device_fs(device_fs);
+
+ auto [device_fs_root, device_fs_data] = *device_fs_mount_result;
+ graft_persistent_device_fs(*devfs, device_fs_root, device_fs_data);
// mount boot fs at / (shadows rootfs), re-graft devfs
auto [boot_device_dentry, boot_device_mount_context] =
resolve_path_internal("/dev/ram0").value_or(std::pair{nullptr, nullptr});
if (boot_device_dentry && boot_device_mount_context)
{
- if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode()))
+ auto driver = driver_registry::get().match(boot_device_dentry->inode());
+ if (!driver)
{
- if (auto root_dentry = resolve_path("/"))
- {
- do_mount_internal(*root_dentry, root_mount, *boot_root_fs, boot_device_mount_context);
- graft_persistent_device_fs(device_fs);
- }
+ kstd::println(kstd::print_sink::stderr, "[OS:FS] Missing driver for root disk!");
+ return;
+ }
+
+ auto root_dentry = resolve_path("/");
+ if (!root_dentry)
+ {
+ kapi::system::panic("[OS:FS] No root directory found!");
+ }
+
+ auto mount =
+ mount::create(*root_dentry, *driver, root_mount, boot_device_mount_context, boot_device_dentry->inode());
+
+ if (!mount)
+ {
+ kapi::system::panic("[OS:FS] failed to mount boot FS", mount.error());
}
+ m_mount_table.add_mount(*mount);
+
+ graft_persistent_device_fs(*devfs, device_fs_root, device_fs_data);
}
}
@@ -118,7 +136,7 @@ namespace kernel::filesystem
}
auto [dentry, mount] = resolved_path.value();
- auto inode = dentry->get_inode();
+ auto inode = dentry->inode();
if (inode->is_device())
{
@@ -128,7 +146,8 @@ namespace kernel::filesystem
return kstd::failure(vfs_errc::no_such_device);
}
- auto type = inode->is_block_device() ? file_type::block : file_type::character;
+ auto type = inode->is_block_device() ? kapi::filesystem::file_type::block_device
+ : kapi::filesystem::file_type::character_device;
auto resolved_device = device_number_registry::get().resolve(*number, type);
if (!resolved_device)
{
@@ -177,13 +196,20 @@ namespace kernel::filesystem
}
auto [source_dentry, source_mount_context] = *resolved_source;
- auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode());
- if (!fs)
+ auto driver = driver_registry::get().match(source_dentry->inode());
+ if (!driver)
+ {
+ return kstd::failure(driver.error());
+ }
+
+ auto mount =
+ mount::create(mount_point_dentry, *driver, mount_context, source_mount_context, source_dentry->inode());
+ if (!mount)
{
- return kstd::failure(fs.error());
+ return kstd::failure(mount.error());
}
+ m_mount_table.add_mount(*mount);
- do_mount_internal(mount_point_dentry, mount_context, *fs, source_mount_context);
return kstd::success();
}
@@ -233,7 +259,7 @@ namespace kernel::filesystem
return kstd::failure(resolved.error());
}
- return (*resolved)->get_inode()->status();
+ return (*resolved)->inode()->status();
}
auto vfs::create_device_node(std::string_view path, std::uint32_t mode, kapi::filesystem::device_number device)
@@ -278,10 +304,13 @@ namespace kernel::filesystem
auto [parent_dentry, mount_context] = *resolved_parent;
- if (auto fs = mount_context->get_filesystem())
+ if (auto fs = mount_context->filesystem())
{
- if (auto new_inode = fs->create_inode(parent_dentry->get_inode(), name, type, raw_device))
+ if (auto new_inode =
+ fs->create_inode(parent_dentry->inode(), name, type, mount_context->driver_data(), raw_device))
{
+ (*new_inode)->set_owning_mount(mount_context);
+
auto new_dentry = kstd::make_shared<dentry>(parent_dentry, *new_inode, name);
parent_dentry->add_child(new_dentry);
@@ -292,16 +321,9 @@ namespace kernel::filesystem
return kstd::failure(vfs_errc::no_such_file_or_directory);
}
- auto vfs::do_mount_internal(dentry_ptr const & mount_point_dentry, mount_ptr const & parent_mount, fs_ptr const & fs,
- mount_ptr const & source_mount) -> void
- {
- auto new_fs_root =
- kstd::make_shared<dentry>(mount_point_dentry->parent(), fs->root_inode(), mount_point_dentry->name());
- auto new_mount = kstd::make_shared<struct mount>(mount_point_dentry, new_fs_root, fs, parent_mount, source_mount);
- m_mount_table.add_mount(new_mount);
- }
-
- auto vfs::graft_persistent_device_fs(kstd::shared_ptr<filesystem> const & device_fs) -> void
+ auto vfs::graft_persistent_device_fs(kstd::shared_ptr<filesystem> const & device_fs,
+ kstd::shared_ptr<inode> const & root_inode, kstd::shared_ptr<void> driver_data)
+ -> void
{
auto [root_mount_point_dentry, root_mount] = resolve_path_internal("/").value_or(std::pair{nullptr, nullptr});
if (root_mount_point_dentry && root_mount)
@@ -309,11 +331,12 @@ namespace kernel::filesystem
auto dev_dentry = root_mount_point_dentry->find_child("dev");
if (!dev_dentry)
{
- dev_dentry = kstd::make_shared<dentry>(root_mount_point_dentry, device_fs->root_inode(), "dev");
+ dev_dentry = kstd::make_shared<dentry>(root_mount_point_dentry, root_inode, "dev");
root_mount_point_dentry->add_child(dev_dentry);
}
- do_mount_internal(dev_dentry, root_mount, device_fs);
+ auto new_mount = mount::attach(dev_dentry, device_fs, root_inode, driver_data, root_mount, nullptr);
+ m_mount_table.add_mount(new_mount);
}
}
@@ -343,7 +366,7 @@ namespace kernel::filesystem
auto part = path_parts_vector.back();
path_parts_vector.pop_back();
- if (!current_dentry->get_inode()->is_directory())
+ if (!current_dentry->inode()->is_directory())
{
return kstd::failure(vfs_errc::not_a_directory);
}
@@ -378,13 +401,15 @@ namespace kernel::filesystem
auto next_dentry = current_dentry->find_child(part);
if (!next_dentry)
{
- auto current_fs = current_mount->get_filesystem();
- auto found_inode = current_fs->lookup(current_dentry->get_inode(), part);
+ auto current_fs = current_mount->filesystem();
+ auto found_inode = current_fs->lookup(current_dentry->inode(), part, current_mount->driver_data());
if (!found_inode)
{
return kstd::failure(found_inode.error());
}
+ (*found_inode)->set_owning_mount(current_mount);
+
next_dentry = kstd::make_shared<dentry>(current_dentry, *found_inode, part);
current_dentry->add_child(next_dentry);
}
@@ -399,7 +424,7 @@ namespace kernel::filesystem
next_dentry = current_mount->root_dentry();
}
- if (next_dentry->get_inode()->is_symbolic_link())
+ if (next_dentry->inode()->is_symbolic_link())
{
if (symlink_counter++ > constants::symloop_max)
{
@@ -407,7 +432,7 @@ namespace kernel::filesystem
}
kstd::vector<std::byte> buffer(constants::symlink_max_path_length);
- auto const bytes_read = next_dentry->get_inode()->read(buffer, 0_B);
+ auto const bytes_read = next_dentry->inode()->read(buffer, 0_B);
if (!bytes_read)
{
return kstd::failure(bytes_read.error());
diff --git a/kernel/kernel/filesystem/vfs.hpp b/kernel/kernel/filesystem/vfs.hpp
index 2f30539b..a53ab532 100644
--- a/kernel/kernel/filesystem/vfs.hpp
+++ b/kernel/kernel/filesystem/vfs.hpp
@@ -111,10 +111,9 @@ namespace kernel::filesystem
[[nodiscard]] auto find_mount(std::string_view path) const -> kstd::result<mount_ptr>;
- auto do_mount_internal(dentry_ptr const & mount_point_dentry, mount_ptr const & parent_mount, fs_ptr const & fs,
- mount_ptr const & source_mount = nullptr) -> void;
-
- auto graft_persistent_device_fs(kstd::shared_ptr<filesystem> const & device_fs) -> void;
+ auto graft_persistent_device_fs(kstd::shared_ptr<filesystem> const & device_fs,
+ kstd::shared_ptr<inode> const & root_inode, kstd::shared_ptr<void> driver_data)
+ -> void;
auto create_inode(std::string_view path, kapi::filesystem::file_type type,
std::optional<kapi::filesystem::device_number> raw_device = std::nullopt) -> kstd::result<void>;
diff --git a/kernel/kernel/filesystem/vfs.tests.cpp b/kernel/kernel/filesystem/vfs.tests.cpp
index a035e083..5b7ea621 100644
--- a/kernel/kernel/filesystem/vfs.tests.cpp
+++ b/kernel/kernel/filesystem/vfs.tests.cpp
@@ -665,11 +665,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS
REQUIRE(via_persistent_fs != nullptr);
auto marker = kstd::vector{std::byte{'M'}, std::byte{'K'}, std::byte{'N'}, std::byte{'O'}, std::byte{'D'}};
- auto written = (*via_persistent_fs)->get_inode()->write(marker, 0_B);
+ auto written = (*via_persistent_fs)->inode()->write(marker, 0_B);
REQUIRE(written == kstd::bytes{marker.size()});
auto read_back = kstd::vector<std::byte>(marker.size());
- auto read = (*via_devfs)->get_inode()->read(read_back, 0_B);
+ auto read = (*via_devfs)->inode()->read(read_back, 0_B);
REQUIRE(read == kstd::bytes{marker.size()});
REQUIRE(read_back == marker);
diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp
index db47f167..f8322b31 100644
--- a/kernel/kernel/main.cpp
+++ b/kernel/kernel/main.cpp
@@ -1,4 +1,4 @@
-#include "kernel/filesystem/type_registry.hpp"
+#include "kernel/filesystem/driver_registry.hpp"
#include <kernel/devices/init.hpp>
#include <kernel/devices/storage.hpp>
#include <kernel/drivers/init.hpp>
@@ -248,7 +248,7 @@ auto main() -> int
kernel::filesystem::open_file_table::init();
kstd::println("[OS] Global open file table initialized.");
- kernel::filesystem::type_registry::init();
+ kernel::filesystem::driver_registry::init();
kstd::println("[OS] Builtin filesystems registered.");
kernel::filesystem::vfs::init();
diff --git a/kernel/kernel/test_support/filesystem/driver_registry.hpp b/kernel/kernel/test_support/filesystem/driver_registry.hpp
new file mode 100644
index 00000000..b91acd57
--- /dev/null
+++ b/kernel/kernel/test_support/filesystem/driver_registry.hpp
@@ -0,0 +1,10 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_DRIVER_REGISTRY_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_DRIVER_REGISTRY_HPP
+
+namespace kernel::tests::filesystem::driver_registry
+{
+ //! Deinitialize the FS type registry singleton.
+ auto deinit() -> void;
+} // namespace kernel::tests::filesystem::driver_registry
+
+#endif \ No newline at end of file
diff --git a/kernel/kernel/test_support/filesystem/filesystem.cpp b/kernel/kernel/test_support/filesystem/filesystem.cpp
index cd08f961..0d4610ab 100644
--- a/kernel/kernel/test_support/filesystem/filesystem.cpp
+++ b/kernel/kernel/test_support/filesystem/filesystem.cpp
@@ -9,20 +9,30 @@
#include <kstd/result.hpp>
#include <kstd/system_error.hpp>
+#include <cstdint>
#include <optional>
#include <string_view>
+#include <utility>
namespace kernel::tests::filesystem
{
- auto filesystem::lookup(kstd::shared_ptr<kernel::filesystem::inode> const &, std::string_view) const
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
+ auto filesystem::probe(inode_ptr const &) const -> kstd::result<std::uint32_t>
+ {
+ return 0;
+ }
+
+ auto filesystem::mount(inode_ptr const &) -> kstd::result<mount_result>
+ {
+ return std::pair{kstd::make_shared<inode>(), nullptr};
+ }
+
+ auto filesystem::lookup(inode_ptr const &, std::string_view, driver_data_ptr) const -> kstd::result<inode_ptr>
{
return kstd::make_shared<inode>();
}
- auto filesystem::create_inode(kstd::shared_ptr<kernel::filesystem::inode> const &, std::string_view,
- kapi::filesystem::file_type, std::optional<kapi::filesystem::device_number>)
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>>
+ auto filesystem::create_inode(inode_ptr const &, std::string_view, kapi::filesystem::file_type, driver_data_ptr,
+ std::optional<kapi::filesystem::device_number>) -> kstd::result<inode_ptr>
{
return kstd::make_shared<inode>();
}
diff --git a/kernel/kernel/test_support/filesystem/filesystem.hpp b/kernel/kernel/test_support/filesystem/filesystem.hpp
index 7aa14a9f..3df2cccb 100644
--- a/kernel/kernel/test_support/filesystem/filesystem.hpp
+++ b/kernel/kernel/test_support/filesystem/filesystem.hpp
@@ -10,6 +10,7 @@
#include <kstd/result.hpp>
#include <kstd/system_error.hpp>
+#include <cstdint>
#include <optional>
#include <string_view>
@@ -19,13 +20,19 @@ namespace kernel::tests::filesystem
{
filesystem() = default;
- [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
+ [[nodiscard]] auto probe(inode_ptr const & inode) const -> kstd::result<std::uint32_t> override;
+
+ auto mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode)
+ -> kstd::result<mount_result> override;
+
+ [[nodiscard]] auto lookup(inode_ptr const & parent, std::string_view name,
+ driver_data_ptr driver_data = nullptr) const
-> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
- [[nodiscard]] auto create_inode(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name,
- kapi::filesystem::file_type type,
+ [[nodiscard]] auto create_inode(inode_ptr const & parent, std::string_view name, kapi::filesystem::file_type type,
+ driver_data_ptr driver_data = nullptr,
std::optional<kapi::filesystem::device_number> = std::nullopt)
- -> kstd::result<kstd::shared_ptr<kernel::filesystem::inode>> override;
+ -> kstd::result<inode_ptr> override;
};
} // namespace kernel::tests::filesystem
diff --git a/kernel/kernel/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/kernel/test_support/filesystem/storage_boot_module_fixture.cpp
index 3df23751..b9228c69 100644
--- a/kernel/kernel/test_support/filesystem/storage_boot_module_fixture.cpp
+++ b/kernel/kernel/test_support/filesystem/storage_boot_module_fixture.cpp
@@ -2,8 +2,6 @@
#include <kernel/bus/boot_modules.hpp>
#include <kernel/devices/storage.hpp>
-#include <kernel/filesystem/type.hpp>
-#include <kernel/filesystem/type_registry.hpp>
#include <kapi/boot_modules.hpp>
#include <kapi/boot_modules/device.hpp>
diff --git a/kernel/kernel/test_support/filesystem/type_registry.hpp b/kernel/kernel/test_support/filesystem/type_registry.hpp
deleted file mode 100644
index 8d5b6029..00000000
--- a/kernel/kernel/test_support/filesystem/type_registry.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_TYPE_REGISTRY_HPP
-#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_TYPE_REGISTRY_HPP
-
-namespace kernel::tests::filesystem::type_registry
-{
- //! Deinitialize the FS type registry singleton.
- auto deinit() -> void;
-} // namespace kernel::tests::filesystem::type_registry
-
-#endif \ No newline at end of file
diff --git a/kernel/kernel/test_support/state_reset_listener.cpp b/kernel/kernel/test_support/state_reset_listener.cpp
index e900b31c..59f103b7 100644
--- a/kernel/kernel/test_support/state_reset_listener.cpp
+++ b/kernel/kernel/test_support/state_reset_listener.cpp
@@ -1,13 +1,14 @@
#include <kernel/devices/storage.hpp>
#include <kernel/drivers/init.hpp>
+#include <kernel/filesystem/device_number_registry.hpp>
+#include <kernel/filesystem/driver_registry.hpp>
#include <kernel/filesystem/open_file_table.hpp>
-#include <kernel/filesystem/type_registry.hpp>
#include <kernel/filesystem/vfs.hpp>
#include <kernel/test_support/cio.hpp>
#include <kernel/test_support/cpu.hpp>
#include <kernel/test_support/filesystem/device_number_registry.hpp>
+#include <kernel/test_support/filesystem/driver_registry.hpp>
#include <kernel/test_support/filesystem/open_file_table.hpp>
-#include <kernel/test_support/filesystem/type_registry.hpp>
#include <kernel/test_support/filesystem/vfs.hpp>
#include <kernel/test_support/memory.hpp>
@@ -42,15 +43,15 @@ struct state_reset_listener : Catch::EventListenerBase
std::ignore = kernel::devices::storage::init();
kernel::filesystem::open_file_table::init();
- kernel::filesystem::type_registry::init();
+ std::ignore = kernel::filesystem::device_number_registry::get();
+ kernel::filesystem::driver_registry::init();
}
void testCaseEnded(Catch::TestCaseStats const &) override
{
- kernel::tests::filesystem::type_registry::deinit();
+ kernel::tests::filesystem::driver_registry::deinit();
kernel::tests::filesystem::device_number_registry::deinit();
kernel::tests::filesystem::vfs::deinit();
- kernel::tests::filesystem::type_registry::deinit();
kernel::tests::filesystem::open_file_table::deinit();
kapi::test_support::devices::deinit();