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:
@@ -2146,9 +2146,18 @@ fn systemFsMount(state: *architecture.CpuState) void {
|
||||
|
||||
const flags = sync.enter();
|
||||
defer sync.leave(flags);
|
||||
// The ownership gate. A LIVE owner's mount is its own: nobody else may
|
||||
// replace it — displacement-by-remount would be worse than unmounting.
|
||||
// A DEAD owner's mount is replaceable by anyone with a backend: that is
|
||||
// the restart story (a respawned filesystem is a new task retaking its
|
||||
// prefix). Kernel-installed mounts (owner 0) are never displaceable.
|
||||
if (vfs.mountOwner(prefix)) |owner| {
|
||||
const displaceable = owner != 0 and (owner == t.id or scheduler.taskByIdLocked(owner) == null);
|
||||
if (!displaceable) return failErr(state, ipc.EPERM);
|
||||
}
|
||||
const endpoint = ipc.resolveHandle(t, backend_handle) orelse return failErr(state, ipc.EBADF);
|
||||
endpoint.refcount += 1; // the mount table's reference
|
||||
if (!vfs.mountBackend(prefix, endpoint, rewrite)) {
|
||||
if (!vfs.mountBackend(prefix, endpoint, rewrite, t.id)) {
|
||||
ipc.dropRef(endpoint);
|
||||
return fail(state);
|
||||
}
|
||||
@@ -2166,6 +2175,12 @@ fn systemFsUnmount(state: *architecture.CpuState) void {
|
||||
if (!user_memory.copyFromUser(t.address_space, prefix_ptr, prefix)) return failErr(state, ipc.EFAULT);
|
||||
const flags = sync.enter();
|
||||
defer sync.leave(flags);
|
||||
// Only the mounting task unmounts. No dead-owner exception here: a dead
|
||||
// owner's mount is already being swept lazily by resolution, and a
|
||||
// stranger gains nothing legitimate by racing that — the restart story
|
||||
// goes through remount-replace, never through unmount.
|
||||
const owner = vfs.mountOwner(prefix) orelse return fail(state);
|
||||
if (owner != t.id) return failErr(state, ipc.EPERM);
|
||||
if (!vfs.unmount(prefix)) return fail(state);
|
||||
architecture.setSystemCallResult(state, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user