//! system/services/vfs — the user-space VFS server. Shipped in the initial_ramdisk, spawned as a //! ring-3 process, and reached by every other process through IPC (the `runtime` //! 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 (/device) layer on top in M10, //! where `open` on a /device name forwards to the owning driver's endpoint. const std = @import("std"); const runtime = @import("runtime"); const protocol = runtime.vfs_protocol; 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: protocol.Reply, payload: []const u8) usize { @memcpy(out[0..protocol.reply_size], std.mem.asBytes(&reply)); const n = @min(payload.len, out.len - protocol.reply_size); @memcpy(out[protocol.reply_size..][0..n], payload[0..n]); return protocol.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(message: []const u8, out: []u8) usize { if (message.len < protocol.request_size) return fail(out); const request = std.mem.bytesToValue(protocol.Request, message[0..protocol.request_size]); const payload = message[protocol.request_size..]; switch (request.operation) { .open => { const name = payload[0..@min(payload.len, request.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(request.node) orelse return fail(out); const nd = &nodes[of.node]; const off: usize = @intCast(request.offset); if (off >= nd.size) return writeReply(out, .{ .status = 0, .len = 0 }, &.{}); // EOF const n = @min(@min(nd.size - off, request.len), protocol.maximum_payload); return writeReply(out, .{ .status = 0, .len = @intCast(n) }, nd.data[off .. off + n]); }, .write => { const of = openAt(request.node) orelse return fail(out); const nd = &nodes[of.node]; const off: usize = @intCast(request.offset); if (off > nd.data.len) return fail(out); const n = @min(@min(payload.len, request.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) }, &.{}); }, .status => { const of = openAt(request.node) orelse return fail(out); const st = protocol.FileStatus{ .size = nodes[of.node].size, .kind = 0 }; return writeReply(out, .{ .status = 0, .len = @sizeOf(protocol.FileStatus) }, std.mem.asBytes(&st)); }, .close => { if (request.node < opens.len) opens[@intCast(request.node)].used = false; return writeReply(out, .{ .status = 0 }, &.{}); }, } } pub fn main() void { const endpoint = runtime.ipc.createEndpoint() orelse { _ = runtime.system.write("vfs: no endpoint\n"); return; }; if (!runtime.ipc.register(.vfs, endpoint)) { _ = runtime.system.write("vfs: register failed\n"); return; } _ = runtime.system.write("vfs: ready\n"); var reply_buffer: [protocol.message_maximum]u8 = undefined; var reply_len: usize = 0; var receive: [protocol.message_maximum]u8 = undefined; while (true) { const got = runtime.ipc.replyWait(endpoint, reply_buffer[0..reply_len], &receive, null); // Ignore notifications (none expected here); handle a request. reply_len = handle(receive[0..got.len], &reply_buffer); } } pub const panic = runtime.panic; comptime { _ = &runtime.start._start; }