summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 08:15:36 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit4a7d377d160cd95c89acad2abaf08bc533ff02a8 (patch)
treed3b0fb33e1a21010ce08c41cea4870a21b146154 /src/ninep
parenta630f5e83ef9efabca790770112dcea689384631 (diff)
downloadpardes-4a7d377d160cd95c89acad2abaf08bc533ff02a8.tar.gz
pardes-4a7d377d160cd95c89acad2abaf08bc533ff02a8.zip
A builtin that needs its argument, written bare to an exec, is EINVAL as through ctl
Through a ctl the bare word was refused up front (EINVAL); through exec it ran, failed with EIO or asked for the argument. The exec now refuses it the same way before it runs. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig20
1 files changed, 20 insertions, 0 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 76cba9d5..1ef9a2f6 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -220,6 +220,16 @@ pub const e_control = "invalid command line: it holds a control character other
/// the write, with its words and an err record, and no msg, as a ctl line
/// does (runBuiltin). The refusal, or null.
pub fn captured(p: *Pardes, req: Req, id: usize, exec: bool, text: []const u8, in_tag: bool) ?Reply {
+ // A builtin that means nothing without its argument, written bare: the
+ // write is refused, EINVAL, as a ctl's is (checkBuiltin), not run to
+ // fail or to ask.
+ if (exec) {
+ const cmd = exec_line.commandText(text);
+ if (std.meta.stringToEnum(Builtin, cmd)) |b| if (builtins.registry.requiresArg(b)) {
+ p.fs.results_len = 0;
+ return refuse(p, req, "wrong #args in control message", cmd);
+ };
+ }
p.fs.capturing = true;
p.fs.failure_len = 0;
defer p.fs.capturing = false;
@@ -1799,6 +1809,16 @@ test "Tty in a directory that is not there fails and makes no pane; a shell that
try testing.expectEqualStrings("error shell gone\n", call(p, .{ .tag = 3, .op = .read, .node = run_node, .handle = h, .size = 64 }).bytes);
}
+test "a builtin that needs its argument, written bare to an exec, is EINVAL as through ctl" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ for ([_]u64{ root_exec, Node.of(serialOf(p), .exec), @intFromEnum(tree.TopFile.tagexec) }) |node| {
+ const refused = wr(p, node, "Mount\n");
+ try testing.expectEqual(E.INVAL, refused.errno());
+ try testing.expectEqualStrings("wrong #args in control message \"Mount\"", refused.reply.ename);
+ }
+}
+
test "size is monotonic: growing is never refused, and a size once taken is taken again" {
const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 60 });
defer p.deinit();