//! system/services/fat/fat-test — a client that proves the FAT mount end to end: //! it waits for the fat server to mount the USB volume at /mnt/usb, lists the //! root directory through the VFS (which routes /mnt/usb 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. const std = @import("std"); const runtime = @import("runtime"); const fs = runtime.fs; fn writeLine(comptime fmt: []const u8, arguments: anytype) void { var line: [128]u8 = undefined; _ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return); } pub fn main(init: runtime.process.Init) void { _ = init; // Wait for /mnt/usb 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("/mnt/usb"); if (opened == null) runtime.system.sleep(50); } var dir = opened orelse { _ = runtime.system.write("fat-test: /mnt/usb never became available\n"); return; }; var count: u32 = 0; var entry: fs.Entry = .{}; while (dir.next(&entry)) { writeLine("fat-test: entry '{s}' kind={d} size={d}\n", .{ entry.name(), @intFromEnum(entry.kind), entry.size }); count += 1; if (count > 32) break; } dir.close(); writeLine("fat-test: listed {d} entries\n", .{count}); // 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("/mnt/usb/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') { _ = runtime.system.write("fat-test: read /mnt/usb/system/kernel ELF magic ok\n"); } else { writeLine("fat-test: /mnt/usb/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("/mnt/usb/TESTDIR")) { var wrote = false; if (fs.open("/mnt/usb/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("/mnt/usb/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) _ = runtime.system.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("/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/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/RENAMED.TXT"); const gone = !fs.exists("/mnt/usb/TESTDIR/RENAMED.TXT"); if (wrote and mtime_ok 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={} mtime={} renamed={} oldgone={} read={} removed={} gone={})\n", .{ wrote, mtime_ok, renamed, old_gone, readback, removed, gone }); } } else { _ = runtime.system.write("fat-test: mkdir /mnt/usb/TESTDIR failed\n"); } if (count > 0) { while (true) { _ = runtime.system.write("fat-test: ok\n"); runtime.system.sleep(1000); } } _ = runtime.system.write("fat-test: root listing was empty\n"); }