summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 06:28:40 -0300
committerGabriel Schneider <[email protected]>2026-10-01 07:16:43 -0300
commitc9e5c77eb9a20d7012ee972727f250f3a2fb95cc (patch)
tree5db45459bfaf825e969ef949fe7916725dd91890 /src/ninep
parenta88139a012939be9394014f483f6920a6e6d3a53 (diff)
downloadpardes-c9e5c77eb9a20d7012ee972727f250f3a2fb95cc.tar.gz
pardes-c9e5c77eb9a20d7012ee972727f250f3a2fb95cc.zip
/pager resolves its directory, `~` and `..` alike, and refuses one that may not be written, permission denied; `pardes -` from such a directory pages into the session's
`/tmp/../etc` named a +Pager `/tmp/../etc/+Pager`, a second one beside `/etc/+Pager`, and `~/x` was refused as relative. The directory is now home-expanded and resolved before it is checked, and one that may not be written is refused as a file's name there is (fs.deniedAbove). `pardes -` run in such a directory (`git log` under /usr/src) asks for the session's +Pager instead, so its text is still paged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig14
1 files changed, 13 insertions, 1 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 89d438df..fa6310bb 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -654,7 +654,11 @@ pub fn writePager(p: *Pardes, req: Req) Reply {
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");
- const dir = std.mem.trim(u8, line, " \t");
+ // `~` 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;
+ const typed = pardes.filesystem.expandHome(std.mem.trim(u8, line, " \t"), &home_buf);
+ const dir = if (typed.len > 0 and typed[0] == '/') std.fs.path.resolvePosix(p.scratch.allocator(), &.{typed}) catch typed else typed;
// A directory that is there, or none (the session's): never a +Pager
// named into one missing, nor a relative one quietly taken for none.
if (dir.len > 0 and dir[0] != '/') {
@@ -663,6 +667,9 @@ pub fn writePager(p: *Pardes, req: Req) Reply {
}
if (comptime pardes.hosted) if (dir.len > 0) if (pardes.filesystem.localPath(dir)) |local| if (!exec_line.isDirectory(local))
return tree.failText(req.tag, E.NOENT, std.fmt.bufPrint(&p.fs.ename, "pager: {s}: no such directory", .{dir[0..@min(dir.len, 256)]}) catch "pager: no such directory");
+ // Nor one that may not be written, as a file's name there is refused.
+ if (comptime pardes.hosted) if (dir.len > 0) if (pardes.filesystem.localPath(dir)) |local| if (pardes.filesystem.deniedAbove(local))
+ return tree.failText(req.tag, E.PERM, std.fmt.bufPrint(&p.fs.ename, "pager: {s}: permission denied", .{dir[0..@min(dir.len, 256)]}) catch "pager: permission denied");
const id = pardes.panes.Output.openPager(p, p.active, dir) catch |err|
return tree.failText(req.tag, if (err == error.NoPaneRoom or err == error.NoPaneSlots) E.NOSPC else E.IO, switch (err) {
error.NoPaneRoom, error.NoPaneSlots => "pager: no space for a +Pager pane",
@@ -4049,6 +4056,11 @@ test "pager answers its +Pager on the open that asked, whatever another client e
// Refused, the same open answers nothing, not its last answer.
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);
+ // 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());
const relative = wr(p, pager, "relative\n");