From c1990b3e6e196ad41379aa432bf3ccca8a65d9f0 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 20 Sep 2026 00:36:50 -0300 Subject: Flatten the 9P control tree and move it out of fs.zig The served tree loses the self/ level: /index /ctl /new /log /screen /listeners /pane//... /os, with /src only in -Dembed-sources=true builds (default off, on for esp32p4). ctl speaks the editor's own language with two lowercase verbs, look TEXT and exec TEXT, plus acme's addr verbs; the new/ factory directory becomes one clone file; cons is gone (exec Msg); name and sel are files; stats report real lengths, modes and mtimes; /log streams pane new/del/rename/save events. The tree code lives in src/ninep/ (tree, pane, ctl, addr, pty, events, screen, sources); fs.zig keeps host access, mounts, resolution and find/grep. Same engine and transports. README (fs-help.txt) and docs rewritten; tests updated and extended. Co-Authored-By: Claude Fable 5.1 --- src/9p_io.zig | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'src/9p_io.zig') diff --git a/src/9p_io.zig b/src/9p_io.zig index 943561eb..d0db1c86 100644 --- a/src/9p_io.zig +++ b/src/9p_io.zig @@ -138,7 +138,7 @@ fn localIp(address: std.Io.net.IpAddress) bool { return false; } -const Srv = ninep.Server(pardes.filesystem, ninep.max_fids); +const Srv = ninep.Server(pardes.ctlfs, ninep.max_fids); const Conn = struct { fd: c_int = -1, @@ -149,7 +149,7 @@ const Conn = struct { in: [msize]u8 = undefined, out: [2 * msize]u8 = undefined, - fn step(c: *Conn, core: *pardes.Pardes, req: pardes.filesystem.Req) void { + fn step(c: *Conn, core: *pardes.Pardes, req: pardes.ctlfs.Req) void { core.update(.{ .fs_req = req }); var answered = false; while (core.nextEffect()) |effect| { @@ -160,7 +160,7 @@ const Conn = struct { } else core.perform(effect); } if (!answered) { - const reply = pardes.filesystem.Reply.fail(req.tag, pardes.filesystem.E.IO); + const reply = pardes.ctlfs.Reply.fail(req.tag, pardes.ctlfs.E.IO); c.srv.reply(&reply, ""); } } @@ -226,7 +226,7 @@ pub const Listener = struct { c.srv = .init(.{ .in = &c.in, .out = &c.out, - .root = pardes.filesystem.namespace_root, + .root = pardes.ctlfs.root, }); } } @@ -246,7 +246,7 @@ pub const Listener = struct { c.quic = connection; c.draining = false; c.accepted_ms = nowMs(); - c.srv = .init(.{ .in = &c.in, .out = &c.out, .root = pardes.filesystem.namespace_root }); + c.srv = .init(.{ .in = &c.in, .out = &c.out, .root = pardes.ctlfs.root }); }; } } @@ -748,17 +748,17 @@ test "Unix TCP and QUIC share one listener through reads writes reconnects and r defer testing.allocator.free(before); try testing.expectEqualStrings(w.expected, before); try testing.expectError(error.FileTooLarge, Client.readLimit(testing.allocator, w.dial, w.body_path, w.body_path, w.expected.len - 1)); - const listing = try Client.readLimit(testing.allocator, w.dial, "/self/pane", "/self/pane", 128); + const listing = try Client.readLimit(testing.allocator, w.dial, "/pane", "/pane", 128); defer testing.allocator.free(listing); - const exact_listing = try Client.readLimit(testing.allocator, w.dial, "/self/pane", "/self/pane", listing.len); + const exact_listing = try Client.readLimit(testing.allocator, w.dial, "/pane", "/pane", listing.len); defer testing.allocator.free(exact_listing); try testing.expectEqualStrings(listing, exact_listing); - try testing.expectError(error.FileTooLarge, Client.readLimit(testing.allocator, w.dial, "/self/pane", "/self/pane", listing.len - 1)); + try testing.expectError(error.FileTooLarge, Client.readLimit(testing.allocator, w.dial, "/pane", "/pane", listing.len - 1)); try Client.write(testing.allocator, w.dial, w.body_path, w.replacement); const after = try Client.read(testing.allocator, w.dial, w.body_path, w.body_path); defer testing.allocator.free(after); try testing.expectEqualStrings(w.replacement, after); - const screen = try Client.read(testing.allocator, w.dial, "/self/screen", "/self/screen"); + const screen = try Client.read(testing.allocator, w.dial, "/screen", "/screen"); defer testing.allocator.free(screen); const parsed = try std.json.parseFromSlice(struct { cols: u16, rows: u16 }, testing.allocator, screen, .{ .ignore_unknown_fields = true }); defer parsed.deinit(); @@ -790,7 +790,7 @@ test "Unix TCP and QUIC share one listener through reads writes reconnects and r var dial_buf: [64]u8 = undefined; const network_dial = try std.fmt.bufPrint(&dial_buf, "{s}!{s}!{d}", .{ protocol, host, port }); var body_buf: [64]u8 = undefined; - const body = try std.fmt.bufPrint(&body_buf, "/self/pane/{d}/body", .{pane.serial}); + const body = try std.fmt.bufPrint(&body_buf, "/pane/{d}/body", .{pane.serial}); for ([_][]const u8{ network_dial, l.path(), network_dial }, 0..) |dial, attempt| { var worker: Worker = .{ .dial = dial, .body_path = body, .expected = if (attempt == 0) "initial\n" else replacement, .replacement = replacement }; const thread = try std.Thread.spawn(.{}, Worker.run, .{&worker}); @@ -1245,15 +1245,14 @@ pub const Client = struct { test "a path becomes walk elements, normalised the way a shell would" { var out: [max_depth][]const u8 = undefined; - try testing.expectEqual(@as(usize, 4), try elements("/self/pane/1/body", &out)); - try testing.expectEqualStrings("self", out[0]); - try testing.expectEqualStrings("pane", out[1]); - try testing.expectEqualStrings("1", out[2]); - try testing.expectEqualStrings("body", out[3]); + try testing.expectEqual(@as(usize, 3), try elements("/pane/1/body", &out)); + try testing.expectEqualStrings("pane", out[0]); + try testing.expectEqualStrings("1", out[1]); + try testing.expectEqualStrings("body", out[2]); - try testing.expectEqual(@as(usize, 4), try elements("self/pane/1/body", &out)); - try testing.expectEqual(@as(usize, 4), try elements("//self//pane//1//body//", &out)); - try testing.expectEqual(@as(usize, 2), try elements("/self/index", &out)); + try testing.expectEqual(@as(usize, 3), try elements("pane/1/body", &out)); + try testing.expectEqual(@as(usize, 3), try elements("//pane//1//body//", &out)); + try testing.expectEqual(@as(usize, 1), try elements("/index", &out)); try testing.expectEqual(@as(usize, 0), try elements("/", &out)); @@ -1284,12 +1283,12 @@ pub const Client = struct { test "a dial with nothing listening is one error and not a wait" { if (comptime !supported) return error.SkipZigTest; var names: [max_depth][]const u8 = undefined; - const n = try elements("/self/pane/1/body", &names); + const n = try elements("/pane/1/body", &names); var remote: RemoteError = .{}; const before = nowMs(); try testing.expectError( Error.Dial, - fetchBytes(testing.allocator, .{ .unix = "/tmp/pardes-9p-no-such-socket.sock" }, names[0..n], &remote, null, "/self/pane/1/body", limits.max_file_bytes), + fetchBytes(testing.allocator, .{ .unix = "/tmp/pardes-9p-no-such-socket.sock" }, names[0..n], &remote, null, "/pane/1/body", limits.max_file_bytes), ); try testing.expect(nowMs() - before < budget_ms); } -- cgit v1.3