summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fs.zig29
-rw-r--r--src/ninep/ctl.zig1
-rw-r--r--src/sam_edit.zig6
3 files changed, 33 insertions, 3 deletions
diff --git a/src/fs.zig b/src/fs.zig
index bc35e1cb..d13e36bd 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -213,6 +213,11 @@ pub fn osHandle(p: *pardes.Pardes, req: Req) Reply {
return .{ .tag = req.tag, .payload = .{ .staged = @intCast(out.items.len) } };
},
.read, .write, .setattr => {
+ // A rename or a mode change (`mv`, `chmod`) is refused as not
+ // permitted, EACCES through a mount: /os reads and writes the
+ // files it shows and changes nothing about them.
+ if (req.op == .setattr and (req.set.name or req.set.mode))
+ return tree.failText(req.tag, E.PERM, "permission denied: /os renames nothing and changes no mode");
if (stat.kind != .file) return Reply.fail(req.tag, E.PERM);
var z: [4096]u8 = undefined;
const path_z = std.fmt.bufPrintSentinel(&z, "{s}", .{path}, 0) catch return Reply.fail(req.tag, E.NOENT);
@@ -255,6 +260,30 @@ pub fn validMountName(name: []const u8) bool {
return true;
}
+test "/os refuses a rename or a mode change as not permitted, in words, and still truncates to zero" {
+ if (comptime !pardes.hosted) return error.SkipZigTest;
+ const p = try th.withFile(std.testing.allocator, "x\n");
+ defer p.deinit();
+ var tmp = std.testing.tmpDir(.{});
+ defer tmp.cleanup();
+ try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "kept.txt", .data = "kept\n" });
+ var dir_buf: [4096]u8 = undefined;
+ const dir = dir_buf[0..try tmp.dir.realPath(std.testing.io, &dir_buf)];
+ var node: u64 = os_root;
+ var parts = std.mem.tokenizeScalar(u8, dir, '/');
+ while (parts.next()) |part| node = th.look_up(p, node, part).reply.attr.node;
+ node = th.look_up(p, node, "kept.txt").reply.attr.node;
+ for ([_]Req{
+ .{ .tag = 1, .op = .setattr, .node = node, .set = .{ .mode = true } },
+ .{ .tag = 2, .op = .setattr, .node = node, .set = .{ .name = true }, .data = "moved.txt" },
+ }) |req| {
+ const r = th.call(p, req);
+ try std.testing.expectEqual(E.PERM, r.errno());
+ try std.testing.expectStringStartsWith(r.reply.ename, "permission denied");
+ }
+ try std.testing.expectEqual(tree.Status.ok, th.call(p, .{ .tag = 3, .op = .setattr, .node = node, .truncate = true }).reply.status);
+}
+
test "mounts own their names and dials and reject duplicate or reserved names" {
const gpa = std.testing.allocator;
var ns: Namespace = .{};
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 5a199f55..6e09084b 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -1854,6 +1854,7 @@ test "an error's words give the errno a mount reads: EINVAL for what is malforme
.{ .text = addressing.e_order, .malformed = false },
.{ .text = addressing.e_slow, .malformed = false },
.{ .text = "/tmp/x.txt: Modified (Exit again to discard)", .malformed = false },
+ .{ .text = "Edit: w is a sam command pardes's Edit leaves out (it has no file or shell commands)", .malformed = false },
}) |c| {
const says_einval = for (einval) |w| {
if (std.ascii.findIgnoreCase(c.text, w) != null) break true;
diff --git a/src/sam_edit.zig b/src/sam_edit.zig
index 94cf6fe0..9b055ef9 100644
--- a/src/sam_edit.zig
+++ b/src/sam_edit.zig
@@ -242,7 +242,7 @@ const Parser = struct {
}
},
'\'' => return fail(ps.why, "can't handle '", .{}),
- '"' => return fail(ps.why, "file addresses are not supported", .{}),
+ '"' => return fail(ps.why, "a file address is no address pardes's Edit takes", .{}),
else => break,
}
}
@@ -410,7 +410,7 @@ const Parser = struct {
if (nest == 0) return fail(ps.why, "right brace with no left brace", .{});
return null;
},
- 'b', 'B', 'D', 'e', 'r', 'w', 'f', 'X', 'Y', '<', '|', '>' => return fail(ps.why, "{c} is not supported in pardes", .{c}),
+ 'b', 'B', 'D', 'e', 'r', 'w', 'f', 'X', 'Y', '<', '|', '>' => return fail(ps.why, "{c} is a sam command pardes's Edit leaves out (it has no file or shell commands)", .{c}),
else => return fail(ps.why, "unknown command {c}", .{c}),
}
return cmd;
@@ -768,7 +768,7 @@ test "an Edit that fails halfway changes nothing, and says why in acme's words"
.{ "}", "right brace with no left brace" },
.{ ",x/foo/{", "unmatched `{'" },
.{ ",x/foo/{\nd", "unmatched `{'" },
- .{ "w /tmp/x", "w is not supported in pardes" },
+ .{ "w /tmp/x", "w is a sam command pardes's Edit leaves out (it has no file or shell commands)" },
.{ ",s/(a)/\\1/", "no \\1: mvzr keeps no submatches" },
.{ "1 m 1,2", "move overlaps itself" },
.{ ",x/(^|\\n)foo/d", "bad regular expression: in a pattern with \\n, ^ can only come first and $ only just before a \\n" },