diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 20:50:48 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 22:07:48 -0300 |
| commit | 406f483288c7689bdd2e1f3f68b276c61ca9412a (patch) | |
| tree | b6398fb9974b91441563fd655d18ddaf182a2a5a /src/fs.zig | |
| parent | b55cb01d2777760ed79b545dfca7115837591577 (diff) | |
| download | pardes-406f483288c7689bdd2e1f3f68b276c61ca9412a.tar.gz pardes-406f483288c7689bdd2e1f3f68b276c61ca9412a.zip | |
An Edit's held write: < | > commands no longer block their 9P connection (answered like a held read, so a status read or a filter reading the session's own mount runs beside it), a Tflush or hang-up of the write kills the commands' process groups and changes nothing; e loads by Get's way (asked once, clean after, undo puts the name back); ~ in e r w f B; B checks every name first; an Edit that runs commands is a write of its own, refused up front with other lines; Edit's +Errors output keeps the keyboard where it was; X goes in pane order; a refused write open says its errno; the reference's Coming from acme rows say what Get file's undo, failure and directory refusal, Putall's answer, Zerox across Dump and Incl's directories now do
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/fs.zig')
| -rw-r--r-- | src/fs.zig | 65 |
1 files changed, 62 insertions, 3 deletions
@@ -85,6 +85,14 @@ pub const WriteError = error{ NoSpaceLeft, OpenFailed, WriteFailed, + // The rest are open(2)'s errno, named so errorWords says them. + NoSuchFileOrDirectory, + NotDir, + InvalidArgument, + FileBusy, + SymLinkLoop, + NoSuchDeviceOrAddress, + FileTooBig, }; /// The host takes the bytes. The caller has given the turn up (`write`), @@ -102,6 +110,13 @@ pub fn writeFile(path: []const u8, bytes: []const u8) WriteError!void { .ROFS => error.ReadOnlyFilesystem, .NOSPC, .DQUOT => error.NoSpaceLeft, .NAMETOOLONG => error.PathTooLong, + .NOENT => error.NoSuchFileOrDirectory, + .NOTDIR => error.NotDir, + .INVAL => error.InvalidArgument, + .BUSY, .TXTBSY => error.FileBusy, + .LOOP => error.SymLinkLoop, + .NXIO, .NODEV => error.NoSuchDeviceOrAddress, + .FBIG => error.FileTooBig, else => error.OpenFailed, }; var off: usize = 0; @@ -1554,6 +1569,36 @@ fn readFileLimit(gpa: std.mem.Allocator, path: []const u8, limit: usize) ![]u8 { return buf; } +/// A 9P write of an Edit held while its commands run (Namespace.edit_hold). +pub const EditHold = struct { + /// The listener's: the connection that asked, and its park. + asker: *anyopaque, + slot: u16, + seq: u64, + tag: u64, + node: u64, + handle: u32, + written: u32, + /// The Edit is done: applied, or failed with `said`. + done: bool = false, + errno: u16 = 0, + said: [256]u8 = undefined, + said_len: u16 = 0, + + pub fn names(h: *const EditHold, req: tree.Req) bool { + return req.op == .write and req.tag == h.tag and req.node == h.node and req.handle == h.handle; + } + + /// Its answer, the held record let go: the words copied out first. + pub fn answer(ns: *Namespace) tree.Reply { + const h = ns.edit_hold.?; + ns.edit_hold = null; + if (h.errno == 0) return .{ .tag = h.tag, .written = h.written }; + @memcpy(ns.edit_said[0..h.said_len], h.said[0..h.said_len]); + return tree.failText(h.tag, h.errno, ns.edit_said[0..h.said_len]); + } +}; + pub const Namespace = struct { socket_path: []const u8 = "", tcp_address: ?std.Io.net.IpAddress = null, @@ -1613,9 +1658,15 @@ pub const Namespace = struct { /// A 9P write asked a language server something: the answer count it /// waits to change (`turn.lsp_answers`), so its failure is the write's. lsp_answer_at: ?u64 = null, - /// A write ran an Edit whose `<`, `|` or `>` commands are running: it is - /// answered once `turn.edit_answers` passes this, the Edit done. - edit_answer_at: ?u64 = null, + /// The request being served started an Edit whose `<`, `|` or `>` + /// commands run off the loop (edit_cmd.zig): the listener holds it. + edit_started: bool = false, + /// That write, held: answered once `done` (9p_io answerHeld), or + /// asked again by a retry of its park (Pardes.serveFs); a flush or a + /// hang-up stops the Edit (9p_io watchEdit). + edit_hold: ?EditHold = null, + /// The words a held Edit write was refused with, kept for its answer. + edit_said: [256]u8 = undefined, /// The pane a language answer filled (a Rename's preview): what the /// waiting 9P write answers once it is in (9p_io). lsp_result: ?u32 = null, @@ -1757,3 +1808,11 @@ test { test "the tree's README lists src/ only when the build serves it" { try std.testing.expectEqual(limits.embedded_sources, std.mem.indexOf(u8, help, "src/") != null); } + +test "a write open(2) refuses says its errno in words" { + if (comptime @import("builtin").os.tag != .linux or !pardes.hosted) return error.SkipZigTest; + // /proc takes no new file: ENOENT, which used to read "open failed". + try std.testing.expectError(error.NoSuchFileOrDirectory, writeFile("/proc/pardes-no-such-file", "x")); + var words: [128]u8 = undefined; + try std.testing.expectEqualStrings("no such file or directory", pardes.Messages.errorWords(error.NoSuchFileOrDirectory, &words)); +} |
