Commit 14c046fc07

Pat Tullmann <pat.github@tullmann.org>
2025-02-19 02:18:03
lib/std: PermissionDenied/AccessDenied cleanup and fallout
This PR consistently maps .ACCES into AccessDenied and .PERM into PermissionDenied. AccessDenied is returned if the file mode bit (user/group/other rwx bits) disallow access (errno was `EACCES`). PermissionDenied is returned if something else denies access (errno was `EPERM`) (immutable bit, SELinux, capabilities, etc). This somewhat subtle distinction is a POSIX thing. Most of the change is updating std.posix Error Sets to contain both errors, and then propagating the pair up through caller Error Sets. Fixes #16782
1 parent 02373eb
Changed files (7)
lib/std/fs/Dir.zig
@@ -247,7 +247,7 @@ pub const Iterator = switch (native_os) {
                             .NOTDIR => unreachable,
                             .INVAL => unreachable,
                             .ACCES => return error.AccessDenied,
-                            .PERM => return error.AccessDenied,
+                            .PERM => return error.PermissionDenied,
                             else => |err| return posix.unexpectedErrno(err),
                         }
                         self.first_iter = false;
@@ -267,7 +267,7 @@ pub const Iterator = switch (native_os) {
                             .INVAL => unreachable,
                             .OVERFLOW => unreachable,
                             .ACCES => return error.AccessDenied,
-                            .PERM => return error.AccessDenied,
+                            .PERM => return error.PermissionDenied,
                             else => |err| return posix.unexpectedErrno(err),
                         }
                     }
@@ -294,7 +294,7 @@ pub const Iterator = switch (native_os) {
                     .BADF => unreachable, // Dir is invalid
                     .NOMEM => return error.SystemResources,
                     .ACCES => return error.AccessDenied,
-                    .PERM => return error.AccessDenied,
+                    .PERM => return error.PermissionDenied,
                     .FAULT => unreachable,
                     .NAMETOOLONG => unreachable,
                     .LOOP => unreachable,
@@ -768,6 +768,7 @@ pub const OpenError = error{
     FileNotFound,
     NotDir,
     AccessDenied,
+    PermissionDenied,
     SymLinkLoop,
     ProcessFdQuotaExceeded,
     NameTooLong,
@@ -1503,7 +1504,7 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenOptions) OpenErr
                 .NOENT => return error.FileNotFound,
                 .NOMEM => return error.SystemResources,
                 .NOTDIR => return error.NotDir,
-                .PERM => return error.AccessDenied,
+                .PERM => return error.PermissionDenied,
                 .BUSY => return error.DeviceBusy,
                 else => |err| return posix.unexpectedErrno(err),
             }
