Commit f8ea00bd6d

Andrew Kelley <andrew@ziglang.org>
2025-10-17 05:53:28
std.Io: add dirAccess
1 parent 3bf0ce6
lib/std/fs/Dir.zig
@@ -2353,47 +2353,14 @@ pub fn writeFile(self: Dir, options: WriteFileOptions) WriteFileError!void {
     try file.writeAll(options.data);
 }
 
-pub const AccessError = posix.AccessError;
+/// Deprecated in favor of `Io.Dir.AccessError`.
+pub const AccessError = Io.Dir.AccessError;
 
-/// Test accessing `sub_path`.
-/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `sub_path` should be encoded as valid UTF-8.
-/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
-/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function.
-/// For example, instead of testing if a file exists and then opening it, just
-/// open it and handle the error for file not found.
-pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
-    if (native_os == .windows) {
-        const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
-        return self.accessW(sub_path_w.span().ptr, flags);
-    }
-    const path_c = try posix.toPosixPath(sub_path);
-    return self.accessZ(&path_c, flags);
-}
-
-/// Same as `access` except the path parameter is null-terminated.
-pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
-    if (native_os == .windows) {
-        const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path);
-        return self.accessW(sub_path_w.span().ptr, flags);
-    }
-    const os_mode = switch (flags.mode) {
-        .read_only => @as(u32, posix.F_OK),
-        .write_only => @as(u32, posix.W_OK),
-        .read_write => @as(u32, posix.R_OK | posix.W_OK),
-    };
-    const result = posix.faccessatZ(self.fd, sub_path, os_mode, 0);
-    return result;
-}
-
-/// Same as `access` except asserts the target OS is Windows and the path parameter is
-/// * WTF-16 LE encoded
-/// * null-terminated
-/// * relative or has the NT namespace prefix
-/// TODO currently this ignores `flags`.
-pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
-    _ = flags;
-    return posix.faccessatW(self.fd, sub_path_w);
+/// Deprecated in favor of `Io.Dir.access`.
+pub fn access(self: Dir, sub_path: []const u8, options: Io.Dir.AccessOptions) AccessError!void {
+    var threaded: Io.Threaded = .init_single_threaded;
+    const io = threaded.io();
+    return Io.Dir.access(self.adaptToNewApi(), io, sub_path, options);
 }
 
 pub const CopyFileOptions = struct {
lib/std/fs/File.zig
@@ -40,65 +40,12 @@ pub const default_mode = switch (builtin.os.tag) {
 
 /// Deprecated in favor of `Io.File.OpenError`.
 pub const OpenError = Io.File.OpenError || error{WouldBlock};
-
-pub const OpenMode = enum {
-    read_only,
-    write_only,
-    read_write,
-};
-
-pub const Lock = enum {
-    none,
-    shared,
-    exclusive,
-};
-
-pub const OpenFlags = struct {
-    mode: OpenMode = .read_only,
-
-    /// Open the file with an advisory lock to coordinate with other processes
-    /// accessing it at the same time. An exclusive lock will prevent other
-    /// processes from acquiring a lock. A shared lock will prevent other
-    /// processes from acquiring a exclusive lock, but does not prevent
-    /// other process from getting their own shared locks.
-    ///
-    /// The lock is advisory, except on Linux in very specific circumstances[1].
-    /// This means that a process that does not respect the locking API can still get access
-    /// to the file, despite the lock.
-    ///
-    /// On these operating systems, the lock is acquired atomically with
-    /// opening the file:
-    /// * Darwin
-    /// * DragonFlyBSD
-    /// * FreeBSD
-    /// * Haiku
-    /// * NetBSD
-    /// * OpenBSD
-    /// On these operating systems, the lock is acquired via a separate syscall
-    /// after opening the file:
-    /// * Linux
-    /// * Windows
-    ///
-    /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
-    lock: Lock = .none,
-
-    /// Sets whether or not to wait until the file is locked to return. If set to true,
-    /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
-    /// is available to proceed.
-    lock_nonblocking: bool = false,
-
-    /// Set this to allow the opened file to automatically become the
-    /// controlling TTY for the current process.
-    allow_ctty: bool = false,
-
-    pub fn isRead(self: OpenFlags) bool {
-        return self.mode != .write_only;
-    }
-
-    pub fn isWrite(self: OpenFlags) bool {
-        return self.mode != .read_only;
-    }
-};
+/// Deprecated in favor of `Io.File.OpenMode`.
+pub const OpenMode = Io.File.OpenMode;
+/// Deprecated in favor of `Io.File.Lock`.
+pub const Lock = Io.File.Lock;
+/// Deprecated in favor of `Io.File.OpenFlags`.
+pub const OpenFlags = Io.File.OpenFlags;
 
 pub const CreateFlags = struct {
     /// Whether the file will be created with read access.
lib/std/Io/Dir.zig
@@ -23,6 +23,37 @@ pub const PathNameError = error{
     BadPathName,
 };
 
+pub const AccessError = error{
+    AccessDenied,
+    PermissionDenied,
+    FileNotFound,
+    InputOutput,
+    SystemResources,
+    FileBusy,
+    SymLinkLoop,
+    ReadOnlyFileSystem,
+} || PathNameError || Io.Cancelable || Io.UnexpectedError;
+
+pub const AccessOptions = packed struct {
+    follow_symlinks: bool = true,
+    read: bool = false,
+    write: bool = false,
+    execute: bool = false,
+};
+
+/// Test accessing `sub_path`.
+///
+/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
+/// On WASI, `sub_path` should be encoded as valid UTF-8.
+/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
+///
+/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this
+/// function. For example, instead of testing if a file exists and then opening
+/// it, just open it and handle the error for file not found.
+pub fn access(dir: Dir, io: Io, sub_path: []const u8, options: AccessOptions) AccessError!void {
+    return io.vtable.dirAccess(io.userdata, dir, sub_path, options);
+}
+
 pub const OpenError = error{
     FileNotFound,
     NotDir,
lib/std/Io/File.zig
@@ -80,7 +80,67 @@ pub fn stat(file: File, io: Io) StatError!Stat {
     return io.vtable.fileStat(io.userdata, file);
 }
 
-pub const OpenFlags = std.fs.File.OpenFlags;
+pub const OpenMode = enum {
+    read_only,
+    write_only,
+    read_write,
+};
+
+pub const Lock = enum {
+    none,
+    shared,
+    exclusive,
+};
+
+pub const OpenFlags = struct {
+    mode: OpenMode = .read_only,
+
+    /// Open the file with an advisory lock to coordinate with other processes
+    /// accessing it at the same time. An exclusive lock will prevent other
+    /// processes from acquiring a lock. A shared lock will prevent other
+    /// processes from acquiring a exclusive lock, but does not prevent
+    /// other process from getting their own shared locks.
+    ///
+    /// The lock is advisory, except on Linux in very specific circumstances[1].
+    /// This means that a process that does not respect the locking API can still get access
+    /// to the file, despite the lock.
+    ///
+    /// On these operating systems, the lock is acquired atomically with
+    /// opening the file:
+    /// * Darwin
+    /// * DragonFlyBSD
+    /// * FreeBSD
+    /// * Haiku
+    /// * NetBSD
+    /// * OpenBSD
+    /// On these operating systems, the lock is acquired via a separate syscall
+    /// after opening the file:
+    /// * Linux
+    /// * Windows
+    ///
+    /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
+    lock: Lock = .none,
+
+    /// Sets whether or not to wait until the file is locked to return. If set to true,
+    /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
+    /// is available to proceed.
+    lock_nonblocking: bool = false,
+
+    /// Set this to allow the opened file to automatically become the
+    /// controlling TTY for the current process.
+    allow_ctty: bool = false,
+
+    follow_symlinks: bool = true,
+
+    pub fn isRead(self: OpenFlags) bool {
+        return self.mode != .write_only;
+    }
+
+    pub fn isWrite(self: OpenFlags) bool {
+        return self.mode != .read_only;
+    }
+};
+
 pub const CreateFlags = std.fs.File.CreateFlags;
 
 pub const OpenError = error{
lib/std/Io/Threaded.zig
@@ -183,6 +183,11 @@ pub fn io(t: *Threaded) Io {
                 .wasi => fileStatWasi,
                 else => fileStatPosix,
             },
+            .dirAccess = switch (builtin.os.tag) {
+                .windows => @panic("TODO"),
+                .wasi => dirAccessWasi,
+                else => dirAccessPosix,
+            },
             .dirCreateFile = switch (builtin.os.tag) {
                 .windows => @panic("TODO"),
                 .wasi => @panic("TODO"),
@@ -992,7 +997,6 @@ fn dirStatPathWasi(
 ) Io.Dir.StatPathError!Io.File.Stat {
     if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options);
     const t: *Threaded = @ptrCast(@alignCast(userdata));
-    const dir_fd = dir.handle;
     const wasi = std.os.wasi;
     const flags: wasi.lookupflags_t = .{
         .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
@@ -1000,16 +1004,16 @@ fn dirStatPathWasi(
     var stat: wasi.filestat_t = undefined;
     while (true) {
         try t.checkCancel();
-        switch (wasi.path_filestat_get(dir_fd, flags, sub_path.ptr, sub_path.len, &stat)) {
+        switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
             .SUCCESS => return statFromWasi(stat),
             .INTR => continue,
             .CANCELED => return error.Canceled,
 
-            .INVAL => |err| errnoBug(err),
-            .BADF => |err| errnoBug(err), // Always a race condition.
+            .INVAL => |err| return errnoBug(err),
+            .BADF => |err| return errnoBug(err), // Always a race condition.
             .NOMEM => return error.SystemResources,
             .ACCES => return error.AccessDenied,
-            .FAULT => |err| errnoBug(err),
+            .FAULT => |err| return errnoBug(err),
             .NAMETOOLONG => return error.NameTooLong,
             .NOENT => return error.FileNotFound,
             .NOTDIR => return error.FileNotFound,
@@ -1103,6 +1107,110 @@ const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.syste
 const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek;
 const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
 
+fn dirAccessPosix(
+    userdata: ?*anyopaque,
+    dir: Io.Dir,
+    sub_path: []const u8,
+    options: Io.Dir.AccessOptions,
+) Io.Dir.AccessError!void {
+    const t: *Threaded = @ptrCast(@alignCast(userdata));
+
+    var path_buffer: [posix.PATH_MAX]u8 = undefined;
+    const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
+
+    const flags: u32 = @as(u32, if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0);
+
+    const mode: u32 =
+        @as(u32, if (options.read) posix.R_OK else 0) |
+        @as(u32, if (options.write) posix.W_OK else 0) |
+        @as(u32, if (options.execute) posix.X_OK else 0);
+
+    while (true) {
+        try t.checkCancel();
+        switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) {
+            .SUCCESS => return,
+            .INTR => continue,
+            .CANCELED => return error.Canceled,
+
+            .ACCES => return error.AccessDenied,
+            .PERM => return error.PermissionDenied,
+            .ROFS => return error.ReadOnlyFileSystem,
+            .LOOP => return error.SymLinkLoop,
+            .TXTBSY => return error.FileBusy,
+            .NOTDIR => return error.FileNotFound,
+            .NOENT => return error.FileNotFound,
+            .NAMETOOLONG => return error.NameTooLong,
+            .INVAL => |err| return errnoBug(err),
+            .FAULT => |err| return errnoBug(err),
+            .IO => return error.InputOutput,
+            .NOMEM => return error.SystemResources,
+            .ILSEQ => return error.BadPathName, // TODO move to wasi
+            else => |err| return posix.unexpectedErrno(err),
+        }
+    }
+}
+
+fn dirAccessWasi(
+    userdata: ?*anyopaque,
+    dir: Io.Dir,
+    sub_path: []const u8,
+    options: Io.File.OpenFlags,
+) Io.File.AccessError!void {
+    if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);
+    const t: *Threaded = @ptrCast(@alignCast(userdata));
+    const wasi = std.os.wasi;
+    const flags: wasi.lookupflags_t = .{
+        .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
+    };
+    const stat = while (true) {
+        var stat: wasi.filestat_t = undefined;
+        try t.checkCancel();
+        switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
+            .SUCCESS => break statFromWasi(stat),
+            .INTR => continue,
+            .CANCELED => return error.Canceled,
+
+            .INVAL => |err| return errnoBug(err),
+            .BADF => |err| return errnoBug(err), // Always a race condition.
+            .NOMEM => return error.SystemResources,
+            .ACCES => return error.AccessDenied,
+            .FAULT => |err| return errnoBug(err),
+            .NAMETOOLONG => return error.NameTooLong,
+            .NOENT => return error.FileNotFound,
+            .NOTDIR => return error.FileNotFound,
+            .NOTCAPABLE => return error.AccessDenied,
+            .ILSEQ => return error.BadPathName,
+            else => |err| return posix.unexpectedErrno(err),
+        }
+    };
+
+    if (!options.mode.read and !options.mode.write and !options.mode.execute)
+        return;
+
+    var directory: wasi.fdstat_t = undefined;
+    if (wasi.fd_fdstat_get(dir.handle, &directory) != .SUCCESS)
+        return error.AccessDenied;
+
+    var rights: wasi.rights_t = .{};
+    if (options.mode.read) {
+        if (stat.filetype == .DIRECTORY) {
+            rights.FD_READDIR = true;
+        } else {
+            rights.FD_READ = true;
+        }
+    }
+    if (options.mode.write)
+        rights.FD_WRITE = true;
+
+    // No validation for execution.
+
+    // https://github.com/ziglang/zig/issues/18882
+    const rights_int: u64 = @bitCast(rights);
+    const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
+    if ((rights_int & inheriting_int) != rights_int)
+        return error.AccessDenied;
+}
+
 fn dirCreateFilePosix(
     userdata: ?*anyopaque,
     dir: Io.Dir,
lib/std/fs.zig
@@ -1,14 +1,15 @@
 //! File System.
+const builtin = @import("builtin");
+const native_os = builtin.os.tag;
 
 const std = @import("std.zig");
-const builtin = @import("builtin");
+const Io = std.Io;
 const root = @import("root");
 const mem = std.mem;
 const base64 = std.base64;
 const crypto = std.crypto;
 const Allocator = std.mem.Allocator;
 const assert = std.debug.assert;
-const native_os = builtin.os.tag;
 const posix = std.posix;
 const windows = std.os.windows;
 
@@ -274,7 +275,7 @@ pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) Fi
 /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
 /// On WASI, `absolute_path` should be encoded as valid UTF-8.
 /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
-pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void {
+pub fn accessAbsolute(absolute_path: []const u8, flags: Io.Dir.AccessOptions) Dir.AccessError!void {
     assert(path.isAbsolute(absolute_path));
     try cwd().access(absolute_path, flags);
 }
lib/std/Io.zig
@@ -663,6 +663,7 @@ pub const VTable = struct {
     dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void,
     dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
     dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,
+    dirAccess: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.AccessOptions) Dir.AccessError!void,
     dirCreateFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.CreateFlags) File.OpenError!File,
     dirOpenFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File,
     fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
lib/std/posix.zig
@@ -4360,8 +4360,7 @@ pub const FStatAtError = FStatError || error{
     NameTooLong,
     FileNotFound,
     SymLinkLoop,
-    /// WASI-only; file paths must be valid UTF-8.
-    InvalidUtf8,
+    BadPathName,
 };
 
 /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
@@ -4900,7 +4899,7 @@ pub fn access(path: []const u8, mode: u32) AccessError!void {
         _ = try windows.GetFileAttributesW(path_w.span().ptr);
         return;
     } else if (native_os == .wasi and !builtin.link_libc) {
-        return faccessat(AT.FDCWD, path, mode, 0);
+        @compileError("wasi doesn't support absolute paths");
     }
     const path_c = try toPosixPath(path);
     return accessZ(&path_c, mode);
@@ -4934,121 +4933,6 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
     }
 }
 
-/// Check user's permissions for a file, based on an open directory handle.
-///
-/// * On Windows, asserts `path` is valid [WTF-8](https://wtf-8.codeberg.page/).
-/// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`.
-/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
-///
-/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by
-/// Windows. See `fs` for the cross-platform file system API.
-pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
-    if (native_os == .windows) {
-        const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
-        return faccessatW(dirfd, path_w.span().ptr);
-    } else if (native_os == .wasi and !builtin.link_libc) {
-        const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path };
-
-        const st = try std.os.fstatat_wasi(dirfd, path, .{
-            .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
-        });
-
-        if (mode != F_OK) {
-            var directory: wasi.fdstat_t = undefined;
-            if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
-                return error.AccessDenied;
-            }
-
-            var rights: wasi.rights_t = .{};
-            if (mode & R_OK != 0) {
-                if (st.filetype == .DIRECTORY) {
-                    rights.FD_READDIR = true;
-                } else {
-                    rights.FD_READ = true;
-                }
-            }
-            if (mode & W_OK != 0) {
-                rights.FD_WRITE = true;
-            }
-            // No validation for X_OK
-
-            // https://github.com/ziglang/zig/issues/18882
-            const rights_int: u64 = @bitCast(rights);
-            const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
-            if ((rights_int & inheriting_int) != rights_int) {
-                return error.AccessDenied;
-            }
-        }
-        return;
-    }
-    const path_c = try toPosixPath(path);
-    return faccessatZ(dirfd, &path_c, mode, flags);
-}
-
-/// Same as `faccessat` except the path parameter is null-terminated.
-pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
-    if (native_os == .windows) {
-        const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
-        return faccessatW(dirfd, path_w.span().ptr);
-    } else if (native_os == .wasi and !builtin.link_libc) {
-        return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
-    }
-    switch (errno(system.faccessat(dirfd, path, mode, flags))) {
-        .SUCCESS => return,
-        .ACCES => return error.AccessDenied,
-        .PERM => return error.PermissionDenied,
-        .ROFS => return error.ReadOnlyFileSystem,
-        .LOOP => return error.SymLinkLoop,
-        .TXTBSY => return error.FileBusy,
-        .NOTDIR => return error.FileNotFound,
-        .NOENT => return error.FileNotFound,
-        .NAMETOOLONG => return error.NameTooLong,
-        .INVAL => unreachable,
-        .FAULT => unreachable,
-        .IO => return error.InputOutput,
-        .NOMEM => return error.SystemResources,
-        .ILSEQ => return error.BadPathName,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// Same as `faccessat` except asserts the target is Windows and the path parameter
-/// is NtDll-prefixed, null-terminated, WTF-16 encoded.
-pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16) AccessError!void {
-    if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
-        return;
-    }
-    if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
-        return;
-    }
-
-    const path_len_bytes = cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
-    var nt_name = windows.UNICODE_STRING{
-        .Length = path_len_bytes,
-        .MaximumLength = path_len_bytes,
-        .Buffer = @constCast(sub_path_w),
-    };
-    var attr = windows.OBJECT_ATTRIBUTES{
-        .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
-        .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
-        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
-        .ObjectName = &nt_name,
-        .SecurityDescriptor = null,
-        .SecurityQualityOfService = null,
-    };
-    var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
-    switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
-        .SUCCESS => return,
-        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
-        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
-        .OBJECT_NAME_INVALID => unreachable,
-        .INVALID_PARAMETER => unreachable,
-        .ACCESS_DENIED => return error.AccessDenied,
-        .OBJECT_PATH_SYNTAX_BAD => unreachable,
-        else => |rc| return windows.unexpectedStatus(rc),
-    }
-}
-
 pub const PipeError = error{
     SystemFdQuotaExceeded,
     ProcessFdQuotaExceeded,