From 72c84bd7206bba5e2323c2e7f12f2458405e800d Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 08:05:41 -0300 Subject: The root exec's refusals before a line runs name the pane, and say invalid, so a mount reads EINVAL A line over 1024 bytes or with a control character was refused with its errno EINVAL but words 9ns reads as EIO (no `invalid` in them), and logged `err - exec`. They now say `invalid command line: ...`, and the root's err record names the pane the line would have run at. Co-Authored-By: Claude Opus 5.5 --- src/ninep/ctl.zig | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 58fcdf3a..0b1ec194 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -170,7 +170,9 @@ pub fn completeEnd(p: *Pardes, data: []const u8) usize { /// A null `serial` clicks at whichever pane is active as each line runs, /// which is what the root's own look and exec mean; a serial stays put. pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Reply { - p.fs.ran_at = 0; + // The root's lines run at the keyboard's pane: a refusal before any runs + // is that pane's too, in its err record. + p.fs.ran_at = if (serial == null) (if (p.panes[p.active]) |pane| pane.serial else 0) else 0; for ([2]bool{ false, true }) |apply| { if (apply) p.fs.results_len = 0; var it = Messages.init(p, req.data); @@ -196,7 +198,9 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Rep return .{ .tag = req.tag, .written = @intCast(req.data.len) }; } -pub const e_too_long = std.fmt.comptimePrint("a command line is at most {d} bytes", .{pardes.exec.command_max}); +// "invalid" is the word 9ns reads as EINVAL (enameToErrno): through a +// mount the refusal is EINVAL, as the reply's errno is. +pub const e_too_long = std.fmt.comptimePrint("invalid command line: a command line is at most {d} bytes", .{pardes.exec.command_max}); /// A line an exec would run as a command, over the most a command line may /// be: refused whole, before anything runs, rather than said on the message @@ -210,7 +214,7 @@ pub fn tooLong(req: Req, line: []const u8) ?Reply { return tree.failText(req.tag, E.INVAL, e_too_long); } -pub const e_control = "a command line holds no control character but a tab"; +pub const e_control = "invalid command line: it holds a control character other than a tab"; /// Runs one click (`run`) as a 9P write's: a builtin that fails there fails /// the write, with its words and an err record, and no msg, as a ctl line @@ -1682,7 +1686,10 @@ test "an exec line over the command limit fails the write and says the limit; a const refused = wr(p, root_exec, long); try testing.expectEqual(E.INVAL, refused.errno()); try testing.expectEqualStrings(e_too_long, refused.reply.ename); - try testing.expect(th.logHas(p, "a command line is at most 1024 bytes")); + var rec: [96]u8 = undefined; + try testing.expect(th.logHas(p, try std.fmt.bufPrint(&rec, "err {d} exec: invalid command line: a command line is at most 1024 bytes", .{serialOf(p)}))); + try testing.expectEqualStrings(e_control, wr(p, root_exec, "echo a\x01b\n").reply.ename); + try testing.expect(th.logHas(p, try std.fmt.bufPrint(&rec, "err {d} exec: invalid command line: it holds", .{serialOf(p)}))); try testing.expectEqual(Status.ok, wr(p, root_exec, "Msg " ++ "z" ** 1100 ++ "\n").reply.status); } -- cgit v1.3