diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 00:50:45 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 7aa057e29b729ad6d9ac53e3a06f99858fc449e4 (patch) | |
| tree | a0010efb342018f32f98d9e805680306feb88836 /src/ninep/tree.zig | |
| parent | e563be42a5b3e9243e2c01d34cf8e94d2047b753 (diff) | |
| download | pardes-7aa057e29b729ad6d9ac53e3a06f99858fc449e4.tar.gz pardes-7aa057e29b729ad6d9ac53e3a06f99858fc449e4.zip | |
A column has a ctl, an exec and an rmdir over 9P, and Restore maps its serial
A script could read and write a column's tag but not act on the column.
col/<serial>/ctl takes Delcol, Joincol, New and Tty, each acting on that
column as from its tag; col/<serial>/exec runs a word as a middle click in
the tag; rmdir col/<serial> closes an empty column and refuses one with
panes (ENOTEMPTY, an err record saying why). The root ctl's refusal of
Delcol and Joincol points at col/<serial>/ctl. A dump keeps each column's
serial and Restore logs restoredcol <old> <new>; the dump golden gains
them. Documented: Joincol's survivor, the column on the right, keeps its
serial and tag.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/tree.zig')
| -rw-r--r-- | src/ninep/tree.zig | 38 |
1 files changed, 30 insertions, 8 deletions
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index c4ade1f7..baf8c772 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -272,7 +272,16 @@ pub const PaneFile = enum(u5) { /// /col/<serial> and its tag: a column is named by its serial, which stays /// while it lives as a pane's does; this bit, past a pane serial's 32, keeps /// the two apart. -pub const ColFile = enum(u5) { dir = 0, tag = 1 }; +pub const ColFile = enum(u5) { + dir = 0, + tag = 1, + ctl = 2, + exec = 3, + + pub fn fileName(f: ColFile) []const u8 { + return @tagName(f); + } +}; const col_bit: u59 = 1 << 58; pub const Node = packed struct(u64) { @@ -454,10 +463,14 @@ fn attrOf(p: *Pardes, target: Target) ?Reply.Attr { .col => |c| { if (layout.columnBySerial(p, c.serial) == null) return null; return .{ - .name = if (c.file == .dir) (std.fmt.bufPrint(&p.fs.node_name, "{d}", .{c.serial}) catch unreachable) else "tag", + .name = if (c.file == .dir) (std.fmt.bufPrint(&p.fs.node_name, "{d}", .{c.serial}) catch unreachable) else c.file.fileName(), .node = Node.ofCol(c.serial, c.file), .dir = c.file == .dir, - .mode = if (c.file == .dir) 0o755 else 0o666, + .mode = switch (c.file) { + .dir => 0o755, + .ctl, .exec => 0o222, + .tag => 0o666, + }, .size = if (c.file == .tag) cols.headerText(p, c.serial).?.len + 1 else 0, .mtime = p.fs.started, .append = c.file == .tag, @@ -562,8 +575,9 @@ fn lookup(p: *Pardes, req: Req, target: Target) Reply { }, .col => |c| col: { if (c.file != .dir) return Reply.fail(req.tag, E.NOTDIR); - if (!std.mem.eql(u8, name, "tag")) return Reply.fail(req.tag, E.NOENT); - break :col Node.ofCol(c.serial, .tag); + const f = std.meta.stringToEnum(ColFile, name) orelse return Reply.fail(req.tag, E.NOENT); + if (f == .dir) return Reply.fail(req.tag, E.NOENT); + break :col Node.ofCol(c.serial, f); }, .pane => |t| pane: { _ = p.paneBySerial(t.serial) orelse return Reply.fail(req.tag, E.NOENT); @@ -638,7 +652,9 @@ fn readdir(p: *Pardes, req: Req, target: Target) Reply { .col => |c| { if (layout.columnBySerial(p, c.serial) == null) return Reply.fail(req.tag, E.NOENT); if (c.file != .dir) return Reply.fail(req.tag, E.NOTDIR); - if (skip > 0) skip -= 1 else stageDirent(out, p.gpa, Node.ofCol(c.serial, .tag), false, "tag"); + inline for (.{ ColFile.tag, ColFile.ctl, ColFile.exec }) |f| { + if (skip > 0) skip -= 1 else stageDirent(out, p.gpa, Node.ofCol(c.serial, f), false, f.fileName()); + } }, .pane => |t| { const id = p.paneBySerial(t.serial) orelse return Reply.fail(req.tag, E.NOENT); @@ -729,7 +745,8 @@ fn remove(p: *Pardes, req: Req) Reply { const target = Node.target(req.node) orelse return Reply.fail(req.tag, E.NOENT); const t = switch (target) { .pane => |t| t, - .top, .col => return Reply.fail(req.tag, E.PERM), + .top => return Reply.fail(req.tag, E.PERM), + .col => |c| return cols.remove(p, req, c.serial, c.file), }; if (t.file != .dir) return Reply.fail(req.tag, E.PERM); const id = p.paneBySerial(t.serial) orelse return Reply.fail(req.tag, E.NOENT); @@ -902,7 +919,12 @@ fn write(p: *Pardes, req: Req, target: Target) Reply { if (t.file.inPty() and !pn.isTerminal()) return Reply.fail(req.tag, E.NOENT); return pane.write(p, req, id, pn, t.file); }, - .col => |c| return if (c.file == .tag) cols.write(p, req, c.serial) else Reply.fail(req.tag, E.PERM), + .col => |c| return switch (c.file) { + .tag => cols.write(p, req, c.serial), + .ctl => cols.writeCtl(p, req, c.serial), + .exec => cols.writeExec(p, req, c.serial), + .dir => Reply.fail(req.tag, E.PERM), + }, } } |
