reorg: move test fixtures to test/system/services (source + boot volume)

The 11 QEMU-suite fixtures lived mixed into system/services/ with their
binaries bundled at /system/tests/<name>. Now the repo path is the boot
path, like every real service: test/system/services/<name>. fat-test
moves out of the fat server's directory into its own; display-demo
stays a boot service.

- kernel VFS: setInitialRamdisk derives one read-only initrd mount per
  top-level tree named by the ramdisk entry paths (/system, /test),
  registers ancestors generically with self-parented roots, and refuses
  backend shadowing of any initrd tree
- EFI loader: the fallback walk also enumerates \test (optional — a
  volume without fixtures still boots); manifest and capsule unchanged
- path literals: vfs-test self-open + create probe, process-test
  process_enumerate matches, the args-echo argv[0] expectation; the
  kvfs case now covers the /test root end to end
- docs: DFHS /test rows, tree diagrams, loader prose, and the location
  convention gain the third home; fixed the input-source link

100/100 QEMU cases pass.
This commit is contained in:
Daniel Samson
2026-07-23 00:11:07 +01:00
parent b9cec7d1be
commit f023f1cfd6
24 changed files with 150 additions and 108 deletions
+21 -21
View File
@@ -4,7 +4,7 @@
//! the mechanism is **resolve + redirect**:
//!
//! - `fs_resolve(path)` walks the mount table. A path under a KERNEL-backed
//! mount (the initrd at /system, the scratch ram nodes) resolves to a
//! mount (the initrd trees at /system and /test, the scratch ram nodes) resolves to a
//! stateless node TOKEN served directly by `fs_node` (read/status/readdir
//! with copy-out). A path under a USERSPACE mount (the fat server at
//! /mnt/usb and /var) resolves to the backend's ENDPOINT: the kernel
@@ -85,7 +85,7 @@ const maximum_directories = 8;
const Directory = struct {
path: [maximum_prefix]u8 = undefined,
path_len: usize = 0,
parent: usize = 0, // index into `directories`; 0 is /system itself
parent: usize = 0, // index into `directories`; a top-level tree root is its own parent
fn slice(self: *const Directory) []const u8 {
return self.path[0..self.path_len];
@@ -122,37 +122,35 @@ fn parentOf(path: []const u8) []const u8 {
// --- boot wiring -------------------------------------------------------------
/// Publish the initrd as the kernel-backed /system mount and derive its bounded
/// directory table (the unique parents of the entry paths). Called once at boot.
/// Publish the initrd as kernel-backed mounts — one per top-level tree its
/// entry paths name (/system, /test) — and derive the bounded directory table
/// (every ancestor directory of the entry paths). Called once at boot.
pub fn setInitialRamdisk(image: []const u8) void {
ramdisk_image = image;
installMount("/system", .kernel_initrd, null, "");
// Directory 0 is /system itself.
directories[0] = .{ .parent = 0 };
@memcpy(directories[0].path[0..7], "/system");
directories[0].path_len = 7;
directory_count = 1;
directory_count = 0;
const rd = initial_ramdisk.Reader.init(image) orelse return;
var i: u32 = 0;
while (i < rd.count) : (i += 1) {
const item = rd.entry(i) orelse continue;
// Register every ancestor directory strictly below /system.
// Register every ancestor directory, top-level tree roots included.
var parent = parentOf(item.name);
while (parent.len > 7) : (parent = parentOf(parent)) {
while (parent.len > 1) : (parent = parentOf(parent)) {
if (directoryIndex(parent) == null and directory_count < maximum_directories) {
var d = &directories[directory_count];
@memcpy(d.path[0..parent.len], parent);
d.path_len = parent.len;
d.parent = 0; // fixed up below once all exist
d.parent = directory_count; // fixed up below once all exist
directory_count += 1;
}
}
}
// Parent links (a second pass so out-of-order registration doesn't matter).
for (directories[1..directory_count]) |*d| {
d.parent = directoryIndex(parentOf(d.slice())) orelse 0;
// A top-level root keeps itself as parent and becomes an initrd mount.
for (directories[0..directory_count], 0..) |*d, index| {
const parent = parentOf(d.slice());
d.parent = directoryIndex(parent) orelse index;
if (parent.len == 1) installMount(d.slice(), .kernel_initrd, null, "");
}
}
@@ -193,7 +191,7 @@ pub const Resolved = union(enum) {
};
/// Longest-prefix match over the mount table, then per-kind resolution.
/// `create`-intent on the immutable /system fails here (EROFS-style).
/// `create`-intent on the immutable initrd trees fails here (EROFS-style).
pub fn resolvePath(path: []const u8, wants_create: bool) Resolved {
if (!isAbsolute(path)) {
return .{ .not_found = {} }; // bare names have no kernel namespace (ramfs retired)
@@ -295,11 +293,11 @@ pub fn nodeReaddir(node_token: u64, cursor: u64, name_out: []u8) ?struct { heade
const self_path = directories[@intCast(directory_index)].slice();
var index: u64 = 0;
// Subdirectories whose parent is this directory.
// Subdirectories whose parent is this directory (roots are self-parented,
// so they never appear as anyone's child).
for (directories[0..directory_count], 0..) |*d, i| {
if (i == directory_index) continue;
if (d.parent != directory_index) continue;
if (i == 0) continue;
if (index == cursor) {
const name = d.slice()[self_path.len + 1 ..];
const n = @min(name.len, name_out.len);
@@ -330,11 +328,13 @@ pub fn nodeReaddir(node_token: u64, cursor: u64, name_out: []u8) ?struct { heade
/// Mount `backend` at `prefix` with an optional backend-side `rewrite` prefix.
/// The endpoint reference is taken by the caller (process.zig bumps it); refuses
/// shadowing or replacing /system.
/// shadowing or replacing the initrd trees (/system, /test).
pub fn mountBackend(prefix: []const u8, backend: *ipc.Endpoint, rewrite: []const u8) bool {
if (!isAbsolute(prefix) or prefix.len < 2 or prefix.len > maximum_prefix) return false;
if (rewrite.len > maximum_rewrite) return false;
if (underMount(prefix, "/system") != null) return false; // the initrd is not shadowable
for (&mounts) |*m| { // the initrd trees are not shadowable
if (m.used and m.kind == .kernel_initrd and underMount(prefix, m.prefixSlice()) != null) return false;
}
installMount(prefix, .backend, backend, rewrite);
return true;
}