diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 05:01:31 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 05:18:09 -0300 |
| commit | 250db41743f77ce3d8421f729bdea0065aa6bc79 (patch) | |
| tree | 906bbefca6d448ba04c81c93cf552e23b5e6c3a4 /src/fs.zig | |
| parent | 36fe45e3a4a7ce9b61ab7bf2df07f0132a9170c1 (diff) | |
| download | pardes-250db41743f77ce3d8421f729bdea0065aa6bc79.tar.gz pardes-250db41743f77ce3d8421f729bdea0065aa6bc79.zip | |
A name under a directory that may not be searched or written is refused, permission denied, and a Save there says so, not no such directory
`pardes noperm/a/b/c` and `pardes /proc/1/root/x` took the missing
directory for one Save would make, opened a pane and exited 0; its Save
then said "no such directory". fs.deniedAbove finds the nearest
directory there and asks whether it may be searched and written:
forwarding refuses such a name, exit 1, "permission denied", and a
failed Save says "permission denied", which the writer's 9P error
carries as EPERM (9ns: EACCES).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/fs.zig')
| -rw-r--r-- | src/fs.zig | 20 |
1 files changed, 20 insertions, 0 deletions
@@ -685,6 +685,26 @@ pub fn makeDirs(dir: []const u8) void { _ = libc.mkdir(z[0..dir.len :0], 0o755); } +/// Whether `dir` is not there to make because one above it refuses: its +/// nearest ancestor that is there may not be searched or written (EACCES: +/// `noperm/a/b`, `/proc/1/root/x`). +pub fn deniedAbove(dir: []const u8) bool { + if (comptime !platform_has_fs) return false; + var at: ?[]const u8 = dir; + while (at) |d| : (at = std.fs.path.dirname(d)) { + var z: [4096]u8 = undefined; + const dz = std.fmt.bufPrintSentinel(&z, "{s}", .{d}, 0) catch return false; + if (libc.access(dz.ptr, 0) != 0) continue; // F_OK + return libc.access(dz.ptr, 2 | 1) != 0; // W_OK | X_OK + } + return false; +} + +test "a directory under one that may not be searched or written is denied, one merely missing is not" { + try std.testing.expect(deniedAbove("/proc/1/root/x/y")); + try std.testing.expect(!deniedAbove("/tmp/pardes-surely-missing-dir/a/b")); +} + pub fn localPath(path: []const u8) ?[]const u8 { if (std.mem.eql(u8, path, "/n/os")) return "/"; if (std.mem.startsWith(u8, path, "/n/os/")) return path[5..]; |
