From 13c5f281ae0264e42e04bce527b83159fa75466c Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 10:10:50 -0300 Subject: /pager takes one directory an open, as pty/run takes one line: a second line, in the same write or the next, is refused EINVAL Through a mount `printf 'a\nb\n' > pager` arrives a line a write, so the second asked for another +Pager on the same open, its answer replacing the first. An open that asked already refuses another. Co-Authored-By: Claude Opus 5.5 --- src/ninep/ctl.zig | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 554333bb..4fb5bf41 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -656,8 +656,11 @@ pub fn writePager(p: *Pardes, req: Req) Reply { p.fs.results_len = 0; var it = Messages.init(p, req.data); const line = it.next() orelse ""; - // Words 9ns maps back to EINVAL (`invalid`), as every refusal here. - if (it.next() != null) return tree.failText(req.tag, E.INVAL, "pager: invalid write: one directory a write"); + // Words 9ns maps back to EINVAL (`invalid`), as every refusal here. One + // directory an open, as pty/run takes one line: through a mount + // `printf 'a\nb\n'` comes a line a write, and the second is refused. + const asked_before = if (tree.openOf(p, req)) |o| o.results.wrote else false; + if (it.next() != null or asked_before) return tree.failText(req.tag, E.INVAL, "pager: invalid write: one directory an open"); // `~` is home, and `/tmp/../etc` is `/etc`: the +Pager is named in the // directory it is, as any other pane's name is. var home_buf: [4096]u8 = undefined; @@ -4071,12 +4074,19 @@ test "pager answers its +Pager on the open that asked, whatever another client e try testing.expectEqual(E.INVAL, call(p, .{ .tag = 6, .op = .write, .node = pager, .handle = asked, .data = "relative\n" }).errno()); try testing.expectEqualStrings("", call(p, .{ .tag = 7, .op = .read, .node = pager, .handle = asked, .size = 64 }).bytes); // Resolved: `/tmp/../tmp/.` is /tmp's +Pager, the one made above. - try testing.expectEqual(Status.ok, call(p, .{ .tag = 8, .op = .write, .node = pager, .handle = asked, .data = "/tmp/../tmp/.\n" }).reply.status); - try testing.expectEqualStrings(want, call(p, .{ .tag = 9, .op = .read, .node = pager, .handle = asked, .size = 64 }).bytes); + const again = call(p, .{ .tag = 8, .op = .open, .node = pager, .omode = 2 }).reply.handle; + try testing.expectEqual(Status.ok, call(p, .{ .tag = 8, .op = .write, .node = pager, .handle = again, .data = "/tmp/../tmp/.\n" }).reply.status); + try testing.expectEqualStrings(want, call(p, .{ .tag = 9, .op = .read, .node = pager, .handle = again, .size = 64 }).bytes); + _ = call(p, .{ .tag = 9, .op = .release, .node = pager, .handle = again }); // One that may not be written is refused, permission denied. try testing.expectEqual(E.PERM, wr(p, pager, "/proc/1\n").errno()); // One directory a write, absolute, and there. try testing.expectEqual(E.INVAL, wr(p, pager, "/a\n/b\n").errno()); + // A second line on an open that asked already, as a mount sends two. + const twice = call(p, .{ .tag = 20, .op = .open, .node = pager, .omode = 2 }).reply.handle; + try testing.expectEqual(Status.ok, call(p, .{ .tag = 21, .op = .write, .node = pager, .handle = twice, .data = "/tmp\n" }).reply.status); + try testing.expectEqual(E.INVAL, call(p, .{ .tag = 22, .op = .write, .node = pager, .handle = twice, .data = "/tmp\n" }).errno()); + _ = call(p, .{ .tag = 23, .op = .release, .node = pager, .handle = twice }); const relative = wr(p, pager, "relative\n"); try testing.expectEqual(E.INVAL, relative.errno()); try testing.expectEqualStrings("pager: invalid directory \"relative\": must be absolute", relative.reply.ename); -- cgit v1.3