From 9750db14da82181bc669c174cf17a3b746be9678 Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Mon, 10 Aug 2026 01:37:32 +0100 Subject: [PATCH] fat: install the FHS boot rewrites only on the system volume (S3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A volume backs /system/configuration and /system/logs only when it actually carries the /system tree — decided by content (resolve /system/configuration on its own media at mount), not by spawn order. The boot volume takes the branch and installs the two rewrites; a data volume resolves null, mounts only at its id-path, and never shadows the running system's config or logs with a dead mount. This retires the "resolve-at-bring-up quirk" the single-volume step deferred around: that diagnosis was wrong. A boot probe confirmed resolve() works the instant mount() returns — /system, /system/ configuration, /system/kernel, /system/services all resolve at bring-up (mount reads LBA 0 through the same block path, so a directory read cannot fail where the boot-sector read succeeded). No deferral needed. Behavior-preserving on the single boot volume (it carries the system tree, so it still installs all three mounts): suite stays 128/128. --- system/services/fat/fat.zig | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/system/services/fat/fat.zig b/system/services/fat/fat.zig index 8c405cc..fea3bc2 100644 --- a/system/services/fat/fat.zig +++ b/system/services/fat/fat.zig @@ -173,16 +173,23 @@ fn fatBringUp(endpoint: ipc.Handle) ?Harness.Volume { }; std.log.info("mounted FAT ({s}, {d} clusters, partition lba {d})", .{ @tagName(filesystem.geometry.fat_type), filesystem.geometry.cluster_count, filesystem.base_lba }); - // The volume mounts at its id-path (argv[2]), plus the two FHS rewrites so - // hierarchy paths (the logger's /system/logs) stay decoupled from which - // volume backs them. This single-volume increment's one volume IS the boot - // volume, so it installs both unconditionally; S3 (multi-volume) makes the - // rewrites content-conditional — installed only by whichever volume carries - // the system, decided by content, not order. + // Every volume mounts at its own id-path (argv[2]). The boot/system volume — + // the one carrying the /system tree — ADDITIONALLY installs the two FHS + // rewrites, so hierarchy paths (config reads, the logger's persistent + // /system/logs) stay decoupled from which volume backs them. Detection is by + // CONTENT, not spawn order: a volume is the system volume iff /system/ + // configuration resolves on its own media. A data volume has no /system, so it + // mounts only at its id-path and never shadows the running system's config or + // logs with a dead mount. mount_specs[0] = .{ .prefix = volume_mount_prefix }; - mount_specs[1] = .{ .prefix = "/system/configuration", .rewrite = "/system/configuration" }; - mount_specs[2] = .{ .prefix = "/system/logs", .rewrite = "/system/logs" }; - return .{ .engine = &filesystem, .mounts = mount_specs[0..3], .flush = flushIfDirty }; + var mount_count: usize = 1; + if (filesystem.resolve("/system/configuration") != null) { + std.log.info("volume {d} carries the system tree; backing /system/configuration and /system/logs", .{my_volume_id}); + mount_specs[1] = .{ .prefix = "/system/configuration", .rewrite = "/system/configuration" }; + mount_specs[2] = .{ .prefix = "/system/logs", .rewrite = "/system/logs" }; + mount_count = 3; + } + return .{ .engine = &filesystem, .mounts = mount_specs[0..mount_count], .flush = flushIfDirty }; } pub fn main(init: process.Init) void {