summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/fs.md3
-rw-r--r--src/ninep/pane.zig19
2 files changed, 21 insertions, 1 deletions
diff --git a/docs/fs.md b/docs/fs.md
index dacb49ba..041f3389 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -397,7 +397,8 @@ buffer (relative to the pane's directory) and marks nothing dirty; `Save`
then writes under the new name. A name is one line; refused (EINVAL) are a
second line, a blank at either end, control bytes and non-UTF-8 (`bad
character in file name: a blank at its start`, ...). Up to 255 bytes a
-component.
+component. A directory (`/`, `~`, `foo/`) is no file name: refused, EISDIR
+(`name: /home/u is a directory, not a file`).
**`body`** reads the text; a write appends; `>` (OTRUNC) replaces it all.
A terminal's body is its history as plain text in logical lines (wrapped
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index d3c1b907..78bec09f 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -883,6 +883,11 @@ fn writeName(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply {
const full = std.fs.path.resolvePosix(p.scratch.allocator(), &.{ pardes.Pardes.paneDir(pane), fs.expandHome(name, &home_buf) }) catch
return Reply.fail(req.tag, E.NOMEM);
if (!std.fs.path.isAbsolute(full) or full.len >= 4096) return tree.failText(req.tag, E.INVAL, "invalid file name: longer than a path may be");
+ // A directory (`~`, `/`, `foo/`) is no file to name a pane after: a
+ // Save could only fail. Said now, EISDIR.
+ const is_dir = name[name.len - 1] == '/' or std.mem.eql(u8, full, "/") or
+ (if (comptime pardes.hosted) (if (fs.localPath(full)) |local| exec.isDirectory(local) else false) else false);
+ if (is_dir) return tree.failText(req.tag, E.ISDIR, std.fmt.bufPrint(&p.fs.ename, "name: {s} is a directory, not a file", .{full[0..@min(full.len, 256)]}) catch "name: is a directory, not a file");
nameBuffer(p, id, full, false) catch |err| return Reply.fail(req.tag, switch (err) {
error.OutOfMemory => E.NOMEM,
else => E.INVAL,
@@ -1415,6 +1420,20 @@ test "an addr pair out of order or out of range is refused as the address form i
try testing.expectEqual(State.Range{ .q0 = 2, .q1 = 4 }, p.panes[p.paneBySerial(serial).?].?.fs.addr);
}
+test "name refuses a directory, EISDIR, and leaves the name as it was" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const serial = serialOf(p);
+ const node = Node.of(serial, .name);
+ for ([_][]const u8{ "/\n", "/tmp/\n", "sub/\n", "/tmp\n" }) |dir| {
+ if (comptime !pardes.hosted) if (std.mem.eql(u8, dir, "/tmp\n")) continue;
+ const r = wr(p, node, dir);
+ try testing.expectEqual(E.ISDIR, r.errno());
+ try testing.expect(std.mem.indexOf(u8, r.reply.ename, "is a directory") != null);
+ }
+ try testing.expectEqualStrings("/test.txt", nameOf(p, p.panes[p.paneBySerial(serial).?].?));
+}
+
test "a write of two lines to name is refused EINVAL, on a held open or not" {
const p = try withFile(testing.allocator, "x\n");
defer p.deinit();