Commit 62c0496d0a

Andrew Kelley <andrew@ziglang.org>
2025-10-20 15:13:39
std.Io.Threaded: implement dirOpenDir
1 parent dc6a4f3
Changed files (2)
lib
lib/std/fs/Dir.zig
@@ -1069,96 +1069,9 @@ pub const OpenOptions = Io.Dir.OpenOptions;
 
 /// Deprecated in favor of `Io.Dir.openDir`.
 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenOptions) OpenError!Dir {
-    switch (native_os) {
-        .windows => {
-            var threaded: Io.Threaded = .init_single_threaded;
-            const io = threaded.io();
-            return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
-        },
-        .wasi => if (!builtin.link_libc) {
-            var threaded: Io.Threaded = .init_single_threaded;
-            const io = threaded.io();
-            return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
-        },
-        else => {},
-    }
-    const sub_path_c = try posix.toPosixPath(sub_path);
-    return self.openDirZ(&sub_path_c, args);
-}
-
-/// Same as `openDir` except the parameter is null-terminated.
-pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenOptions) OpenError!Dir {
-    switch (native_os) {
-        .windows => {
-            @compileError("use std.Io instead");
-        },
-        // Use the libc API when libc is linked because it implements things
-        // such as opening absolute directory paths.
-        .wasi => if (!builtin.link_libc) {
-            return openDir(self, mem.sliceTo(sub_path_c, 0), args);
-        },
-        .haiku => {
-            const rc = posix.system._kern_open_dir(self.fd, sub_path_c);
-            if (rc >= 0) return .{ .fd = rc };
-            switch (@as(posix.E, @enumFromInt(rc))) {
-                .FAULT => unreachable,
-                .INVAL => unreachable,
-                .BADF => unreachable,
-                .ACCES => return error.AccessDenied,
-                .LOOP => return error.SymLinkLoop,
-                .MFILE => return error.ProcessFdQuotaExceeded,
-                .NAMETOOLONG => return error.NameTooLong,
-                .NFILE => return error.SystemFdQuotaExceeded,
-                .NODEV => return error.NoDevice,
-                .NOENT => return error.FileNotFound,
-                .NOMEM => return error.SystemResources,
-                .NOTDIR => return error.NotDir,
-                .PERM => return error.PermissionDenied,
-                .BUSY => return error.DeviceBusy,
-                else => |err| return posix.unexpectedErrno(err),
-            }
-        },
-        else => {},
-    }
-
-    var symlink_flags: posix.O = switch (native_os) {
-        .wasi => .{
-            .read = true,
-            .NOFOLLOW = !args.follow_symlinks,
-            .DIRECTORY = true,
-        },
-        else => .{
-            .ACCMODE = .RDONLY,
-            .NOFOLLOW = !args.follow_symlinks,
-            .DIRECTORY = true,
-            .CLOEXEC = true,
-        },
-    };
-
-    if (@hasField(posix.O, "PATH") and !args.iterate)
-        symlink_flags.PATH = true;
-
-    return self.openDirFlagsZ(sub_path_c, symlink_flags);
-}
-
-/// Asserts `flags` has `DIRECTORY` set.
-fn openDirFlagsZ(self: Dir, sub_path_c: [*:0]const u8, flags: posix.O) OpenError!Dir {
-    assert(flags.DIRECTORY);
-    const fd = posix.openatZ(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
-        error.FileTooBig => unreachable, // can't happen for directories
-        error.IsDir => unreachable, // we're setting DIRECTORY
-        error.NoSpaceLeft => unreachable, // not setting CREAT
-        error.PathAlreadyExists => unreachable, // not setting CREAT
-        error.FileLocksNotSupported => unreachable, // locking folders is not supported
-        error.WouldBlock => unreachable, // can't happen for directories
-        error.FileBusy => unreachable, // can't happen for directories
-        error.SharingViolation => unreachable, // can't happen for directories
-        error.PipeBusy => unreachable, // can't happen for directories
-        error.AntivirusInterference => unreachable, // can't happen for directories
-        error.ProcessNotFound => unreachable, // can't happen for directories
-        else => |e| return e,
-    };
-    return Dir{ .fd = fd };
+    var threaded: Io.Threaded = .init_single_threaded;
+    const io = threaded.io();
+    return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
 }
 
 pub const DeleteFileError = posix.UnlinkError;
