diff --git a/build.zig b/build.zig index e609877..e4ea6cf 100644 --- a/build.zig +++ b/build.zig @@ -368,7 +368,7 @@ pub fn build(b: *std.Build) void { // is the first "protocol module" (see docs/driver-model.md); usb/block will // expose theirs the same way. const vfs_protocol_module = b.addModule("vfs-protocol", .{ - .root_source_file = b.path("system/vfs-protocol.zig"), + .root_source_file = b.path("library/protocol/vfs/vfs-protocol.zig"), }); // The input wire protocol: the input service's public interface, exposed as its own @@ -572,6 +572,7 @@ pub fn build(b: *std.Build) void { // The FAT filesystem server: mounts the block device and serves it into the VFS // at /mnt/usb. Its engine (engine.zig / on-disk.zig) is imported relatively. const fat_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "fat", "system/services/fat/fat.zig"); + programModule(fat_exe).addImport("vfs-protocol", vfs_protocol_module); // Threaded: the display runs a mouse-listener thread alongside its compositor loop // (docs/threading.md, docs/display.md), so it opts into real atomics/TLS. const display_exe = addThreadedUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display", "system/services/display/display.zig"); @@ -957,7 +958,7 @@ pub fn build(b: *std.Build) void { "system/drivers/usb-hid/hid-report.zig", // HID boot-report keyboard/mouse decode "system/drivers/usb-storage/bulk-only-transport.zig", // CBW/CSW wrapper sizes "system/drivers/usb-storage/scsi.zig", // SCSI CDB encodings (big-endian) - "system/vfs-protocol.zig", // NodeKind / DirectoryEntry sizes + op values + "library/protocol/vfs/vfs-protocol.zig", // NodeKind / DirectoryEntry sizes + op values "system/services/fat/on-disk.zig", // FAT on-disk struct sizes + type detection "system/services/fat/engine.zig", // FAT read/write over a RAM-backed image "system/services/display/compositor.zig", // Rect math + fill/composite/blit-tile diff --git a/system/vfs-protocol.zig b/library/protocol/vfs/vfs-protocol.zig similarity index 100% rename from system/vfs-protocol.zig rename to library/protocol/vfs/vfs-protocol.zig diff --git a/library/runtime/runtime.zig b/library/runtime/runtime.zig index 7ef40a6..f9b4039 100644 --- a/library/runtime/runtime.zig +++ b/library/runtime/runtime.zig @@ -18,8 +18,6 @@ pub const time = @import("time.zig"); pub const heap = @import("heap.zig"); pub const ipc = @import("ipc.zig"); pub const start = @import("start.zig"); -/// The VFS wire protocol (shared with the VFS server). -pub const vfs_protocol = @import("vfs-protocol"); /// The device-manager protocol: hello + tree reports (docs/device-manager.md). pub const device_manager_protocol = @import("device-manager-protocol"); diff --git a/system/services/fat/fat.zig b/system/services/fat/fat.zig index e76a2c8..f06b3ab 100644 --- a/system/services/fat/fat.zig +++ b/system/services/fat/fat.zig @@ -13,7 +13,7 @@ const std = @import("std"); const runtime = @import("runtime"); const engine = @import("engine.zig"); const on_disk = @import("on-disk.zig"); -const protocol = runtime.vfs_protocol; +const vfs_protocol = @import("vfs-protocol"); const dma = runtime.dma; const mount_point = "/mnt/usb"; @@ -69,11 +69,11 @@ fn openAt(id: u64) ?*OpenNode { return if (o.used) o else null; } -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 writeReply(out: []u8, reply: vfs_protocol.Reply, payload: []const u8) usize { + @memcpy(out[0..vfs_protocol.reply_size], std.mem.asBytes(&reply)); + const n = @min(payload.len, out.len - vfs_protocol.reply_size); + @memcpy(out[vfs_protocol.reply_size..][0..n], payload[0..n]); + return vfs_protocol.reply_size + n; } fn fail(out: []u8) usize { @@ -183,7 +183,7 @@ fn splitParent(path: []const u8) ParentLeaf { fn handleOpen(out: []u8, path: []const u8, flags: u32, sender: u32) usize { var node = filesystem.resolve(path); - if (node == null and flags & protocol.create != 0) { + if (node == null and flags & vfs_protocol.create != 0) { const split = splitParent(path); const parent = filesystem.resolve(split.parent) orelse return fail(out); node = filesystem.createFile(parent, split.leaf); @@ -191,7 +191,7 @@ fn handleOpen(out: []u8, path: []const u8, flags: u32, sender: u32) usize { var resolved = node orelse return fail(out); // O_TRUNC: replace an existing file's contents rather than overwriting in place // (frees the old chain, so a shorter rewrite leaves no stale tail). - if (flags & protocol.truncate != 0 and !resolved.is_directory) { + if (flags & vfs_protocol.truncate != 0 and !resolved.is_directory) { filesystem.truncate(&resolved); } const index = allocOpen() orelse return fail(out); @@ -202,9 +202,9 @@ fn handleOpen(out: []u8, path: []const u8, flags: u32, sender: u32) usize { fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize { _ = capability; if (!mounted) return fail(out); // storage not up (yet): fail politely, clients retry - 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..]; + if (message.len < vfs_protocol.request_size) return fail(out); + const request = std.mem.bytesToValue(vfs_protocol.Request, message[0..vfs_protocol.request_size]); + const payload = message[vfs_protocol.request_size..]; // Stamp create/write with the current wall-clock time (mtime). Cheap, and it // keeps the engine pure (it takes the time as data, not a syscall). @@ -214,7 +214,7 @@ fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?runtime.i .open => return handleOpen(out, payload[0..@min(payload.len, request.len)], request.flags, sender), .read => { const o = openAt(request.node) orelse return fail(out); - var buffer: [protocol.maximum_payload]u8 = undefined; + var buffer: [vfs_protocol.maximum_payload]u8 = undefined; const want = @min(@as(usize, request.len), buffer.len); const n = filesystem.readFile(o.node, @intCast(request.offset), buffer[0..want]); return writeReply(out, .{ .status = 0, .len = @intCast(n) }, buffer[0..n]); @@ -227,21 +227,21 @@ fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?runtime.i }, .status => { const o = openAt(request.node) orelse return fail(out); - const kind: protocol.NodeKind = if (o.node.is_directory) .directory else .regular; - const status = protocol.FileStatus{ .size = o.node.size, .kind = @intFromEnum(kind), .mtime = o.node.mtime }; - return writeReply(out, .{ .status = 0, .len = @sizeOf(protocol.FileStatus) }, std.mem.asBytes(&status)); + const kind: vfs_protocol.NodeKind = if (o.node.is_directory) .directory else .regular; + const status = vfs_protocol.FileStatus{ .size = o.node.size, .kind = @intFromEnum(kind), .mtime = o.node.mtime }; + return writeReply(out, .{ .status = 0, .len = @sizeOf(vfs_protocol.FileStatus) }, std.mem.asBytes(&status)); }, .readdir => { const o = openAt(request.node) orelse return fail(out); if (!o.node.is_directory) return writeReply(out, .{ .status = 0, .len = 0 }, &.{}); const listing = filesystem.listEntry(o.node, @intCast(request.offset)) orelse return writeReply(out, .{ .status = 0, .len = 0 }, &.{}); - const kind: protocol.NodeKind = if (listing.is_directory) .directory else .regular; - const header = protocol.DirectoryEntry{ .kind = @intFromEnum(kind), .name_len = @intCast(listing.name_len), .size = listing.size }; - var buffer: [protocol.maximum_payload]u8 = undefined; - @memcpy(buffer[0..protocol.directory_entry_size], std.mem.asBytes(&header)); - const nlen = @min(listing.name_len, buffer.len - protocol.directory_entry_size); - @memcpy(buffer[protocol.directory_entry_size..][0..nlen], listing.name_buffer[0..nlen]); - const total = protocol.directory_entry_size + nlen; + const kind: vfs_protocol.NodeKind = if (listing.is_directory) .directory else .regular; + const header = vfs_protocol.DirectoryEntry{ .kind = @intFromEnum(kind), .name_len = @intCast(listing.name_len), .size = listing.size }; + var buffer: [vfs_protocol.maximum_payload]u8 = undefined; + @memcpy(buffer[0..vfs_protocol.directory_entry_size], std.mem.asBytes(&header)); + const nlen = @min(listing.name_len, buffer.len - vfs_protocol.directory_entry_size); + @memcpy(buffer[vfs_protocol.directory_entry_size..][0..nlen], listing.name_buffer[0..nlen]); + const total = vfs_protocol.directory_entry_size + nlen; return writeReply(out, .{ .status = 0, .len = @intCast(total) }, buffer[0..total]); }, .close => { @@ -287,7 +287,7 @@ fn onMessage(message: []const u8, out: []u8, sender: u32, capability: ?runtime.i } pub fn main() void { - runtime.service.run(protocol.message_maximum, .{ + runtime.service.run(vfs_protocol.message_maximum, .{ .service = .fat, .init = initialise, .on_message = onMessage,