@@ -1652,9 +1653,9 @@ pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
 pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {
     posix.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) {
         error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR
-        error.AccessDenied => |e| switch (native_os) {
-            // non-Linux POSIX systems return EPERM when trying to delete a directory, so
-            // we need to handle that case specifically and translate the error
+        error.AccessDenied, error.PermissionDenied => |e| switch (native_os) {
+            // non-Linux POSIX systems return permission errors when trying to delete a
+            // directory, so we need to handle that case specifically and translate the error
             .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {
                 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
                 const fstat = posix.fstatatZ(self.fd, sub_path_c, posix.AT.SYMLINK_NOFOLLOW) catch return e;
@@ -1679,6 +1680,7 @@ pub const DeleteDirError = error{
     DirNotEmpty,
     FileNotFound,
     AccessDenied,
+    PermissionDenied,
     FileBusy,
     FileSystem,
     SymLinkLoop,
@@ -1991,6 +1993,7 @@ pub fn readFileAllocOptions(
 
 pub const DeleteTreeError = error{
     AccessDenied,
+    PermissionDenied,
     FileTooBig,
     SymLinkLoop,
     ProcessFdQuotaExceeded,
@@ -2073,6 +2076,7 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
                             },
 
                             error.AccessDenied,
+                            error.PermissionDenied,
                             error.SymLinkLoop,
                             error.ProcessFdQuotaExceeded,
                             error.NameTooLong,
@@ -2112,6 +2116,7 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
                         },
 
                         error.AccessDenied,
+                        error.PermissionDenied,
                         error.InvalidUtf8,
                         error.InvalidWtf8,
                         error.SymLinkLoop,
@@ -2168,6 +2173,7 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
                             },
 
                             error.AccessDenied,
+                            error.PermissionDenied,
                             error.SymLinkLoop,
                             error.ProcessFdQuotaExceeded,
                             error.NameTooLong,
@@ -2197,6 +2203,7 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
                             },
 
                             error.AccessDenied,
+                            error.PermissionDenied,
                             error.InvalidUtf8,
                             error.InvalidWtf8,
                             error.SymLinkLoop,
@@ -2273,6 +2280,7 @@ fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint
                             },
 
                             error.AccessDenied,
+                            error.PermissionDenied,
                             error.SymLinkLoop,
                             error.ProcessFdQuotaExceeded,
                             error.NameTooLong,
@@ -2309,6 +2317,7 @@ fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint
                             },
 
                             error.AccessDenied,
+                            error.PermissionDenied,
                             error.InvalidUtf8,
                             error.InvalidWtf8,
                             error.SymLinkLoop,
@@ -2371,6 +2380,7 @@ fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File
                     },
 
                     error.AccessDenied,
+                    error.PermissionDenied,
                     error.SymLinkLoop,
                     error.ProcessFdQuotaExceeded,
                     error.NameTooLong,
@@ -2397,6 +2407,7 @@ fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File
                     },
 
                     error.AccessDenied,
+                    error.PermissionDenied,
                     error.InvalidUtf8,
                     error.InvalidWtf8,
                     error.SymLinkLoop,
lib/std/io/c_writer.zig
@@ -23,7 +23,7 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u
         .FBIG => return error.FileTooBig,
         .IO => return error.InputOutput,
         .NOSPC => return error.NoSpaceLeft,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .PIPE => return error.BrokenPipe,
         else => |err| return std.posix.unexpectedErrno(err),
     }
lib/std/os/linux/bpf.zig
@@ -1548,7 +1548,7 @@ pub fn map_create(map_type: MapType, key_size: u32, value_size: u32, max_entries
         .SUCCESS => return @as(fd_t, @intCast(rc)),
         .INVAL => return error.MapTypeOrAttrInvalid,
         .NOMEM => return error.SystemResources,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         else => |err| return unexpectedErrno(err),
     }
 }
@@ -1574,7 +1574,7 @@ pub fn map_lookup_elem(fd: fd_t, key: []const u8, value: []u8) !void {
         .FAULT => unreachable,
         .INVAL => return error.FieldInAttrNeedsZeroing,
         .NOENT => return error.NotFound,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         else => |err| return unexpectedErrno(err),
     }
 }
@@ -1597,7 +1597,7 @@ pub fn map_update_elem(fd: fd_t, key: []const u8, value: []const u8, flags: u64)
         .FAULT => unreachable,
         .INVAL => return error.FieldInAttrNeedsZeroing,
         .NOMEM => return error.SystemResources,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         else => |err| return unexpectedErrno(err),
     }
 }
@@ -1617,7 +1617,7 @@ pub fn map_delete_elem(fd: fd_t, key: []const u8) !void {
         .FAULT => unreachable,
         .INVAL => return error.FieldInAttrNeedsZeroing,
         .NOENT => return error.NotFound,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         else => |err| return unexpectedErrno(err),
     }
 }
@@ -1638,7 +1638,7 @@ pub fn map_get_next_key(fd: fd_t, key: []const u8, next_key: []u8) !bool {
         .FAULT => unreachable,
         .INVAL => return error.FieldInAttrNeedsZeroing,
         .NOENT => return false,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         else => |err| return unexpectedErrno(err),
     }
 }
