M5: VFS mount support — mount table + forwarding router
Turn the flat-ramfs VFS into a router: a mount table maps an absolute path prefix (e.g. /mnt/usb) to a backend server's endpoint, and open/read/write/status/ readdir/close on a path under a mount are forwarded to that backend, which speaks the same vfs-protocol. This is what a FAT filesystem mounts into. - protocol: append readdir / mount / unmount operations, a NodeKind enum (the FSH file types) that now fills FileStatus.kind, a DirectoryEntry record, and a directory open flag. Appended values keep existing clients and tests unchanged. - vfs.zig: a mount table, longest-prefix routing, forwarding of every op on a backend handle, mount/unmount handlers (the backend arrives as the call's capability), and release-on-death that also closes the backend's handles. - path.zig: pure, host-tested mount-prefix matching that never captures a non-boundary like /mnt/usbextra. - unistd: mount(), opendir / readdir / closedir clients. Bare names still resolve in the flat ramfs — the backward-compat contract; the vfs and vfs-client-death tests pass unchanged. End-to-end mount+read is exercised by the FAT server (M6). Host units cover path matching and protocol sizes.
This commit is contained in:
@@ -146,3 +146,61 @@ pub fn close(fd: i32) void {
|
||||
_ = transact(request, &.{}, &.{});
|
||||
f.used = false;
|
||||
}
|
||||
|
||||
/// Mount a filesystem backend (its server endpoint) at absolute path `target`;
|
||||
/// the VFS then routes every path under `target` to that backend. Returns 0 or
|
||||
/// -1. This is the one call that hands the VFS a capability (the backend).
|
||||
pub fn mount(target: []const u8, backend: usize) i32 {
|
||||
const h = vfs() orelse return -1;
|
||||
const request = protocol.Request{ .operation = .mount, .node = 0, .offset = 0, .len = @intCast(target.len), .flags = 0 };
|
||||
var message: [protocol.message_maximum]u8 = undefined;
|
||||
@memcpy(message[0..protocol.request_size], std.mem.asBytes(&request));
|
||||
const tlen = @min(target.len, protocol.maximum_payload);
|
||||
@memcpy(message[protocol.request_size..][0..tlen], target[0..tlen]);
|
||||
var rbuf: [protocol.message_maximum]u8 = undefined;
|
||||
const result = ipc.callCap(h, message[0 .. protocol.request_size + tlen], &rbuf, backend) catch return -1;
|
||||
if (result.len < protocol.reply_size) return -1;
|
||||
return if (std.mem.bytesToValue(protocol.Reply, rbuf[0..protocol.reply_size]).status == 0) 0 else -1;
|
||||
}
|
||||
|
||||
/// A directory entry filled by `readdir`.
|
||||
pub const DirEntry = struct {
|
||||
kind: u32 = 0, // a protocol.NodeKind
|
||||
size: u64 = 0,
|
||||
name_buffer: [64]u8 = undefined,
|
||||
name_len: usize = 0,
|
||||
|
||||
pub fn name(self: *const DirEntry) []const u8 {
|
||||
return self.name_buffer[0..self.name_len];
|
||||
}
|
||||
};
|
||||
|
||||
/// Open a directory for reading with `readdir`. Returns an fd or -1.
|
||||
pub fn opendir(path: []const u8) i32 {
|
||||
return open(path, protocol.directory);
|
||||
}
|
||||
|
||||
/// Read the next entry of a directory fd into `entry`; returns false at EOF or on
|
||||
/// error. Advances the fd's cursor by one entry.
|
||||
pub fn readdir(fd: i32, entry: *DirEntry) bool {
|
||||
const f = fdPtr(fd) orelse return false;
|
||||
const request = protocol.Request{ .operation = .readdir, .node = f.node, .offset = f.offset, .len = 0, .flags = 0 };
|
||||
var buffer: [protocol.message_maximum]u8 = undefined;
|
||||
const r = transact(request, &.{}, &buffer) orelse return false;
|
||||
if (r.reply.status != 0 or r.reply.len == 0) return false; // error or EOF
|
||||
if (r.payload.len < protocol.directory_entry_size) return false;
|
||||
const header = std.mem.bytesToValue(protocol.DirectoryEntry, r.payload[0..protocol.directory_entry_size]);
|
||||
entry.kind = header.kind;
|
||||
entry.size = header.size;
|
||||
const source = r.payload[protocol.directory_entry_size..];
|
||||
const nlen = @min(@min(@as(usize, header.name_len), source.len), entry.name_buffer.len);
|
||||
@memcpy(entry.name_buffer[0..nlen], source[0..nlen]);
|
||||
entry.name_len = nlen;
|
||||
f.offset += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Close a directory fd (same as `close`).
|
||||
pub fn closedir(fd: i32) void {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user