diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 6097fc6df3..6121878f2b 100644 --- a/lib/std/Io/Threaded.zig +++ b/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( diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 1f179241b0..15f3a6c45a 100644 --- a/lib/std/fs/Dir.zig +++ b/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;