kernel: mounts have owners — V0 of the volume-manager plan

fs_unmount was gated by nothing but the /protocol carve-out: any process
could unmount any prefix — latent with one mount owner, an obvious
cross-tenant hole once volumes multiply. Each backend mount now records the
mounting task, and the syscall layer enforces two rules that keep the
restart story intact: only the owner unmounts (a dead owner's mount is
swept lazily by resolution — strangers gain nothing by racing that), and a
mount may be REPLACED only by its live owner or after its owner died (the
respawned-filesystem path; displacement of a live mount would be worse than
unmounting it). Kernel-installed mounts are never displaceable.

The vfs-test park role is the discrimination: with the volume provably
mounted it attempts the foreign unmount, requires the refusal AND the
subtree still resolving, and withholds its "parked" marker otherwise —
against the ungated kernel the unmount was ALLOWED and vfs-client-death
fails; with the gate, green. (Its verification handle closes immediately:
the kernel test string-matches "released 1 handle(s)".)
This commit is contained in:
Daniel Samson
2026-08-09 16:16:14 +01:00
parent 451abba000
commit 60b41c0e82
3 changed files with 66 additions and 7 deletions
@@ -84,6 +84,28 @@ fn park() void {
_ = logging.write("vfstest: park open failed\n");
return;
}
// Mount ownership (V0, docs/volume-manager-plan.md): the volume is
// provably mounted (the parked file just opened on it), it is FAT's mount,
// and this process is not fat — unmounting it must be REFUSED and the
// subtree must still resolve afterwards. Bailing here withholds the
// "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")) {
_ = logging.write("vfstest: foreign unmount was ALLOWED\n");
return;
}
if (fs.open("/volumes/usb/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");
return;
}
_ = logging.write("vfstest: foreign unmount refused\n");
while (true) {
_ = logging.write("vfstest: parked\n");
time.sleepMillis(500);