//! /sbin/vfs — the user-space VFS server. Shipped in the initrd, spawned as a //! ring-3 process, and reached by every other process through IPC (the `rt` //! file API marshals open/read/write/stat/close into calls to this server's //! endpoint, published under the well-known `vfs` service id). //! //! For now the namespace is a small in-memory ramfs (opening a name creates it): //! enough to prove the whole path — client file API -> IPC -> server dispatch -> //! reply. Device nodes backed by user-space drivers (/dev) layer on top in M10, //! where `open` on a /dev name forwards to the owning driver's endpoint. const std = @import("std"); const rt = @import("rt"); const proto = rt.vfsproto; const Node = struct { used: bool = false, name: [24]u8 = undefined, name_len: usize = 0, data: [512]u8 = undefined, size: usize = 0, }; const OpenFile = struct { used: bool = false, node: usize = 0, }; var nodes = [_]Node{.{}} ** 8; var opens = [_]OpenFile{.{}} ** 16; fn findNode(name: []const u8) ?usize { for (&nodes, 0..) |*n, i| { if (n.used and std.mem.eql(u8, n.name[0..n.name_len], name)) return i; } return null; } fn createNode(name: []const u8) ?usize { for (&nodes, 0..) |*n, i| { if (!n.used) { const l = @min(name.len, n.name.len); @memcpy(n.name[0..l], name[0..l]); n.* = .{ .used = true, .name = n.name, .name_len = l, .size = 0 }; return i; } } return null; } fn openAt(id: u64) ?*OpenFile { if (id >= opens.len) return null; const o = &opens[@intCast(id)]; return if (o.used) o else null; } /// Serialise a reply header + payload into `out`; returns the total length. fn writeReply(out: []u8, reply: proto.Reply, payload: []const u8) usize { @memcpy(out[0..proto.reply_size], std.mem.asBytes(&reply)); const n = @min(payload.len, out.len - proto.reply_size); @memcpy(out[proto.reply_size..][0..n], payload[0..n]); return proto.reply_size + n; } fn fail(out: []u8) usize { return writeReply(out, .{ .status = -1 }, &.{}); } /// Handle one request; write the reply into `out`, return its length. fn handle(msg: []const u8, out: []u8) usize { if (msg.len < proto.req_size) return fail(out); const req = std.mem.bytesToValue(proto.Request, msg[0..proto.req_size]); const payload = msg[proto.req_size..]; switch (req.op) { .open => { const name = payload[0..@min(payload.len, req.len)]; const ni = findNode(name) orelse createNode(name) orelse return fail(out); for (&opens, 0..) |*o, i| { if (!o.used) { o.* = .{ .used = true, .node = ni }; return writeReply(out, .{ .status = 0, .node = i }, &.{}); } } return fail(out); }, .read => { const of = openAt(req.node) orelse return fail(out); const nd = &nodes[of.node]; const off: usize = @intCast(req.offset); if (off >= nd.size) return writeReply(out, .{ .status = 0, .len = 0 }, &.{}); // EOF const n = @min(@min(nd.size - off, req.len), proto.max_payload); return writeReply(out, .{ .status = 0, .len = @intCast(n) }, nd.data[off .. off + n]); }, .write => { const of = openAt(req.node) orelse return fail(out); const nd = &nodes[of.node]; const off: usize = @intCast(req.offset); if (off > nd.data.len) return fail(out); const n = @min(@min(payload.len, req.len), nd.data.len - off); @memcpy(nd.data[off .. off + n], payload[0..n]); if (off + n > nd.size) nd.size = off + n; return writeReply(out, .{ .status = 0, .len = @intCast(n) }, &.{}); }, .stat => { const of = openAt(req.node) orelse return fail(out); const st = proto.Stat{ .size = nodes[of.node].size, .kind = 0 }; return writeReply(out, .{ .status = 0, .len = @sizeOf(proto.Stat) }, std.mem.asBytes(&st)); }, .close => { if (req.node < opens.len) opens[@intCast(req.node)].used = false; return writeReply(out, .{ .status = 0 }, &.{}); }, } } pub fn main() void { const ep = rt.ipc.createEndpoint() orelse { _ = rt.sys.write("vfs: no endpoint\n"); return; }; if (!rt.ipc.register(.vfs, ep)) { _ = rt.sys.write("vfs: register failed\n"); return; } _ = rt.sys.write("vfs: ready\n"); var reply_buf: [proto.msg_max]u8 = undefined; var reply_len: usize = 0; var recv: [proto.msg_max]u8 = undefined; while (true) { const got = rt.ipc.replyWait(ep, reply_buf[0..reply_len], &recv); // Ignore notifications (none expected here); handle a request. reply_len = handle(recv[0..got.len], &reply_buf); } } pub const panic = rt.panic; comptime { _ = &rt.start._start; }