fat: mount at the id-path from argv; migrate /volumes/usb -> id-path (S2)

The flip that makes the mount path the volume's content id. fat retires its
hardcoded fat_mounts: it reads its mount path from argv[2] (the volume manager
hands it the id-path, e.g. /volumes/fat-12345678, from the FAT serial), mounts
its volume root there, and installs the /system/configuration + /system/logs FHS
rewrites so /system/logs persistence stays decoupled from which volume backs it.
The rewrites are unconditional this increment (the single volume IS the boot
volume); S3 makes them content-conditional across N volumes. Every /volumes/usb
reference migrates to /volumes/fat-12345678 in one commit — the fat-test,
badge-scope-test, and vfs-test fixtures and the four QEMU regexes — plus a new
volume-identity-name case asserting the id-path mount and the /system/logs
rewrite. Discrimination: the regexes now require /volumes/fat-12345678, which the
old hardcoded fat never emitted (it mounted /volumes/usb). Full suite 128/128.
This commit is contained in:
Daniel Samson
2026-08-10 00:47:19 +01:00
parent f1e79d0eeb
commit df61693065
5 changed files with 77 additions and 42 deletions
@@ -38,7 +38,7 @@ const time = @import("time");
/// A scratch file on the volume, so the node the intruder tries to write through
/// is one nothing else reads. (A foreign write that *succeeded* would prove the
/// bug — it must not also damage the boot volume proving it.)
const held_path = "/volumes/usb/BADGE.TXT";
const held_path = "/volumes/fat-12345678/BADGE.TXT";
const held_contents = "held";
fn line(comptime format: []const u8, arguments: anytype) void {
@@ -46,12 +46,12 @@ fn line(comptime format: []const u8, arguments: anytype) void {
_ = logging.write(std.fmt.bufPrint(&buffer, format, arguments) catch return);
}
/// The fat server mounts /volumes/usb only after the whole USB storage chain is
/// The fat server mounts /volumes/fat-12345678 only after the whole USB storage chain is
/// up, and both instances race it.
fn waitForVolume() bool {
var tries: u32 = 0;
while (tries < 1400) : (tries += 1) {
if (fs.openDirectory("/volumes/usb")) |opened| {
if (fs.openDirectory("/volumes/fat-12345678")) |opened| {
var directory = opened;
directory.close();
return true;
@@ -79,7 +79,7 @@ pub fn main(init: process.Init) void {
fn own() void {
if (!waitForVolume()) {
_ = logging.write("badge-scope-test: FAILED (/volumes/usb never became available)\n");
_ = logging.write("badge-scope-test: FAILED (/volumes/fat-12345678 never became available)\n");
return;
}
var held = fs.open(held_path, .{ .create = true, .truncate = true }) orelse {
@@ -142,7 +142,7 @@ fn own() void {
fn intrude(foreign_node: u64, foreign_layer: u32) void {
if (!waitForVolume()) {
_ = logging.write("badge-scope-test: FAILED (/volumes/usb never became available)\n");
_ = logging.write("badge-scope-test: FAILED (/volumes/fat-12345678 never became available)\n");
return;
}
const node_verdict = probeNode(foreign_node);
+17 -17
View File
@@ -1,6 +1,6 @@
//! test/system/services/fat-test — a client that proves the FAT mount end to end:
//! it waits for the fat server to mount the USB volume at /volumes/usb, lists the
//! root directory through the VFS (which routes /volumes/usb to the fat backend), and
//! it waits for the fat server to mount the USB volume at /volumes/fat-12345678, lists the
//! root directory through the VFS (which routes /volumes/fat-12345678 to the fat backend), and
//! reads a known file off it. Shipped in the initial_ramdisk; the `fat-mount`
//! kernel test spawns it alongside init.
@@ -18,16 +18,16 @@ fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
pub fn main(init: process.Init) void {
_ = init;
// Wait for /volumes/usb to be mounted — the fat server races us at boot (it must
// Wait for /volumes/fat-12345678 to be mounted — the fat server races us at boot (it must
// bring up the whole USB storage chain first).
var opened: ?fs.Directory = null;
var tries: u32 = 0;
while (opened == null and tries < 1400) : (tries += 1) {
opened = fs.openDirectory("/volumes/usb");
opened = fs.openDirectory("/volumes/fat-12345678");
if (opened == null) time.sleepMillis(50);
}
var dir = opened orelse {
_ = logging.write("fat-test: /volumes/usb never became available\n");
_ = logging.write("fat-test: /volumes/fat-12345678 never became available\n");
return;
};
@@ -43,56 +43,56 @@ pub fn main(init: process.Init) void {
// Read a known file off the boot volume through the mount (best effort): the
// kernel image is an ELF, so its first bytes are the ELF magic.
if (fs.open("/volumes/usb/system/kernel", .{})) |opened_file| {
if (fs.open("/volumes/fat-12345678/system/kernel", .{})) |opened_file| {
var file = opened_file;
var magic: [4]u8 = undefined;
const n = file.read(&magic) orelse 0;
file.close();
if (n == 4 and magic[0] == 0x7F and magic[1] == 'E' and magic[2] == 'L' and magic[3] == 'F') {
_ = logging.write("fat-test: read /volumes/usb/system/kernel ELF magic ok\n");
_ = logging.write("fat-test: read /volumes/fat-12345678/system/kernel ELF magic ok\n");
} else {
writeLine("fat-test: /volumes/usb/system/kernel read {d} bytes (not ELF magic)\n", .{n});
writeLine("fat-test: /volumes/fat-12345678/system/kernel read {d} bytes (not ELF magic)\n", .{n});
}
}
// Exercise directory + file mutation through the mount: mkdir, create a file
// inside it, read it back, then remove it — proof mkdir/unlink reach the engine.
if (fs.makeDirectory("/volumes/usb/TESTDIR")) {
if (fs.makeDirectory("/volumes/fat-12345678/TESTDIR")) {
var wrote = false;
if (fs.open("/volumes/usb/TESTDIR/HELLO.TXT", .{ .create = true, .truncate = true })) |created| {
if (fs.open("/volumes/fat-12345678/TESTDIR/HELLO.TXT", .{ .create = true, .truncate = true })) |created| {
var f = created;
wrote = (f.writeAll("mutation-ok") orelse 0) == "mutation-ok".len;
f.close();
}
// The created file carries a real modification time (stamped from the RTC).
var mtime_ok = false;
if (fs.attributes("/volumes/usb/TESTDIR/HELLO.TXT")) |attrs| {
if (fs.attributes("/volumes/fat-12345678/TESTDIR/HELLO.TXT")) |attrs| {
writeLine("fat-test: mtime {d}\n", .{attrs.mtime});
mtime_ok = attrs.mtime > 1_577_836_800; // after 2020-01-01
}
if (mtime_ok) _ = logging.write("fat-test: mtime ok\n");
// Rename it, then read from the new name and confirm the old name is gone.
const renamed = fs.rename("/volumes/usb/TESTDIR/HELLO.TXT", "/volumes/usb/TESTDIR/RENAMED.TXT");
const old_gone = !fs.exists("/volumes/usb/TESTDIR/HELLO.TXT");
const renamed = fs.rename("/volumes/fat-12345678/TESTDIR/HELLO.TXT", "/volumes/fat-12345678/TESTDIR/RENAMED.TXT");
const old_gone = !fs.exists("/volumes/fat-12345678/TESTDIR/HELLO.TXT");
if (renamed and old_gone) _ = logging.write("fat-test: rename ok\n");
var readback = false;
if (fs.open("/volumes/usb/TESTDIR/RENAMED.TXT", .{})) |reopened| {
if (fs.open("/volumes/fat-12345678/TESTDIR/RENAMED.TXT", .{})) |reopened| {
var f = reopened;
var buf: [16]u8 = undefined;
const got = f.read(&buf) orelse 0;
f.close();
readback = std.mem.eql(u8, buf[0..got], "mutation-ok");
}
const removed = fs.remove("/volumes/usb/TESTDIR/RENAMED.TXT");
const gone = !fs.exists("/volumes/usb/TESTDIR/RENAMED.TXT");
const removed = fs.remove("/volumes/fat-12345678/TESTDIR/RENAMED.TXT");
const gone = !fs.exists("/volumes/fat-12345678/TESTDIR/RENAMED.TXT");
if (wrote and mtime_ok and renamed and old_gone and readback and removed and gone) {
_ = logging.write("fat-test: mutations ok\n");
} else {
writeLine("fat-test: mutations FAILED (wrote={} mtime={} renamed={} oldgone={} read={} removed={} gone={})\n", .{ wrote, mtime_ok, renamed, old_gone, readback, removed, gone });
}
} else {
_ = logging.write("fat-test: mkdir /volumes/usb/TESTDIR failed\n");
_ = logging.write("fat-test: mkdir /volumes/fat-12345678/TESTDIR failed\n");
}
if (count > 0) {
+4 -4
View File
@@ -77,7 +77,7 @@ fn park() void {
var parked: ?fs.File = null;
var tries: u32 = 0;
while (parked == null and tries < 1000) : (tries += 1) {
parked = fs.open("/volumes/usb/parked", .{ .create = true });
parked = fs.open("/volumes/fat-12345678/parked", .{ .create = true });
if (parked == null) time.sleepMillis(20);
}
if (parked == null) {
@@ -92,16 +92,16 @@ fn park() void {
// "parked" marker, which fails the vfs-client-death case: before the
// ownership gate existed, any process could unmount any prefix, and this
// fixture would have deleted the volume out from under the whole boot.
if (fs.fsUnmount("/volumes/usb")) {
if (fs.fsUnmount("/volumes/fat-12345678")) {
_ = logging.write("vfstest: foreign unmount was ALLOWED\n");
return;
}
if (fs.open("/volumes/usb/parked", .{})) |resolved| {
if (fs.open("/volumes/fat-12345678/parked", .{})) |resolved| {
var verification = resolved;
verification.close(); // the park below must be the client's ONLY open
// handle — the kernel test string-matches "released 1 handle(s)".
} else {
_ = logging.write("vfstest: /volumes/usb gone after refused unmount\n");
_ = logging.write("vfstest: /volumes/fat-12345678 gone after refused unmount\n");
return;
}
_ = logging.write("vfstest: foreign unmount refused\n");