From 917e9a099f3a5048ac436aafcfabbf3d40d1c24f Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 13:26:10 -0300 Subject: docs: through a mount a malformed write is EINVAL and one that fails is EIO, and the error words keep it so The served README said every refused write reads as EINVAL, but a mount's errno is 9ns's reading of the error's words, so a no-match or out-of-range address and a refused Exit came back EIO, as the dogfood run saw. That split is the useful one, malformed against failed, so the docs now state it rather than the words being bent to EINVAL; a test pins each error's words to its side of it. It caught one: 'regular expression search took too long' read as ENAMETOOLONG, and now says it gave up past its step budget. Co-Authored-By: Claude Opus 5.5 --- src/fs-help.txt | 2 +- src/ninep/addr.zig | 4 +++- src/ninep/ctl.zig | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/fs-help.txt b/src/fs-help.txt index 689d8839..4633d91c 100644 --- a/src/fs-help.txt +++ b/src/fs-help.txt @@ -40,6 +40,6 @@ Pitfalls, one each: tag reads the path, then the tag's own text; writes append to that text; truncating clears it. A terminal's body is a history snapshot frozen per open; pty/data is the live stream. exec: a word no builtin knows is typed into a terminal in the pane's directory, and not an error. - A refused write reads as EINVAL through a mount: the log's err record says why. + Through a mount a malformed write is EINVAL, a well-formed one that fails EIO: log's err says why. lock/unlock on a pane's ctl need a held fd (exec 3>ctl); a lock someone holds fails: retry. Closing a column's last pane leaves a +New in its place; Delcol closes the column. diff --git a/src/ninep/addr.zig b/src/ninep/addr.zig index 6656c786..3b29f622 100644 --- a/src/ninep/addr.zig +++ b/src/ninep/addr.zig @@ -14,7 +14,9 @@ fn clip(n: usize) u32 { pub const e_no_match = "no match for regexp"; pub const e_range = "address out of range"; pub const e_regexp = "bad regular expression"; -pub const e_slow = "regular expression search took too long"; +/// Not "took too long": 9ns reads errors by their words, and that would be +/// ENAMETOOLONG. +pub const e_slow = "regular expression search gave up, backtracking past its step budget"; pub const e_syntax = "bad address syntax"; pub const e_order = "addresses out of order"; diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 382cc0e7..386fd9a9 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -926,3 +926,28 @@ test "look and exec read back what the last command touched, a ctl write's too" try testing.expectEqual(tree.Status.ok, wr(p, @intFromEnum(tree.TopFile.ctl), "Verbose off\n").reply.status); try testing.expectEqualStrings(try std.fmt.bufPrint(&want, "{d}\n", .{made}), rd(p, root_exec, 0, 64).bytes); } + +test "an error's words give the errno a mount reads: EINVAL for what is malformed, EIO for what failed" { + // 9ns (cloud9's 9ns/src/nine.zig) maps an error to an errno by its + // words, and EIO when none of them match: these words must not. + const addressing = @import("addr.zig"); + const einval = [_][]const u8{ "control message", "invalid", "bad " }; + const other = [_][]const u8{ "interrupt", "shut down", "not exist", "not found", "no such", "exists", "not empty", "not a dir", "is a dir", "permission", "denied", "read-only", "read only", "no space", "not allowed", "not permitted", "cannot", "fid", "busy", "in use", "too long", "not supported", "unsupported" }; + const Case = struct { text: []const u8, malformed: bool }; + for ([_]Case{ + .{ .text = addressing.e_syntax, .malformed = true }, + .{ .text = addressing.e_regexp, .malformed = true }, + .{ .text = e_prompt, .malformed = true }, + .{ .text = addressing.e_no_match, .malformed = false }, + .{ .text = addressing.e_range, .malformed = false }, + .{ .text = addressing.e_order, .malformed = false }, + .{ .text = addressing.e_slow, .malformed = false }, + .{ .text = "/tmp/x.txt: Modified (Exit again to discard)", .malformed = false }, + }) |c| { + const says_einval = for (einval) |w| { + if (std.ascii.findIgnoreCase(c.text, w) != null) break true; + } else false; + try testing.expectEqual(c.malformed, says_einval); + for (other) |w| try testing.expect(std.ascii.findIgnoreCase(c.text, w) == null); + } +} -- cgit v1.3