diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-08-27 18:41:38 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-08-27 18:59:30 +0200 |
| commit | 62c65eae804f2cedb3e1856aad976c57749b7c19 (patch) | |
| tree | e60d2b0d9f4f167a40ef5eff45f7ac4a06a718c2 | |
| parent | 1e5515f822e22a2ad175a35ef2c0473a40be8d88 (diff) | |
| download | kernel-62c65eae804f2cedb3e1856aad976c57749b7c19.tar.xz kernel-62c65eae804f2cedb3e1856aad976c57749b7c19.zip | |
kernel/fs: devfs: inject . and .. entries
| -rw-r--r-- | kernel/kernel/filesystems/devfs/inode.cpp | 56 | ||||
| -rw-r--r-- | kernel/kernel/filesystems/devfs/inode.tests.cpp | 36 |
2 files changed, 69 insertions, 23 deletions
diff --git a/kernel/kernel/filesystems/devfs/inode.cpp b/kernel/kernel/filesystems/devfs/inode.cpp index fb26e34e..dad4e381 100644 --- a/kernel/kernel/filesystems/devfs/inode.cpp +++ b/kernel/kernel/filesystems/devfs/inode.cpp @@ -27,7 +27,9 @@ namespace kernel::filesystems::devfs namespace { constexpr auto static sentinel = kapi::filesystem::directory_cursor{std::numeric_limits<std::uint64_t>::max()}; - constexpr auto static root_inode_number = std::numeric_limits<std::uint64_t>::max() - 1; + constexpr auto static root_inode_number = 1; + constexpr auto static dot_flag = std::uint64_t{1} << 16; + constexpr auto static dot_dot_flag = std::uint64_t{1} << 17; } // namespace auto inode::read(std::span<std::byte>, kstd::bytes) const -> kstd::result<kstd::bytes> @@ -49,7 +51,8 @@ namespace kernel::filesystems::devfs { return vfs::inode::status().transform([](auto status) { status.inode_number = root_inode_number; - status.mode = 0544; // NOLINT(readability-magic-numbers) + status.mode = + std::to_underlying(kapi::filesystem::file_type::directory) | 0544; // NOLINT(readability-magic-numbers) return status; }); } @@ -69,10 +72,41 @@ namespace kernel::filesystems::devfs return std::pair{0, sentinel}; } + auto flags = position.value & (dot_flag | dot_dot_flag); + auto count = 0uz; + + if (!(flags & dot_flag)) + { + entries[count++] = kapi::filesystem::directory_entry{ + .name = ".", + .type = kapi::filesystem::file_type::directory, + .inode_number = root_inode_number, + }; + flags |= dot_flag; + if (count == requested) + { + return std::pair{count, kapi::filesystem::directory_cursor{position.value | flags}}; + } + } + + if (!(flags & dot_dot_flag)) + { + entries[count++] = kapi::filesystem::directory_entry{ + .name = "..", + .type = kapi::filesystem::file_type::directory, + .inode_number = root_inode_number, + }; + flags |= dot_dot_flag; + if (count == requested) + { + return std::pair{count, kapi::filesystem::directory_cursor{position.value | flags}}; + } + } + auto devices = vfs::device_number_registry::get().all(); if (devices.empty()) { - return std::pair{0, position}; + return std::pair{0, kapi::filesystem::directory_cursor{position.value | flags}}; } std::ranges::stable_sort(devices, std::ranges::less{}, &vfs::device_number_registry::entry::number); @@ -82,30 +116,32 @@ namespace kernel::filesystems::devfs auto const begin = std::ranges::lower_bound(devices, device_number, std::ranges::less{}, &vfs::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))); + auto const to_read = std::min(requested - count, static_cast<std::size_t>(std::ranges::distance(begin, end))); if (to_read == 0) { - return std::pair{0, position}; + return std::pair{0, kapi::filesystem::directory_cursor{position.value | flags}}; } - std::ranges::transform(begin, begin + to_read, std::ranges::begin(entries), [](auto const & entry) { + std::ranges::transform(begin, begin + to_read, std::ranges::begin(entries) + count, [](auto const & entry) { return kapi::filesystem::directory_entry{ .name = entry.name, .type = entry.type, .inode_number = std::bit_cast<std::uint16_t>(entry.number), }; }); + count += to_read; auto const last_copied = begin + to_read; if (last_copied == end) { - return std::pair{to_read, sentinel}; + return std::pair{count, sentinel}; } auto const new_device_number = last_copied->number; - auto const new_cursor = kapi::filesystem::directory_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}; + auto const new_cursor = + kapi::filesystem::directory_cursor{flags | static_cast<std::uint64_t>(new_device_number.minor) | + (static_cast<std::uint64_t>(new_device_number.major) << 8)}; + return std::pair{count, new_cursor}; } } // namespace kernel::filesystems::devfs
\ No newline at end of file diff --git a/kernel/kernel/filesystems/devfs/inode.tests.cpp b/kernel/kernel/filesystems/devfs/inode.tests.cpp index 4e315a7a..b923aeb5 100644 --- a/kernel/kernel/filesystems/devfs/inode.tests.cpp +++ b/kernel/kernel/filesystems/devfs/inode.tests.cpp @@ -17,7 +17,9 @@ #include <algorithm> #include <cstddef> +#include <ranges> #include <tuple> +#include <vector> using namespace kstd::units_literals; @@ -111,20 +113,24 @@ SCENARIO("Devfs inode directory listing", "[filesystem][devfs][inode]") WHEN("the directory listing is requested with a buffer large enough for all of them") { - auto entries = kstd::vector<kapi::filesystem::directory_entry>{4}; - auto result = root.read_directory(kapi::filesystem::directory_cursor{}, entries); + auto buffer = kstd::vector<kapi::filesystem::directory_entry>{6}; + auto result = root.read_directory(kapi::filesystem::directory_cursor{}, buffer); + + auto entries = std::ranges::to<std::vector>(buffer | std::views::filter([](auto const & e) { + return e.type == kapi::filesystem::file_type::block_device; + })); THEN("all devices are listed") { REQUIRE(result); auto [count, next] = *result; - REQUIRE(count == 3); + REQUIRE(count == 5); auto names = kstd::vector<kstd::string>{3}; - for (auto i = 0uz; i < count; ++i) + for (auto & entry : entries) { - REQUIRE(entries[i].type == kapi::filesystem::file_type::block_device); - names.push_back(entries[i].name); + REQUIRE(entry.type == kapi::filesystem::file_type::block_device); + names.push_back(entry.name); } REQUIRE(std::ranges::find(names, "devfs_readdir_device1") != names.end()); @@ -133,7 +139,7 @@ SCENARIO("Devfs inode directory listing", "[filesystem][devfs][inode]") AND_THEN("listing again with the returned cursor reports 'end-of-directory'") { - auto result = root.read_directory(next, entries); + auto result = root.read_directory(next, buffer); REQUIRE(result); auto [count, cursor] = *result; @@ -148,7 +154,7 @@ SCENARIO("Devfs inode directory listing", "[filesystem][devfs][inode]") auto seen = kstd::vector<kstd::string>{}; auto position = kapi::filesystem::directory_cursor{}; - for (auto guard = 0; guard < 4; ++guard) + for (auto guard = 0; guard < 5; ++guard) { auto one = kstd::vector<kapi::filesystem::directory_entry>(1); auto result = root.read_directory(position, one); @@ -164,11 +170,11 @@ SCENARIO("Devfs inode directory listing", "[filesystem][devfs][inode]") THEN("nothing is skipped or duplicated across the paginated calls") { - REQUIRE(seen.size() == 3); + REQUIRE(seen.size() == 5); auto sorted = seen; std::ranges::sort(sorted); std::ignore = std::ranges::unique(sorted); - REQUIRE(sorted.size() == 3); + REQUIRE(sorted.size() == 5); } } @@ -193,15 +199,19 @@ SCENARIO("Devfs inode read_directory orders by device number, not registration o WHEN("read_directory is called with room for both remaining entries") { - auto entries = kstd::vector<kapi::filesystem::directory_entry>(2); - auto result = root.read_directory(kapi::filesystem::directory_cursor{}, entries); + auto buffer = kstd::vector<kapi::filesystem::directory_entry>(4); + auto result = root.read_directory(kapi::filesystem::directory_cursor{}, buffer); + + auto entries = std::ranges::to<std::vector>(buffer | std::views::filter([](auto const & e) { + return e.type == kapi::filesystem::file_type::block_device; + })); 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(count == 4); REQUIRE(entries[0].name == "readdir_reuse_later_in"); REQUIRE(entries[1].name == "readdir_reuse_still_in"); } |
