diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-20 00:58:22 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-20 00:58:22 -0300 |
| commit | 65209217b5b68f56bc0bd5bc6c4dce33911ded59 (patch) | |
| tree | be3848ef46b7a2ce1d0ad1a768b7a97d428481c4 /docs/design.md | |
| parent | 3e9f8805f293f622bb885cf849b5ce47dc062ad1 (diff) | |
| download | cloud9-65209217b5b68f56bc0bd5bc6c4dce33911ded59.tar.gz cloud9-65209217b5b68f56bc0bd5bc6c4dce33911ded59.zip | |
Add the file-server engine: cloud9.fs.Server(Backend, Options)
The asynchronous 9P file-server engine from the Pardes editor moves into the
library: fid table, walks, directory cursors, a job/slot model where backend
replies arrive later by tag (status again = parked), Tflush cancellation and
orphaned fids on hangup. Allocation-free, no OS calls, no std.Io; comptime
Options (fid, slot, park data, name and user capacities) replace the
editor's constants. Backend contract types (Req, Reply/ReplyWith, Op,
Status, Attr, E, error strings) live here. 29 engine tests plus the client
tests that sat beside it in Pardes.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'docs/design.md')
| -rw-r--r-- | docs/design.md | 55 |
1 files changed, 51 insertions, 4 deletions
diff --git a/docs/design.md b/docs/design.md index 2013b3e..dda10a6 100644 --- a/docs/design.md +++ b/docs/design.md @@ -66,6 +66,52 @@ identity verification must provide that policy before using it across a trust boundary. OpenSSL allocations and handshake costs are outside the allocation-free protocol core. Accepted connections must close before their shared listener. +# File server engine + +`fs.Server(Backend, Options)` is a file server built on `Server`: it owns the +fid table, permission checks, the directory-read cursor, flush and hangup, and +asks a backend only for filesystem operations. The backend contract is +`fs.Req` (`Op`: lookup, getattr, setattr, open, read, write, release, readdir; +each names a node, and open/read/write/readdir/release carry the handle open +returned) answered by `fs.Reply` (`Status`, an errno from `fs.E`, `fs.Attr`, +the open handle, the written count) with a read's or readdir's bytes passed +beside the reply. `fs.ReplyWith(Payload)` is the same reply carrying an +application-defined locator for those bytes, which the engine ignores; a +backend declares `Req` and `Reply` as those types. A readdir answers records +of `node:u64le dir:u8 len:u8 name`, which the engine encodes into whole Stat +entries directly in the output frame. + +Every 9P request is a job: attach, walk, open, read, readdir, write, clunk, +remove, stat and wstat become one or more backend requests issued in order +(a walk asks one lookup per element; an open with OTRUNC asks setattr then +open; a clunk of an open fid asks release). `next()` yields the next backend +request and null while one is outstanding, the output is full, or no whole +frame has arrived; the backend answers by the request's tag through +`reply()`, at once or later. Tags are unique per connection and never zero. +A stale tag (flushed, hung up) is ignored; the backend must retire canceled +work before answering. + +A read, readdir or write may answer `Status.again`: the job parks in a slot, +the engine moves on, and `retry()` re-issues each parked request once per +round, oldest first, until it completes; a parked write keeps a copy of its +data up to `park_data_max` bytes. Any other operation answering `again`, or +a park with no free slot, fails with EAGAIN. Tflush answers a parked +request's tag with EINTR before its Rflush and drops the slot; a flush of an +unknown tag is just Rflush. `hangup()` marks every open fid orphaned and +`next()` then yields the release each one owes before anything else, so a +dropped connection still pays the backend; a Tversion does the same before +negotiating. + +Every table is sized at comptime by `fs.Options`: `fid_capacity` (256), +`slot_capacity` (32), `park_data_max` (128), `name_capacity` (28, at most +255) and `username_capacity` (28); the defaults are the editor's, except the +name capacity, which is the board's. `msize_min` (217) is the smallest +negotiable msize, one full Rwalk. Create, remove, auth, attaching a named +tree and any wstat other than a zero-length truncation are refused. Rerror +strings are the ones Linux v9fs maps back to errnos (`fs.errString`). The +engine allocates nothing and makes no OS calls; a session is `push()`, +`retry()`, `next()`, `reply()`, `output()`, `wrote()`. + # Related programs Programs built on the library ship from this repository as `cloud9/<name>/`, @@ -83,12 +129,13 @@ programs follow this layout. # Pardes integration -Pardes consumes the sibling package through build.zig.zon. Its `src/9p.zig` now -adapts filesystem requests and retains editor/GPIO capacities and permissions. +Pardes consumes the sibling package through build.zig.zon. Its control tree +(`src/ninep/`) is an `fs.Server` backend using the contract types above; its +`src/9p.zig` names the editor's and the board's `fs.Options`. `src/9p_io.zig` retains mounting, discovery, the editor's event-loop scheduling, connection limits, and error presentation. Protocol bytes, session validation, -and TCP/Unix/QUIC transport implementation come from cloud9. Pardes selects the -existing `pardes-9p` ALPN for compatibility. +the file-server engine, and TCP/Unix/QUIC transport implementation come from +cloud9. Pardes selects the existing `pardes-9p` ALPN for compatibility. The separate `05-zig-p4` firmware build adds cloud9 to the GPIO application's module map. UART hardware access remains in the board firmware. No hardware was |
