summaryrefslogtreecommitdiff
path: root/build.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-27 15:32:02 -0300
committerGabriel Schneider <[email protected]>2026-08-27 16:42:08 -0300
commitdef843b2f59b867ee9b1d501f559f59fb335d4cc (patch)
treed7c1650c045653ebc93a77d7a90985e5e31725c2 /build.zig
parentf5927a033f0c83753b5cc004e514568eec8c24f8 (diff)
downloadpardes-def843b2f59b867ee9b1d501f559f59fb335d4cc.tar.gz
pardes-def843b2f59b867ee9b1d501f559f59fb335d4cc.zip
9p: serve the acme tree over 9P2000 on a unix socket, beside the mount
Step 4 of the 9P chain (docs/9p.typ 12.4, docs/registry.typ 9P-15/16/17/4/5). src/9p.zig is a base 9P2000 codec and a SANS-IO server: it never touches a descriptor, takes no allocator, starts no thread, and builds for wasm32-freestanding and riscv32-freestanding. That is what lets the same code serve a unix socket here and a UART on the board later. Server(comptime fs: type) duck-typed on fs.Req/fs.Reply/fs.Reply.Attr, so it never imports acmefs and acmefs never learns 9P init{ in, out, root } the caller owns the buffers; msize is derived retry/next/reply the three fs_service.Transport ops, by name push/output/wrote/hangup bytes in, bytes out, partial writes supported next() is a PUMP, not one-message-one-request: a 3-element Twalk is three lookups, Topen|OTRUNC is a setattr then an open, Tversion is none at all. Decisions that were open and are now taken, each recorded in the file: * qid.version is ALWAYS 0, which makes Linux set P9L_DIRECT and skip its cache -- the 9P equivalent of the FOPEN_DIRECT_IO fuse.zig relies on. * Every Rread is clamped to the client's count. An over-long one is a hard -EIO in Linux, not a truncation. * Rerror carries Linux's exact strerror text (registry 9P-4 option A), so a mount recovers the errno instead of ESERVERFAULT. Asserted as literals, because a typo there is 'Unknown error 526' on every mount. * `.` and `..` are resolved BY THE SERVER. Under FUSE the kernel does it and acmefs says so; 9P has no kernel, and forwarding `..` as a lookup would break every client that normalises a path. * Topen checks the perm bits itself. Under FUSE the kernel enforced them; over 9P nobody is above the server, and `errors` would have been readable. * Tcreate and Tremove are Rerror: `new/` creates a pane on WALK, so the capability exists and is not spelled Tcreate. THE INTEGRATION BUG, which was not in the protocol: the daemon's push_fs_reply sent every reply to the FUSE mount, whose park table has no 9P tag, so it dropped it -- Tversion worked (no core involved) and Tattach hung forever. That is exactly the 'no routing origin for the 9P descriptor' cell in the layering table of docs/9p.typ. Session.fs_origin now carries the transport that asked. Proved with plan9port against a live daemon serving BOTH transports at once: 9p ls / and /1, read index/ctl/tag, write /1/body, stat, a walk through /1/../index, pane creation through `new/body`, and the two refusals arriving as strings -- 'permission denied' and 'No such file or directory' -- confirmed on the raw wire as Rerror text rather than numbers. A write over 9P reads back through FUSE and a write through FUSE reads back over 9P. msize 8192, 34,072 bytes per connection (Server 9,488 + in 8,192 + out 16,384, out being two msize so that every reply is infallible), four connections. zig build unit-test: 468 tests before, 503 after.
Diffstat (limited to 'build.zig')
-rw-r--r--build.zig16
1 files changed, 16 insertions, 0 deletions
diff --git a/build.zig b/build.zig
index c67caf20..f8dc3e82 100644
--- a/build.zig
+++ b/build.zig
@@ -1739,6 +1739,22 @@ pub fn build(b: *std.Build) void {
.link_libc = true,
}) });
unit_step.dependOn(&b.addRunArtifact(nested_test).step);
+ // The 9P2000 codec and its sans-io server. Its own module for the
+ // reason spelled out above rather than as a convention, and a stronger
+ // one than its neighbours have: 9p.zig is FREESTANDING — it imports
+ // `std` and nothing else, so that the same source compiles for
+ // wasm32- and riscv32-freestanding — and its only host-side importer
+ // is src/fs9_service.zig, which reaches pardes.zig. Compiling it here
+ // as its own root is therefore also the check that the freestanding
+ // promise still holds: NO `link_libc`, and an import of anything
+ // OS-shaped would fail this step rather than passing quietly inside
+ // the core's graph.
+ const ninep_test = b.addTest(.{ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .root_source_file = b.path("src/9p.zig"),
+ }) });
+ unit_step.dependOn(&b.addRunArtifact(ninep_test).step);
// The board's input-rescue policy: drain the receiver while spinning on a full transmitter.
// A measured bug — a 200-byte burst typed into a long frame lost 88 bytes on the die — so
// it gets a test that fails without the fix, and it runs HERE rather than only on hardware.