summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ninep/tree.zig15
1 files changed, 15 insertions, 0 deletions
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig
index 53d33731..4300d2b5 100644
--- a/src/ninep/tree.zig
+++ b/src/ninep/tree.zig
@@ -987,6 +987,10 @@ fn releaseHandle(p: *Pardes, req: Req) void {
}
fn setattr(p: *Pardes, req: Req, target: Target) Reply {
+ // A rename (`mv`) of a pane's directory, a column's or any file here:
+ // the tree names them, not the client. Refused as not permitted
+ // (EPERM), not taken and then lost, which a mount read as EIO.
+ if (req.set.name) return failText(req.tag, E.PERM, "rename not permitted: the tree names its files (a pane's name file renames its buffer)");
if (req.truncate) switch (target) {
.pane => |t| {
const id = p.paneBySerial(t.serial) orelse return Reply.fail(req.tag, E.NOENT);
@@ -1850,6 +1854,17 @@ test "a write cut where a writer's 4096-byte buffer filled holds its partial lin
try testing.expect(!th.logHas(p, "wrong #args"));
}
+test "a rename of a pane's directory or file is refused as not permitted" {
+ const p = try th.withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const serial = serialOf(p);
+ for ([_]u64{ Node.of(serial, .dir), Node.of(serial, .body) }) |node| {
+ const r = call(p, .{ .tag = 1, .op = .setattr, .node = node, .set = .{ .name = true } });
+ try testing.expectEqual(E.PERM, r.errno());
+ try testing.expect(std.mem.indexOf(u8, r.reply.ename, "not permitted") != null);
+ }
+}
+
test "no placement leaves a pane shorter than its tag and two rows; a full column refuses the next" {
const p = try th.withFile(testing.allocator, "x\n");
defer p.deinit();