Phase 2b: wire mkdir + unlink through the VFS to runtime.fs

The engine gained mkdir/unlink in Phase 2a; this exposes them as first-class
filesystem operations so programs can use them.

- vfs protocol: two new path-based operations, mkdir and unlink (appended, so
  existing opcodes/offsets are unchanged).
- VFS router: a forwardPath helper relays a path-based op under a mount to its
  backend; the mkdir/unlink cases forward to the mounted filesystem (the flat
  ramfs refuses them — it has no directories).
- fat server: mkdir -> engine.createDirectory, unlink -> engine.removeFile, each
  resolving the parent via a shared splitParent helper (also used by open-create).
- runtime.fs: makeDirectory(path) and remove(path).
- fat-test now exercises the whole path — mkdir /mnt/usb/TESTDIR, create + write +
  read a file inside it, then remove it — behind a new `fat-mutations` QEMU case.

Verified: zig build, zig build test, zig build check-fat-image, and a sequential
QEMU sweep — fat-mount, fat-mutations (mkdir/write/read/unlink through the mount),
vfs, vfs-client-death, log-flush, orderly-shutdown, initial-ramdisk, smoke — green.
This commit is contained in:
Daniel Samson
2026-07-13 20:12:07 +01:00
parent a32eed877d
commit 184d90c2c6
7 changed files with 117 additions and 12 deletions
+19
View File
@@ -218,6 +218,25 @@ pub fn openDirectory(path: []const u8) ?Directory {
return .{ .node = file.node };
}
// A path-based request that returns only a status (mkdir, unlink).
fn pathOperation(operation: protocol.Operation, path: []const u8) bool {
const request = protocol.Request{ .operation = operation, .node = 0, .offset = 0, .len = @intCast(path.len), .flags = 0 };
const r = transact(request, path, &.{}) orelse return false;
return r.reply.status == 0;
}
/// Create a directory at `path` (its parent must already exist). Returns true on
/// success. Only works under a mounted filesystem that supports directories.
pub fn makeDirectory(path: []const u8) bool {
return pathOperation(.mkdir, path);
}
/// Remove the file at `path`. Returns true on success. Directories are refused
/// (a separate directory-removal would have to check emptiness).
pub fn remove(path: []const u8) bool {
return pathOperation(.unlink, path);
}
/// Mount a filesystem backend (its server endpoint) at absolute path `target`;
/// the VFS then routes everything under `target` to that backend. This is the one
/// call that hands the VFS a capability (the backend endpoint). Returns true on