diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 08:20:20 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:16 -0300 |
| commit | 8157972cff633ee0135c81fd3f1195d9a73eb92f (patch) | |
| tree | 479096be1049ba53ce92918f39e5a557cae02c0a | |
| parent | a0114d0b8be05d62bc1eb0e68642f7c31a766ade (diff) | |
| download | pardes-8157972cff633ee0135c81fd3f1195d9a73eb92f.tar.gz pardes-8157972cff633ee0135c81fd3f1195d9a73eb92f.zip | |
A builtin failure over something not there answers ENOENT: Unmount of a name not mounted
`Unmount nothere` failed EIO with `not mounted`. It says `no such mount
nothere` now, and a builtin's failure that says `no such` (this, Tty's
`no such directory`) answers ENOENT, the errno 9ns reads from those words.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | src/builtins.zig | 9 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 20 |
2 files changed, 25 insertions, 4 deletions
diff --git a/src/builtins.zig b/src/builtins.zig index 59fc140d..095eadbd 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -705,7 +705,14 @@ pub const Unmount = struct { var args = std.mem.tokenizeAny(u8, c.arg orelse "", " \t"); const name = args.next() orelse return c.p.reportError(c.id, "Unmount name", error.MissingArgument); if (args.next() != null) return c.p.reportError(c.id, "Unmount name", error.TooManyArguments); - pardes.filesystem.unmount(c.p, name) catch |err| return c.p.reportError(c.id, "Unmount", err); + pardes.filesystem.unmount(c.p, name) catch |err| { + // "no such": the words 9ns reads as ENOENT. + if (err == error.NotMounted) { + var said: [96]u8 = undefined; + return c.p.reportFailure(c.id, std.fmt.bufPrint(&said, "Unmount: no such mount {s}", .{name[0..@min(name.len, 32)]}) catch "Unmount: no such mount"); + } + return c.p.reportError(c.id, "Unmount", err); + }; } }; diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 330b9cb7..0ec5dc2a 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -239,7 +239,13 @@ pub fn captured(p: *Pardes, req: Req, id: usize, exec: bool, text: []const u8, i p.fs.results_len = 0; const failure = p.fs.failure[0..p.fs.failure_len]; // A slot or a place refused keeps its errno words (no space: ENOSPC). - return tree.failText(req.tag, E.IO, std.fmt.bufPrint(&p.fs.ename, "{s}", .{failure}) catch failure); + return tree.failText(req.tag, failureErrno(failure), std.fmt.bufPrint(&p.fs.ename, "{s}", .{failure}) catch failure); +} + +/// The errno a builtin's failure answers with: ENOENT for what is not +/// there (`no such`, the words 9ns reads as ENOENT too), else EIO. +fn failureErrno(failure: []const u8) u16 { + return if (std.mem.indexOf(u8, failure, "no such") != null) E.NOENT else E.IO; } pub fn resultsLen(p: *Pardes) u64 { @@ -444,7 +450,7 @@ pub fn runBuiltin(p: *Pardes, req: Req, id: usize, line: []const u8) ?Reply { // line, a program, is not quoted after it. const edit = std.mem.startsWith(u8, line, "Edit ") or std.mem.eql(u8, line, "Edit"); const refusal = if (edit) tree.failText(req.tag, E.IO, p.fs.failure[0..p.fs.failure_len]) else refuse(p, req, p.fs.failure[0..p.fs.failure_len], line); - return .{ .tag = req.tag, .status = .err, .errno = E.IO, .ename = refusal.ename }; + return .{ .tag = req.tag, .status = .err, .errno = failureErrno(p.fs.failure[0..p.fs.failure_len]), .ename = refusal.ename }; } const e_prompt = "control message needs its argument"; @@ -1793,7 +1799,7 @@ test "Tty in a directory that is not there fails and makes no pane; a shell that var count: usize = 0; for (p.panes) |slot| count += @intFromBool(slot != null); const refused = wr(p, Node.of(serial, .ctl), "Tty\n"); - try testing.expectEqual(E.IO, refused.errno()); + try testing.expectEqual(E.NOENT, refused.errno()); try testing.expectStringStartsWith(refused.reply.ename, "Tty: /tmp/pardes-no-such-dir-zz: no such directory"); var after: usize = 0; for (p.panes) |slot| after += @intFromBool(slot != null); @@ -1842,6 +1848,14 @@ test "Grep with no hit says grep: no match and opens nothing, a missing pane dir try testing.expectEqual(count, after); } +test "Unmount of a name not mounted says no such mount, which a mount reads as ENOENT" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + const refused = wr(p, @intFromEnum(tree.TopFile.ctl), "Unmount nothere\n"); + try testing.expectEqual(E.NOENT, refused.errno()); + try testing.expectStringStartsWith(refused.reply.ename, "Unmount: no such mount nothere"); +} + 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(); |
