Phase 2c: rename, wired through the VFS to runtime.fs

Completes the Phase 2 FAT mutation set (truncate, mkdir, unlink, rename).

- engine: rename(dir, old_name, new_name) rewrites an existing entry's 8.3 name in
  place within the same directory. Refuses a missing source, a non-8.3 target, or a
  name that already exists; drops any long-name entries on the old file (it takes
  its new 8.3 name), LFN-aware like removeFile. Cross-directory and long-name-
  preserving rename are noted limitations. Host-tested (rename keeps contents;
  collision, non-8.3, and missing-source are refused).
- vfs protocol: a `rename` operation whose payload is old-path, a 0x00 separator,
  then new-path.
- VFS router: a forwardRename helper + a `.rename` case that requires both paths
  under the same mount (cross-filesystem rename is refused) and forwards the
  mount-relative old+new.
- fat server: a `.rename` handler that requires the same parent directory and calls
  engine.rename.
- runtime.fs: rename(old_path, new_path).
- fat-test now renames the file it created (before removing it) and asserts the old
  name is gone, behind a new `fat-rename` QEMU case.

Verified: zig build, zig build test (the engine rename unit test), zig build
check-fat-image, and a sequential QEMU sweep — fat-mount, fat-mutations, fat-rename,
vfs, vfs-client-death, log-flush, orderly-shutdown, initial-ramdisk, smoke — green.
This commit is contained in:
Daniel Samson
2026-07-13 20:22:32 +01:00
parent 184d90c2c6
commit 54635eecf5
8 changed files with 186 additions and 12 deletions
+9 -5
View File
@@ -62,20 +62,24 @@ pub fn main(init: runtime.process.Init) void {
wrote = (f.writeAll("mutation-ok") orelse 0) == "mutation-ok".len;
f.close();
}
// Rename it, then read from the new name and confirm the old name is gone.
const renamed = fs.rename("/mnt/usb/TESTDIR/HELLO.TXT", "/mnt/usb/TESTDIR/RENAMED.TXT");
const old_gone = !fs.exists("/mnt/usb/TESTDIR/HELLO.TXT");
if (renamed and old_gone) _ = runtime.system.write("fat-test: rename ok\n");
var readback = false;
if (fs.open("/mnt/usb/TESTDIR/HELLO.TXT", .{})) |reopened| {
if (fs.open("/mnt/usb/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("/mnt/usb/TESTDIR/HELLO.TXT");
const gone = !fs.exists("/mnt/usb/TESTDIR/HELLO.TXT");
if (wrote and readback and removed and gone) {
const removed = fs.remove("/mnt/usb/TESTDIR/RENAMED.TXT");
const gone = !fs.exists("/mnt/usb/TESTDIR/RENAMED.TXT");
if (wrote and renamed and old_gone and readback and removed and gone) {
_ = runtime.system.write("fat-test: mutations ok\n");
} else {
writeLine("fat-test: mutations FAILED (wrote={} read={} removed={} gone={})\n", .{ wrote, readback, removed, gone });
writeLine("fat-test: mutations FAILED (wrote={} renamed={} oldgone={} read={} removed={} gone={})\n", .{ wrote, renamed, old_gone, readback, removed, gone });
}
} else {
_ = runtime.system.write("fat-test: mkdir /mnt/usb/TESTDIR failed\n");