summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 10:13:09 -0300
committerGabriel Schneider <[email protected]>2026-10-01 10:36:27 -0300
commitecc37bfdc4b5771dfe4bda61b9ffab9ad298950d (patch)
tree8acec3b0a727b2ac41963c4739a2a6fd3e7b94e4 /src
parent13c5f281ae0264e42e04bce527b83159fa75466c (diff)
downloadpardes-ecc37bfdc4b5771dfe4bda61b9ffab9ad298950d.tar.gz
pardes-ecc37bfdc4b5771dfe4bda61b9ffab9ad298950d.zip
Mount dials: a relative socket path is a bad dial address, a socket path not there says no such socket (ENOENT), and the hint says tcp! takes a numeric address
`Mount peer rel/x.sock` dialled relative to wherever the editor ran; `Mount peer unix!/nonexistent` said `dial failed: no answer` (EIO); and the hint offered `tcp!host!port` where only a numeric address is taken. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/9p_io.zig4
-rw-r--r--src/builtins.zig13
-rw-r--r--src/ninep/ctl.zig24
3 files changed, 36 insertions, 5 deletions
diff --git a/src/9p_io.zig b/src/9p_io.zig
index e199dea9..dd755f82 100644
--- a/src/9p_io.zig
+++ b/src/9p_io.zig
@@ -1427,6 +1427,7 @@ test "TCP addresses are numeric and normalize mapped IPv4" {
try testing.expectError(error.BadDial, Client.validateDial("unix!work"));
try testing.expectError(error.BadDial, Client.validateDial("nowhere!x"));
try Client.validateDial("/tmp/odd!name.sock");
+ try testing.expectError(error.BadDial, Client.validateDial("rel/dir.sock"));
try testing.expectError(error.BadDial, Client.validateDial("unix!"));
try testing.expectError(error.BadDial, Client.validateDial("unix!/tmp/a\x00b"));
try testing.expectError(error.BadDial, Client.validateDial("tcp!localhost!5640"));
@@ -2086,6 +2087,9 @@ pub const Client = struct {
const path = if (explicit_unix) dial[5..] else dial;
if (explicit_unix and !std.mem.startsWith(u8, path, "/")) return error.BadDial;
if (std.mem.indexOfScalar(u8, path, '/') != null) {
+ // A socket's path is absolute: a relative one would mean wherever
+ // the editor happens to run.
+ if (path[0] != '/') return error.BadDial;
if (std.mem.indexOfScalar(u8, path, 0) != null) return error.BadDial;
return .{ .unix = std.fmt.bufPrintSentinel(buf, "{s}", .{path}, 0) catch return error.BadDial };
}
diff --git a/src/builtins.zig b/src/builtins.zig
index ca80be26..907650bd 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -804,9 +804,20 @@ pub const Mount = struct {
if (dial.len == 0) return c.p.reportFailure(c.id, e_args);
c.p.fs.mount(c.p.gpa, name, dial) catch |err| {
var said: [200]u8 = undefined;
- if (err == error.BadDial) return c.p.reportFailure(c.id, std.fmt.bufPrint(&said, "Mount: bad dial address: {s} (unix!/path, tcp!host!port, or a session name)", .{dial[0..@min(dial.len, 100)]}) catch "Mount: bad dial address");
+ if (err == error.BadDial) return c.p.reportFailure(c.id, std.fmt.bufPrint(&said, "Mount: bad dial address: {s} (unix!/path, /path, tcp!<numeric-ip>!<port>, or a session name)", .{dial[0..@min(dial.len, 100)]}) catch "Mount: bad dial address");
return c.p.reportError(c.id, "Mount", err);
};
+ // A socket path that is not there: said so (ENOENT), not a dial
+ // that went unanswered.
+ const sock_path: ?[]const u8 = if (std.mem.startsWith(u8, dial, "unix!")) dial[5..] else if (dial.len > 0 and dial[0] == '/') dial else null;
+ if (sock_path) |path| if (@import("exec.zig").kindOf(path) == null) {
+ if (c.p.fs.mounts.pop()) |gone| {
+ c.p.gpa.free(gone.name);
+ c.p.gpa.free(gone.dial);
+ }
+ var said: [200]u8 = undefined;
+ return c.p.reportFailure(c.id, std.fmt.bufPrint(&said, "Mount {s}: {s}: no such socket", .{ name, path[0..@min(path.len, 100)] }) catch "Mount: no such socket");
+ };
// A mount that cannot be reached is said now, and taken back, not
// found out by the first look through it.
const reached = reached: {
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 4fb5bf41..06eb8cdd 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -1363,8 +1363,8 @@ test "the root ctl reads the settings as a write takes them, and takes the sessi
// A builtin that fails as it runs fails the write, quoting its error and
// its line; the lines before it have taken effect, as in acme.
const failed = wr(p, root_ctl, "Verbose on\nMount x /tmp/pardes-no-such-sock-zz\nVerbose off");
- try testing.expectEqual(E.IO, failed.errno());
- try testing.expectStringStartsWith(failed.reply.ename, "Mount x /tmp/pardes-no-such-sock-zz: dial failed");
+ try testing.expectEqual(E.NOENT, failed.errno());
+ try testing.expectStringStartsWith(failed.reply.ename, "Mount x: /tmp/pardes-no-such-sock-zz: no such socket");
try testing.expect(p.settings.verbose);
try testing.expect(!p.fs.no_prompt);
try testing.expectEqual(Status.ok, wr(p, root_ctl, "Verbose off").reply.status);
@@ -2458,9 +2458,14 @@ test "Mount dials its peer and fails, mounting nothing, when none answers; a loo
defer p.deinit();
const root_ctl = @intFromEnum(tree.TopFile.ctl);
const dead = "/tmp/pardes-test-no-such-peer.sock";
- const refused = wr(p, root_ctl, "Mount dead " ++ dead ++ "\n");
+ // A socket path not there is said so; one there that does not answer
+ // (a directory here) is a dial that failed.
+ const missing = wr(p, root_ctl, "Mount dead " ++ dead ++ "\n");
+ try testing.expectEqual(E.NOENT, missing.errno());
+ try testing.expectEqualStrings("Mount dead: " ++ dead ++ ": no such socket", missing.reply.ename);
+ const refused = wr(p, root_ctl, "Mount dead /tmp\n");
try testing.expectEqual(E.IO, refused.errno());
- try testing.expectEqualStrings("Mount dead " ++ dead ++ ": dial failed: no answer", refused.reply.ename);
+ try testing.expectEqualStrings("Mount dead /tmp: dial failed: no answer", 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) });
@@ -4438,3 +4443,14 @@ test "an unsaved pane from a dump that says nothing of its disk asks at its firs
pardes.exec.saveFile(p, id);
try testing.expect(th.logHas(p, "modified on disk since read (Save again to overwrite)"));
}
+
+test "a Mount of a socket path that is not there says no such socket, ENOENT" {
+ if (comptime !pardes.hosted) return;
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const r = wr(p, @intFromEnum(tree.TopFile.ctl), "Mount peer unix!/tmp/pardes-surely-no-socket\n");
+ try testing.expectEqual(E.NOENT, r.errno());
+ try testing.expect(std.mem.indexOf(u8, r.reply.ename, "no such socket") != null);
+ try testing.expectEqual(@as(usize, 0), p.fs.mounts.items.len);
+ try testing.expectEqual(E.INVAL, wr(p, @intFromEnum(tree.TopFile.ctl), "Mount peer rel/x.sock\n").errno());
+}