lib/std/Io/Threaded.zig
@@ -208,6 +208,7 @@ pub fn io(t: *Threaded) Io {
             .dirOpenDir = switch (builtin.os.tag) {
                 .windows => dirOpenDirWindows,
                 .wasi => dirOpenDirWasi,
+                .haiku => dirOpenDirHaiku,
                 else => dirOpenDirPosix,
             },
             .dirClose = dirClose,
@@ -1784,22 +1785,20 @@ fn dirOpenFilePosix(
     if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty;
 
     // Use the O locking flags if the os supports them to acquire the lock
-    // atomically.
-    if (have_flock_open_flags) {
-        // Note that the NONBLOCK flag is removed after the openat() call
-        // is successful.
-        switch (flags.lock) {
-            .none => {},
-            .shared => {
-                os_flags.SHLOCK = true;
-                os_flags.NONBLOCK = flags.lock_nonblocking;
-            },
-            .exclusive => {
-                os_flags.EXLOCK = true;
-                os_flags.NONBLOCK = flags.lock_nonblocking;
-            },
-        }
-    }
+    // atomically. Note that the NONBLOCK flag is removed after the openat()
+    // call is successful.
+    if (have_flock_open_flags) switch (flags.lock) {
+        .none => {},
+        .shared => {
+            os_flags.SHLOCK = true;
+            os_flags.NONBLOCK = flags.lock_nonblocking;
+        },
+        .exclusive => {
+            os_flags.EXLOCK = true;
+            os_flags.NONBLOCK = flags.lock_nonblocking;
+        },
+    };
+
     const fd: posix.fd_t = while (true) {
         try t.checkCancel();
         const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0));
@@ -2008,11 +2007,92 @@ fn dirOpenDirPosix(
 ) Io.Dir.OpenError!Io.Dir {
     const t: *Threaded = @ptrCast(@alignCast(userdata));
 
-    _ = t;
-    _ = dir;
-    _ = sub_path;
+    var path_buffer: [posix.PATH_MAX]u8 = undefined;
+    const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
+
+    var flags: posix.O = switch (native_os) {
+        .wasi => .{
+            .read = true,
+            .NOFOLLOW = !options.follow_symlinks,
+            .DIRECTORY = true,
+        },
+        else => .{
+            .ACCMODE = .RDONLY,
+            .NOFOLLOW = !options.follow_symlinks,
+            .DIRECTORY = true,
+            .CLOEXEC = true,
+        },
+    };
+
+    if (@hasField(posix.O, "PATH") and !options.iterate)
+        flags.PATH = true;
+
+    while (true) {
+        try t.checkCancel();
+        const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0));
+        switch (posix.errno(rc)) {
+            .SUCCESS => return .{ .handle = @intCast(rc) },
+            .INTR => continue,
+            .CANCELED => return error.Canceled,
+
+            .FAULT => |err| return errnoBug(err),
+            .INVAL => return error.BadPathName,
+            .BADF => |err| return errnoBug(err), // File descriptor used after closed.
+            .ACCES => return error.AccessDenied,
+            .LOOP => return error.SymLinkLoop,
+            .MFILE => return error.ProcessFdQuotaExceeded,
+            .NAMETOOLONG => return error.NameTooLong,
+            .NFILE => return error.SystemFdQuotaExceeded,
+            .NODEV => return error.NoDevice,
+            .NOENT => return error.FileNotFound,
+            .NOMEM => return error.SystemResources,
+            .NOTDIR => return error.NotDir,
+            .PERM => return error.PermissionDenied,
+            .BUSY => return error.DeviceBusy,
+            .NXIO => return error.NoDevice,
+            .ILSEQ => return error.BadPathName,
+            else => |err| return posix.unexpectedErrno(err),
+        }
+    }
+}
+
+fn dirOpenDirHaiku(
+    userdata: ?*anyopaque,
+    dir: Io.Dir,
+    sub_path: []const u8,
+    options: Io.Dir.OpenOptions,
+) Io.Dir.OpenError!Io.Dir {
+    const t: *Threaded = @ptrCast(@alignCast(userdata));
+
+    var path_buffer: [posix.PATH_MAX]u8 = undefined;
+    const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
+
     _ = options;
-    @panic("TODO");
+
+    while (true) {
+        try t.checkCancel();
+        const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix);
+        if (rc >= 0) return .{ .handle = rc };
+        switch (@as(posix.E, @enumFromInt(rc))) {
+            .INTR => continue,
+            .CANCELED => return error.Canceled,
+            .FAULT => |err| return errnoBug(err),
+            .INVAL => |err| return errnoBug(err),
+            .BADF => |err| return errnoBug(err), // File descriptor used after closed.
+            .ACCES => return error.AccessDenied,
+            .LOOP => return error.SymLinkLoop,
+            .MFILE => return error.ProcessFdQuotaExceeded,
+            .NAMETOOLONG => return error.NameTooLong,
+            .NFILE => return error.SystemFdQuotaExceeded,
+            .NODEV => return error.NoDevice,
+            .NOENT => return error.FileNotFound,
+            .NOMEM => return error.SystemResources,
+            .NOTDIR => return error.NotDir,
+            .PERM => return error.PermissionDenied,
+            .BUSY => return error.DeviceBusy,
+            else => |err| return posix.unexpectedErrno(err),
+        }
+    }
 }
 
 fn dirOpenDirWindows(