authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-20 06:13:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:50-07:00
log62c0496d0a3bed811174080f651408c89bdce0c9
tree107e2f2869cef0aec08907f2470e68a9c5d94dd6
parentdc6a4f3bf1262b46a46e0e23f5c09a93c7c780c5

std.Io.Threaded: implement dirOpenDir


2 files changed, 103 insertions(+), 110 deletions(-)

lib/std/Io/Threaded.zig+100-20
......@@ -208,6 +208,7 @@ pub fn io(t: *Threaded) Io {
208208 .dirOpenDir = switch (builtin.os.tag) {
209209 .windows => dirOpenDirWindows,
210210 .wasi => dirOpenDirWasi,
211 .haiku => dirOpenDirHaiku,
211212 else => dirOpenDirPosix,
212213 },
213214 .dirClose = dirClose,
......@@ -1784,22 +1785,20 @@ fn dirOpenFilePosix(
17841785 if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty;
17851786
17861787 // Use the O locking flags if the os supports them to acquire the lock
1787 // atomically.
1788 if (have_flock_open_flags) {
1789 // Note that the NONBLOCK flag is removed after the openat() call
1790 // is successful.
1791 switch (flags.lock) {
1792 .none => {},
1793 .shared => {
1794 os_flags.SHLOCK = true;
1795 os_flags.NONBLOCK = flags.lock_nonblocking;
1796 },
1797 .exclusive => {
1798 os_flags.EXLOCK = true;
1799 os_flags.NONBLOCK = flags.lock_nonblocking;
1800 },
1801 }
1802 }
1788 // atomically. Note that the NONBLOCK flag is removed after the openat()
1789 // call is successful.
1790 if (have_flock_open_flags) switch (flags.lock) {
1791 .none => {},
1792 .shared => {
1793 os_flags.SHLOCK = true;
1794 os_flags.NONBLOCK = flags.lock_nonblocking;
1795 },
1796 .exclusive => {
1797 os_flags.EXLOCK = true;
1798 os_flags.NONBLOCK = flags.lock_nonblocking;
1799 },
1800 };
1801
18031802 const fd: posix.fd_t = while (true) {
18041803 try t.checkCancel();
18051804 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0));
......@@ -2008,11 +2007,92 @@ fn dirOpenDirPosix(
20082007) Io.Dir.OpenError!Io.Dir {
20092008 const t: *Threaded = @ptrCast(@alignCast(userdata));
20102009
2011 _ = t;
2012 _ = dir;
2013 _ = sub_path;
2010 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2011 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
2012
2013 var flags: posix.O = switch (native_os) {
2014 .wasi => .{
2015 .read = true,
2016 .NOFOLLOW = !options.follow_symlinks,
2017 .DIRECTORY = true,
2018 },
2019 else => .{
2020 .ACCMODE = .RDONLY,
2021 .NOFOLLOW = !options.follow_symlinks,
2022 .DIRECTORY = true,
2023 .CLOEXEC = true,
2024 },
2025 };
2026
2027 if (@hasField(posix.O, "PATH") and !options.iterate)
2028 flags.PATH = true;
2029
2030 while (true) {
2031 try t.checkCancel();
2032 const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0));
2033 switch (posix.errno(rc)) {
2034 .SUCCESS => return .{ .handle = @intCast(rc) },
2035 .INTR => continue,
2036 .CANCELED => return error.Canceled,
2037
2038 .FAULT => |err| return errnoBug(err),
2039 .INVAL => return error.BadPathName,
2040 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
2041 .ACCES => return error.AccessDenied,
2042 .LOOP => return error.SymLinkLoop,
2043 .MFILE => return error.ProcessFdQuotaExceeded,
2044 .NAMETOOLONG => return error.NameTooLong,
2045 .NFILE => return error.SystemFdQuotaExceeded,
2046 .NODEV => return error.NoDevice,
2047 .NOENT => return error.FileNotFound,
2048 .NOMEM => return error.SystemResources,
2049 .NOTDIR => return error.NotDir,
2050 .PERM => return error.PermissionDenied,
2051 .BUSY => return error.DeviceBusy,
2052 .NXIO => return error.NoDevice,
2053 .ILSEQ => return error.BadPathName,
2054 else => |err| return posix.unexpectedErrno(err),
2055 }
2056 }
2057}
2058
2059fn dirOpenDirHaiku(
2060 userdata: ?*anyopaque,
2061 dir: Io.Dir,
2062 sub_path: []const u8,
2063 options: Io.Dir.OpenOptions,
2064) Io.Dir.OpenError!Io.Dir {
2065 const t: *Threaded = @ptrCast(@alignCast(userdata));
2066
2067 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2068 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
2069
20142070 _ = options;
2015 @panic("TODO");
2071
2072 while (true) {
2073 try t.checkCancel();
2074 const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix);
2075 if (rc >= 0) return .{ .handle = rc };
2076 switch (@as(posix.E, @enumFromInt(rc))) {
2077 .INTR => continue,
2078 .CANCELED => return error.Canceled,
2079 .FAULT => |err| return errnoBug(err),
2080 .INVAL => |err| return errnoBug(err),
2081 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
2082 .ACCES => return error.AccessDenied,
2083 .LOOP => return error.SymLinkLoop,
2084 .MFILE => return error.ProcessFdQuotaExceeded,
2085 .NAMETOOLONG => return error.NameTooLong,
2086 .NFILE => return error.SystemFdQuotaExceeded,
2087 .NODEV => return error.NoDevice,
2088 .NOENT => return error.FileNotFound,
2089 .NOMEM => return error.SystemResources,
2090 .NOTDIR => return error.NotDir,
2091 .PERM => return error.PermissionDenied,
2092 .BUSY => return error.DeviceBusy,
2093 else => |err| return posix.unexpectedErrno(err),
2094 }
2095 }
20162096}
20172097
20182098fn dirOpenDirWindows(
lib/std/fs/Dir.zig+3-90
......@@ -1069,96 +1069,9 @@ pub const OpenOptions = Io.Dir.OpenOptions;
10691069
10701070/// Deprecated in favor of `Io.Dir.openDir`.
10711071pub fn openDir(self: Dir, sub_path: []const u8, args: OpenOptions) OpenError!Dir {
1072 switch (native_os) {
1073 .windows => {
1074 var threaded: Io.Threaded = .init_single_threaded;
1075 const io = threaded.io();
1076 return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
1077 },
1078 .wasi => if (!builtin.link_libc) {
1079 var threaded: Io.Threaded = .init_single_threaded;
1080 const io = threaded.io();
1081 return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
1082 },
1083 else => {},
1084 }
1085 const sub_path_c = try posix.toPosixPath(sub_path);
1086 return self.openDirZ(&sub_path_c, args);
1087}
1088
1089/// Same as `openDir` except the parameter is null-terminated.
1090pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenOptions) OpenError!Dir {
1091 switch (native_os) {
1092 .windows => {
1093 @compileError("use std.Io instead");
1094 },
1095 // Use the libc API when libc is linked because it implements things
1096 // such as opening absolute directory paths.
1097 .wasi => if (!builtin.link_libc) {
1098 return openDir(self, mem.sliceTo(sub_path_c, 0), args);
1099 },
1100 .haiku => {
1101 const rc = posix.system._kern_open_dir(self.fd, sub_path_c);
1102 if (rc >= 0) return .{ .fd = rc };
1103 switch (@as(posix.E, @enumFromInt(rc))) {
1104 .FAULT => unreachable,
1105 .INVAL => unreachable,
1106 .BADF => unreachable,
1107 .ACCES => return error.AccessDenied,
1108 .LOOP => return error.SymLinkLoop,
1109 .MFILE => return error.ProcessFdQuotaExceeded,
1110 .NAMETOOLONG => return error.NameTooLong,
1111 .NFILE => return error.SystemFdQuotaExceeded,
1112 .NODEV => return error.NoDevice,
1113 .NOENT => return error.FileNotFound,
1114 .NOMEM => return error.SystemResources,
1115 .NOTDIR => return error.NotDir,
1116 .PERM => return error.PermissionDenied,
1117 .BUSY => return error.DeviceBusy,
1118 else => |err| return posix.unexpectedErrno(err),
1119 }
1120 },
1121 else => {},
1122 }
1123
1124 var symlink_flags: posix.O = switch (native_os) {
1125 .wasi => .{
1126 .read = true,
1127 .NOFOLLOW = !args.follow_symlinks,
1128 .DIRECTORY = true,
1129 },
1130 else => .{
1131 .ACCMODE = .RDONLY,
1132 .NOFOLLOW = !args.follow_symlinks,
1133 .DIRECTORY = true,
1134 .CLOEXEC = true,
1135 },
1136 };
1137
1138 if (@hasField(posix.O, "PATH") and !args.iterate)
1139 symlink_flags.PATH = true;
1140
1141 return self.openDirFlagsZ(sub_path_c, symlink_flags);
1142}
1143
1144/// Asserts `flags` has `DIRECTORY` set.
1145fn openDirFlagsZ(self: Dir, sub_path_c: [*:0]const u8, flags: posix.O) OpenError!Dir {
1146 assert(flags.DIRECTORY);
1147 const fd = posix.openatZ(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
1148 error.FileTooBig => unreachable, // can't happen for directories
1149 error.IsDir => unreachable, // we're setting DIRECTORY
1150 error.NoSpaceLeft => unreachable, // not setting CREAT
1151 error.PathAlreadyExists => unreachable, // not setting CREAT
1152 error.FileLocksNotSupported => unreachable, // locking folders is not supported
1153 error.WouldBlock => unreachable, // can't happen for directories
1154 error.FileBusy => unreachable, // can't happen for directories
1155 error.SharingViolation => unreachable, // can't happen for directories
1156 error.PipeBusy => unreachable, // can't happen for directories
1157 error.AntivirusInterference => unreachable, // can't happen for directories
1158 error.ProcessNotFound => unreachable, // can't happen for directories
1159 else => |e| return e,
1160 };
1161 return Dir{ .fd = fd };
1072 var threaded: Io.Threaded = .init_single_threaded;
1073 const io = threaded.io();
1074 return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
11621075}
11631076
11641077pub const DeleteFileError = posix.UnlinkError;