@@ -1712,7 +1712,7 @@ pub fn prog_load(
         .ACCES => error.UnsafeProgram,
         .FAULT => unreachable,
         .INVAL => error.InvalidProgram,
-        .PERM => error.AccessDenied,
+        .PERM => error.PermissionDenied,
         else => |err| unexpectedErrno(err),
     };
 }
lib/std/zig/system.zig
@@ -731,6 +731,7 @@ pub fn abiAndDynamicLinkerFromFile(
                 error.NetworkNotFound => unreachable, // Windows only
 
                 error.AccessDenied,
+                error.PermissionDenied,
                 error.FileNotFound,
                 error.NotLink,
                 error.NotDir,
@@ -825,6 +826,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
         error.FileNotFound,
         error.NotDir,
         error.AccessDenied,
+        error.PermissionDenied,
         error.NoDevice,
         => return error.GLibCNotFound,
 
@@ -863,6 +865,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
         error.NoDevice => unreachable, // not asking for a special device
 
         error.AccessDenied,
+        error.PermissionDenied,
         error.FileNotFound,
         error.NotDir,
         error.IsDir,
@@ -1122,6 +1125,7 @@ fn detectAbiAndDynamicLinker(
                 error.IsDir,
                 error.NotDir,
                 error.AccessDenied,
+                error.PermissionDenied,
                 error.NoDevice,
                 error.FileNotFound,
                 error.NetworkNotFound,
lib/std/os.zig
@@ -65,7 +65,7 @@ pub fn accessW(path: [*:0]const u16) windows.GetFileAttributesError!void {
     switch (windows.GetLastError()) {
         .FILE_NOT_FOUND => return error.FileNotFound,
         .PATH_NOT_FOUND => return error.FileNotFound,
-        .ACCESS_DENIED => return error.PermissionDenied,
+        .ACCESS_DENIED => return error.AccessDenied,
         else => |err| return windows.unexpectedError(err),
     }
 }
lib/std/posix.zig
@@ -281,6 +281,7 @@ pub fn close(fd: fd_t) void {
 
 pub const FChmodError = error{
     AccessDenied,
+    PermissionDenied,
     InputOutput,
     SymLinkLoop,
     FileNotFound,
@@ -310,7 +311,7 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
             .NOENT => return error.FileNotFound,
             .NOMEM => return error.SystemResources,
             .NOTDIR => return error.FileNotFound,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .ROFS => return error.ReadOnlyFileSystem,
             else => |err| return unexpectedErrno(err),
         }
@@ -387,7 +388,7 @@ fn fchmodat1(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr
             .NOTDIR => return error.FileNotFound,
             .NOMEM => return error.SystemResources,
             .OPNOTSUPP => return error.OperationNotSupported,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .ROFS => return error.ReadOnlyFileSystem,
             else => |err| return unexpectedErrno(err),
         }
@@ -418,7 +419,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr
             .NOMEM => return error.SystemResources,
             .NOTDIR => return error.FileNotFound,
             .OPNOTSUPP => return error.OperationNotSupported,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .ROFS => return error.ReadOnlyFileSystem,
 
             .NOSYS => {
@@ -447,7 +448,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr
             .FAULT => unreachable,
             .INVAL => unreachable,
             .ACCES => return error.AccessDenied,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .LOOP => return error.SymLinkLoop,
             .MFILE => return error.ProcessFdQuotaExceeded,
             .NAMETOOLONG => return error.NameTooLong,
@@ -486,7 +487,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr
             .LOOP => return error.SymLinkLoop,
             .NOMEM => return error.SystemResources,
             .NOTDIR => return error.FileNotFound,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .ROFS => return error.ReadOnlyFileSystem,
             else => |err| return unexpectedErrno(err),
         }
@@ -495,6 +496,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr
 
 pub const FChownError = error{
     AccessDenied,
+    PermissionDenied,
     InputOutput,
     SymLinkLoop,
     FileNotFound,
@@ -529,7 +531,7 @@ pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
             .NOENT => return error.FileNotFound,
             .NOMEM => return error.SystemResources,
             .NOTDIR => return error.FileNotFound,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .ROFS => return error.ReadOnlyFileSystem,
             else => |err| return unexpectedErrno(err),
         }
@@ -1031,10 +1033,8 @@ pub const TruncateError = error{
     FileTooBig,
     InputOutput,
     FileBusy,
-
-    /// In WASI, this error occurs when the file descriptor does
-    /// not hold the required rights to call `ftruncate` on it.
     AccessDenied,
+    PermissionDenied,
 } || UnexpectedError;
 
 pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
@@ -1066,7 +1066,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
             .INTR => unreachable,
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .TXTBSY => return error.FileBusy,
             .BADF => unreachable, // Handle not open for writing
             .INVAL => unreachable, // Handle not open for writing
@@ -1082,7 +1082,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
             .INTR => continue,
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .TXTBSY => return error.FileBusy,
             .BADF => unreachable, // Handle not open for writing
             .INVAL => unreachable, // Handle not open for writing
@@ -1176,6 +1176,7 @@ pub const WriteError = error{
 
     /// File descriptor does not hold the required rights to write to it.
     AccessDenied,
+    PermissionDenied,
     BrokenPipe,
     SystemResources,
     OperationAborted,
@@ -1250,7 +1251,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .NOTCAPABLE => return error.AccessDenied,
             else => |err| return unexpectedErrno(err),
@@ -1278,7 +1279,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
             .ACCES => return error.AccessDenied,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .CONNRESET => return error.ConnectionResetByPeer,
             .BUSY => return error.DeviceBusy,
@@ -1331,7 +1332,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .NOTCAPABLE => return error.AccessDenied,
             else => |err| return unexpectedErrno(err),
@@ -1353,7 +1354,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .CONNRESET => return error.ConnectionResetByPeer,
             .BUSY => return error.DeviceBusy,
@@ -1410,7 +1411,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .NXIO => return error.Unseekable,
             .SPIPE => return error.Unseekable,
@@ -1443,7 +1444,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .NXIO => return error.Unseekable,
             .SPIPE => return error.Unseekable,
@@ -1502,7 +1503,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .NXIO => return error.Unseekable,
             .SPIPE => return error.Unseekable,
@@ -1528,7 +1529,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
             .FBIG => return error.FileTooBig,
             .IO => return error.InputOutput,
             .NOSPC => return error.NoSpaceLeft,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .PIPE => return error.BrokenPipe,
             .NXIO => return error.Unseekable,
             .SPIPE => return error.Unseekable,
@@ -1543,6 +1544,7 @@ pub const OpenError = error{
     /// In WASI, this error may occur when the file descriptor does
     /// not hold the required rights to open a new resource relative to it.
     AccessDenied,
+    PermissionDenied,
     SymLinkLoop,
     ProcessFdQuotaExceeded,
     SystemFdQuotaExceeded,
@@ -1660,7 +1662,7 @@ pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t {
             .NOMEM => return error.SystemResources,
             .NOSPC => return error.NoSpaceLeft,
             .NOTDIR => return error.NotDir,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .EXIST => return error.PathAlreadyExists,
             .BUSY => return error.DeviceBusy,
             .ILSEQ => |err| if (native_os == .wasi)
@@ -1831,7 +1833,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O
             .NOMEM => return error.SystemResources,
             .NOSPC => return error.NoSpaceLeft,
             .NOTDIR => return error.NotDir,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .EXIST => return error.PathAlreadyExists,
             .BUSY => return error.DeviceBusy,
             .OPNOTSUPP => return error.FileLocksNotSupported,
@@ -1873,6 +1875,7 @@ pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
 pub const ExecveError = error{
     SystemResources,
     AccessDenied,
+    PermissionDenied,
     InvalidExe,
     FileSystem,
     IsDir,
@@ -1899,7 +1902,7 @@ pub fn execveZ(
         .NFILE => return error.SystemFdQuotaExceeded,
         .NOMEM => return error.SystemResources,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .INVAL => return error.InvalidExe,
         .NOEXEC => return error.InvalidExe,
         .IO => return error.FileSystem,
@@ -2077,6 +2080,7 @@ pub const SymLinkError = error{
     /// In WASI, this error may occur when the file descriptor does
     /// not hold the required rights to create a new symbolic link relative to it.
     AccessDenied,
+    PermissionDenied,
     DiskQuota,
     PathAlreadyExists,
     FileSystem,
@@ -2130,7 +2134,7 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
         .FAULT => unreachable,
         .INVAL => unreachable,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .DQUOT => return error.DiskQuota,
         .EXIST => return error.PathAlreadyExists,
         .IO => return error.FileSystem,
@@ -2208,7 +2212,7 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:
         .FAULT => unreachable,
         .INVAL => unreachable,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .DQUOT => return error.DiskQuota,
         .EXIST => return error.PathAlreadyExists,
         .IO => return error.FileSystem,
@@ -2229,6 +2233,7 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:
 
 pub const LinkError = UnexpectedError || error{
     AccessDenied,
+    PermissionDenied,
     DiskQuota,
     PathAlreadyExists,
     FileSystem,
@@ -2264,7 +2269,7 @@ pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8) LinkError!void {
         .NOENT => return error.FileNotFound,
         .NOMEM => return error.SystemResources,
         .NOSPC => return error.NoSpaceLeft,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .ROFS => return error.ReadOnlyFileSystem,
         .XDEV => return error.NotSameFileSystem,
         .INVAL => unreachable,
@@ -2318,7 +2323,7 @@ pub fn linkatZ(
         .NOMEM => return error.SystemResources,
         .NOSPC => return error.NoSpaceLeft,
         .NOTDIR => return error.NotDir,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .ROFS => return error.ReadOnlyFileSystem,
         .XDEV => return error.NotSameFileSystem,
         .INVAL => unreachable,
@@ -2367,7 +2372,7 @@ pub fn linkat(
             .NOMEM => return error.SystemResources,
             .NOSPC => return error.NoSpaceLeft,
             .NOTDIR => return error.NotDir,
-            .PERM => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
             .ROFS => return error.ReadOnlyFileSystem,
             .XDEV => return error.NotSameFileSystem,
             .INVAL => unreachable,
@@ -2386,6 +2391,7 @@ pub const UnlinkError = error{
     /// In WASI, this error may occur when the file descriptor does
     /// not hold the required rights to unlink a resource by path relative to it.
     AccessDenied,
+    PermissionDenied,
     FileBusy,
     FileSystem,
     IsDir,
@@ -2441,7 +2447,7 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
     switch (errno(system.unlink(file_path))) {
         .SUCCESS => return,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .BUSY => return error.FileBusy,
         .FAULT => unreachable,
         .INVAL => unreachable,
@@ -2535,7 +2541,7 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
     switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
         .SUCCESS => return,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .BUSY => return error.FileBusy,
         .FAULT => unreachable,
         .IO => return error.FileSystem,
@@ -2573,6 +2579,7 @@ pub const RenameError = error{
     /// On Windows, this error may be returned instead of PathAlreadyExists when
     /// renaming a directory over an existing directory.
     AccessDenied,
+    PermissionDenied,
     FileBusy,
     DiskQuota,
     IsDir,
@@ -2635,7 +2642,7 @@ pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!voi
     switch (errno(system.rename(old_path, new_path))) {
         .SUCCESS => return,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .BUSY => return error.FileBusy,
         .DQUOT => return error.DiskQuota,
         .FAULT => unreachable,
@@ -2750,7 +2757,7 @@ pub fn renameatZ(
     switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
         .SUCCESS => return,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .BUSY => return error.FileBusy,
         .DQUOT => return error.DiskQuota,
         .FAULT => unreachable,
@@ -2933,7 +2940,7 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: mode_t) MakeDir
         .SUCCESS => return,
         .ACCES => return error.AccessDenied,
         .BADF => unreachable,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .DQUOT => return error.DiskQuota,
         .EXIST => return error.PathAlreadyExists,
         .FAULT => unreachable,
@@ -2978,6 +2985,7 @@ pub const MakeDirError = error{
     /// In WASI, this error may occur when the file descriptor does
     /// not hold the required rights to create a new directory relative to it.
     AccessDenied,
+    PermissionDenied,
     DiskQuota,
     PathAlreadyExists,
     SymLinkLoop,
@@ -3030,7 +3038,7 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void {
     switch (errno(system.mkdir(dir_path, mode))) {
         .SUCCESS => return,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .DQUOT => return error.DiskQuota,
         .EXIST => return error.PathAlreadyExists,
         .FAULT => unreachable,
@@ -3071,6 +3079,7 @@ pub fn mkdirW(dir_path_w: []const u16, mode: mode_t) MakeDirError!void {
 
 pub const DeleteDirError = error{
     AccessDenied,
+    PermissionDenied,
     FileBusy,
     SymLinkLoop,
     NameTooLong,
@@ -3123,7 +3132,7 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
     switch (errno(system.rmdir(dir_path))) {
         .SUCCESS => return,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .BUSY => return error.FileBusy,
         .FAULT => unreachable,
         .INVAL => return error.BadPathName,
@@ -3254,6 +3263,7 @@ pub const ReadLinkError = error{
     /// In WASI, this error may occur when the file descriptor does
     /// not hold the required rights to read value of a symbolic link relative to it.
     AccessDenied,
+    PermissionDenied,
     FileSystem,
     SymLinkLoop,
     NameTooLong,
@@ -3534,7 +3544,7 @@ pub fn isatty(handle: fd_t) bool {
 pub const SocketError = error{
     /// Permission to create a socket of the specified type and/or
     /// pro‐tocol is denied.
-    PermissionDenied,
+    AccessDenied,
 
     /// The implementation does not support the specified address family.
     AddressFamilyNotSupported,
@@ -3604,7 +3614,7 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t
             }
             return fd;
         },
-        .ACCES => return error.PermissionDenied,
+        .ACCES => return error.AccessDenied,
         .AFNOSUPPORT => return error.AddressFamilyNotSupported,
         .INVAL => return error.ProtocolFamilyNotAvailable,
         .MFILE => return error.ProcessFdQuotaExceeded,
@@ -4188,6 +4198,9 @@ pub const ConnectError = error{
     /// or
     /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled  or
     /// the connection request failed because of a local firewall rule.
+    AccessDenied,
+
+    /// See AccessDenied
     PermissionDenied,
 
     /// Local address is already in use.
@@ -4261,7 +4274,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
     while (true) {
         switch (errno(system.connect(sock, sock_addr, len))) {
             .SUCCESS => return,
-            .ACCES => return error.PermissionDenied,
+            .ACCES => return error.AccessDenied,
             .PERM => return error.PermissionDenied,
             .ADDRINUSE => return error.AddressInUse,
             .ADDRNOTAVAIL => return error.AddressNotAvailable,
@@ -4323,7 +4336,7 @@ pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
     switch (errno(rc)) {
         .SUCCESS => switch (@as(E, @enumFromInt(err_code))) {
             .SUCCESS => return,
-            .ACCES => return error.PermissionDenied,
+            .ACCES => return error.AccessDenied,
             .PERM => return error.PermissionDenied,
             .ADDRINUSE => return error.AddressInUse,
             .ADDRNOTAVAIL => return error.AddressNotAvailable,
@@ -4398,6 +4411,7 @@ pub const FStatError = error{
     /// In WASI, this error may occur when the file descriptor does
     /// not hold the required rights to get its filestat information.
     AccessDenied,
+    PermissionDenied,
 } || UnexpectedError;
 
 /// Return information about a file descriptor.
@@ -4466,7 +4480,7 @@ pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!S
         .BADF => unreachable, // Always a race condition.
         .NOMEM => return error.SystemResources,
         .ACCES => return error.AccessDenied,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         .FAULT => unreachable,
         .NAMETOOLONG => return error.NameTooLong,
         .LOOP => return error.SymLinkLoop,
@@ -4870,6 +4884,7 @@ pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void {
 }
 
 pub const AccessError = error{
+    AccessDenied,
     PermissionDenied,
     FileNotFound,
     NameTooLong,
@@ -4917,7 +4932,7 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
     }
     switch (errno(system.access(path, mode))) {
         .SUCCESS => return,
-        .ACCES => return error.PermissionDenied,
+        .ACCES => return error.AccessDenied,
         .PERM => return error.PermissionDenied,
         .ROFS => return error.ReadOnlyFileSystem,
         .LOOP => return error.SymLinkLoop,
@@ -4998,7 +5013,7 @@ pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) Acces
     }
     switch (errno(system.faccessat(dirfd, path, mode, flags))) {
         .SUCCESS => return,
-        .ACCES => return error.PermissionDenied,
+        .ACCES => return error.AccessDenied,
         .PERM => return error.PermissionDenied,
         .ROFS => return error.ReadOnlyFileSystem,
         .LOOP => return error.SymLinkLoop,
@@ -5049,7 +5064,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16) AccessError!void {
         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
         .OBJECT_NAME_INVALID => unreachable,
         .INVALID_PARAMETER => unreachable,
-        .ACCESS_DENIED => return error.PermissionDenied,
+        .ACCESS_DENIED => return error.AccessDenied,
         .OBJECT_PATH_SYNTAX_BAD => unreachable,
         else => |rc| return windows.unexpectedStatus(rc),
     }
@@ -5431,6 +5446,7 @@ pub fn flock(fd: fd_t, operation: i32) FlockError!void {
 pub const RealPathError = error{
     FileNotFound,
     AccessDenied,
+    PermissionDenied,
     NameTooLong,
     NotSupported,
     NotDir,
@@ -6812,7 +6828,7 @@ pub const SetSockOptError = error{
     /// Insufficient resources are available in the system to complete the call.
     SystemResources,
 
-    // Setting the socket option requires more elevated permissions.
+    /// Setting the socket option requires more elevated permissions.
     PermissionDenied,
 
     OperationNotSupported,
@@ -7307,7 +7323,7 @@ pub fn perf_event_open(
 }
 
 pub const TimerFdCreateError = error{
-    AccessDenied,
+    PermissionDenied,
     ProcessFdQuotaExceeded,
     SystemFdQuotaExceeded,
     NoDevice,
@@ -7326,7 +7342,7 @@ pub fn timerfd_create(clock_id: system.timerfd_clockid_t, flags: system.TFD) Tim
         .NFILE => return error.SystemFdQuotaExceeded,
         .NODEV => return error.NoDevice,
         .NOMEM => return error.SystemResources,
-        .PERM => return error.AccessDenied,
+        .PERM => return error.PermissionDenied,
         else => |err| return unexpectedErrno(err),
     };
 }
src/DarwinPosixSpawn.zig
@@ -6,6 +6,7 @@ pub const Error = error{
     InvalidFileDescriptor,
     NameTooLong,
     TooBig,
+    AccessDenied,
     PermissionDenied,
     InputOutput,
     FileSystem,
@@ -188,7 +189,7 @@ pub fn spawnZ(
         .@"2BIG" => return error.TooBig,
         .NOMEM => return error.SystemResources,
         .BADF => return error.InvalidFileDescriptor,
-        .ACCES => return error.PermissionDenied,
+        .ACCES => return error.AccessDenied,
         .IO => return error.InputOutput,
         .LOOP => return error.FileSystem,
         .NAMETOOLONG => return error.NameTooLong,