From 2f1b7cd5fb82e619043ccb34826154a53e53c28e Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 08:12:59 -0300 Subject: get on a pane of a peer out of reach says dial failed, as look and Save do get's failure named the path and `cannot be read` for any error but a missing file; a peer that did not answer now reads `get: : dial failed: no answer`. Co-Authored-By: Claude Opus 5.5 --- src/ninep/ctl.zig | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 8cf4f0a8..76cba9d5 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -760,11 +760,17 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { const r = refuse(p, req, said, line); return .{ .tag = req.tag, .status = .err, .errno = E.IO, .ename = r.ename }; } - const errno = get(p, pane); - // Naming the path, in words 9ns reads as the errno. + var failed: anyerror = error.Unexpected; + const errno = get(p, pane, &failed); + // Naming the path, in words 9ns reads as the errno; a peer + // out of reach as a look or a Save says it. if (errno != 0) { const path = if (pane_files.fileOf(pane)) |f| f.path else ""; - return tree.failText(req.tag, errno, std.fmt.bufPrint(&p.fs.ename, "get: {s}: {s}", .{ path[0..@min(path.len, 256)], if (errno == E.NOENT) "no such file" else "cannot be read" }) catch "get: no such file"); + var why_buf: [48]u8 = undefined; + const why = if (pardes.Messages.dialReason(failed)) |r| + std.fmt.bufPrint(&why_buf, "dial failed: {s}", .{r}) catch "dial failed" + else if (errno == E.NOENT) "no such file" else "cannot be read"; + return tree.failText(req.tag, errno, std.fmt.bufPrint(&p.fs.ename, "get: {s}: {s}", .{ path[0..@min(path.len, 256)], why }) catch "get: no such file"); } } else if (std.mem.eql(u8, line, "lock")) { if (mine == null) return tree.failText(req.tag, E.INVAL, tree.e_bad_ctl); @@ -838,12 +844,15 @@ fn getRefused(p: *Pardes, pane: *Pane) ?[]const u8 { return said; } -fn get(p: *Pardes, pane: *Pane) u16 { +fn get(p: *Pardes, pane: *Pane, failed: *anyerror) u16 { const f = pane_files.fileOf(pane) orelse return 0; if (!panes.Output.fileTraits(f.output).saves) return 0; - const bytes = fs.read(p, f.path) catch |err| return switch (err) { - error.FileNotFound => E.NOENT, - else => E.IO, + const bytes = fs.read(p, f.path) catch |err| { + failed.* = err; + return switch (err) { + error.FileNotFound => E.NOENT, + else => E.IO, + }; }; // Throwing unsaved edits away for it is a reload the log hears of, as a // clean one from the watch is. @@ -1736,6 +1745,11 @@ test "Mount dials its peer and fails, mounting nothing, when none answers; a loo try testing.expectEqual(E.IO, refused.errno()); try testing.expectEqualStrings("Mount dead " ++ dead ++ ": dial failed: no answer \"Mount dead " ++ dead ++ "\"", refused.reply.ename); try testing.expectEqual(@as(usize, 0), p.fs.mounts.items.len); + // A pane of a peer gone since: get says so too. + try p.fs.mounts.append(p.gpa, .{ .name = try p.gpa.dupe(u8, "away"), .dial = try p.gpa.dupe(u8, dead) }); + _ = wr(p, Node.of(serialOf(p), .name), "/n/away/f.txt\n"); + const got = wr(p, Node.of(serialOf(p), .ctl), "get\n"); + try testing.expectEqualStrings("get: /n/away/f.txt: dial failed: no answer", got.reply.ename); // Mounted when it answered, gone since: a look through it says so. try p.fs.mounts.append(p.gpa, .{ .name = try p.gpa.dupe(u8, "gone"), .dial = try p.gpa.dupe(u8, dead) }); const looked = wr(p, @intFromEnum(tree.TopFile.look), "/n/gone/f.txt\n"); -- cgit v1.3