authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-16 20:53:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:50-07:00
logf8ea00bd6dccba678901b50904972c017c9e66a1
tree1a5f6f077f1442325896b1f48e827f72a268e9ff
parent3bf0ce65a514e6f86241364c4a11089e32b3ba57

std.Io: add dirAccess


8 files changed, 225 insertions(+), 226 deletions(-)

lib/std/Io.zig+1
......@@ -663,6 +663,7 @@ pub const VTable = struct {
663663 dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void,
664664 dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
665665 dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,
666 dirAccess: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.AccessOptions) Dir.AccessError!void,
666667 dirCreateFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.CreateFlags) File.OpenError!File,
667668 dirOpenFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File,
668669 fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
lib/std/Io/Dir.zig+31
......@@ -23,6 +23,37 @@ pub const PathNameError = error{
2323 BadPathName,
2424};
2525
26pub const AccessError = error{
27 AccessDenied,
28 PermissionDenied,
29 FileNotFound,
30 InputOutput,
31 SystemResources,
32 FileBusy,
33 SymLinkLoop,
34 ReadOnlyFileSystem,
35} || PathNameError || Io.Cancelable || Io.UnexpectedError;
36
37pub const AccessOptions = packed struct {
38 follow_symlinks: bool = true,
39 read: bool = false,
40 write: bool = false,
41 execute: bool = false,
42};
43
44/// Test accessing `sub_path`.
45///
46/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
47/// On WASI, `sub_path` should be encoded as valid UTF-8.
48/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
49///
50/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this
51/// function. For example, instead of testing if a file exists and then opening
52/// it, just open it and handle the error for file not found.
53pub fn access(dir: Dir, io: Io, sub_path: []const u8, options: AccessOptions) AccessError!void {
54 return io.vtable.dirAccess(io.userdata, dir, sub_path, options);
55}
56
2657pub const OpenError = error{
2758 FileNotFound,
2859 NotDir,
lib/std/Io/File.zig+61-1
......@@ -80,7 +80,67 @@ pub fn stat(file: File, io: Io) StatError!Stat {
8080 return io.vtable.fileStat(io.userdata, file);
8181}
8282
83pub const OpenFlags = std.fs.File.OpenFlags;
83pub const OpenMode = enum {
84 read_only,
85 write_only,
86 read_write,
87};
88
89pub const Lock = enum {
90 none,
91 shared,
92 exclusive,
93};
94
95pub const OpenFlags = struct {
96 mode: OpenMode = .read_only,
97
98 /// Open the file with an advisory lock to coordinate with other processes
99 /// accessing it at the same time. An exclusive lock will prevent other
100 /// processes from acquiring a lock. A shared lock will prevent other
101 /// processes from acquiring a exclusive lock, but does not prevent
102 /// other process from getting their own shared locks.
103 ///
104 /// The lock is advisory, except on Linux in very specific circumstances[1].
105 /// This means that a process that does not respect the locking API can still get access
106 /// to the file, despite the lock.
107 ///
108 /// On these operating systems, the lock is acquired atomically with
109 /// opening the file:
110 /// * Darwin
111 /// * DragonFlyBSD
112 /// * FreeBSD
113 /// * Haiku
114 /// * NetBSD
115 /// * OpenBSD
116 /// On these operating systems, the lock is acquired via a separate syscall
117 /// after opening the file:
118 /// * Linux
119 /// * Windows
120 ///
121 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
122 lock: Lock = .none,
123
124 /// Sets whether or not to wait until the file is locked to return. If set to true,
125 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
126 /// is available to proceed.
127 lock_nonblocking: bool = false,
128
129 /// Set this to allow the opened file to automatically become the
130 /// controlling TTY for the current process.
131 allow_ctty: bool = false,
132
133 follow_symlinks: bool = true,
134
135 pub fn isRead(self: OpenFlags) bool {
136 return self.mode != .write_only;
137 }
138
139 pub fn isWrite(self: OpenFlags) bool {
140 return self.mode != .read_only;
141 }
142};
143
84144pub const CreateFlags = std.fs.File.CreateFlags;
85145
86146pub const OpenError = error{
lib/std/Io/Threaded.zig+113-5
......@@ -183,6 +183,11 @@ pub fn io(t: *Threaded) Io {
183183 .wasi => fileStatWasi,
184184 else => fileStatPosix,
185185 },
186 .dirAccess = switch (builtin.os.tag) {
187 .windows => @panic("TODO"),
188 .wasi => dirAccessWasi,
189 else => dirAccessPosix,
190 },
186191 .dirCreateFile = switch (builtin.os.tag) {
187192 .windows => @panic("TODO"),
188193 .wasi => @panic("TODO"),
......@@ -992,7 +997,6 @@ fn dirStatPathWasi(
992997) Io.Dir.StatPathError!Io.File.Stat {
993998 if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options);
994999 const t: *Threaded = @ptrCast(@alignCast(userdata));
995 const dir_fd = dir.handle;
9961000 const wasi = std.os.wasi;
9971001 const flags: wasi.lookupflags_t = .{
9981002 .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
......@@ -1000,16 +1004,16 @@ fn dirStatPathWasi(
10001004 var stat: wasi.filestat_t = undefined;
10011005 while (true) {
10021006 try t.checkCancel();
1003 switch (wasi.path_filestat_get(dir_fd, flags, sub_path.ptr, sub_path.len, &stat)) {
1007 switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
10041008 .SUCCESS => return statFromWasi(stat),
10051009 .INTR => continue,
10061010 .CANCELED => return error.Canceled,
10071011
1008 .INVAL => |err| errnoBug(err),
1009 .BADF => |err| errnoBug(err), // Always a race condition.
1012 .INVAL => |err| return errnoBug(err),
1013 .BADF => |err| return errnoBug(err), // Always a race condition.
10101014 .NOMEM => return error.SystemResources,
10111015 .ACCES => return error.AccessDenied,
1012 .FAULT => |err| errnoBug(err),
1016 .FAULT => |err| return errnoBug(err),
10131017 .NAMETOOLONG => return error.NameTooLong,
10141018 .NOENT => return error.FileNotFound,
10151019 .NOTDIR => return error.FileNotFound,
......@@ -1103,6 +1107,110 @@ const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.syste
11031107const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek;
11041108const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
11051109
1110fn dirAccessPosix(
1111 userdata: ?*anyopaque,
1112 dir: Io.Dir,
1113 sub_path: []const u8,
1114 options: Io.Dir.AccessOptions,
1115) Io.Dir.AccessError!void {
1116 const t: *Threaded = @ptrCast(@alignCast(userdata));
1117
1118 var path_buffer: [posix.PATH_MAX]u8 = undefined;
1119 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
1120
1121 const flags: u32 = @as(u32, if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0);
1122
1123 const mode: u32 =
1124 @as(u32, if (options.read) posix.R_OK else 0) |
1125 @as(u32, if (options.write) posix.W_OK else 0) |
1126 @as(u32, if (options.execute) posix.X_OK else 0);
1127
1128 while (true) {
1129 try t.checkCancel();
1130 switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) {
1131 .SUCCESS => return,
1132 .INTR => continue,
1133 .CANCELED => return error.Canceled,
1134
1135 .ACCES => return error.AccessDenied,
1136 .PERM => return error.PermissionDenied,
1137 .ROFS => return error.ReadOnlyFileSystem,
1138 .LOOP => return error.SymLinkLoop,
1139 .TXTBSY => return error.FileBusy,
1140 .NOTDIR => return error.FileNotFound,
1141 .NOENT => return error.FileNotFound,
1142 .NAMETOOLONG => return error.NameTooLong,
1143 .INVAL => |err| return errnoBug(err),
1144 .FAULT => |err| return errnoBug(err),
1145 .IO => return error.InputOutput,
1146 .NOMEM => return error.SystemResources,
1147 .ILSEQ => return error.BadPathName, // TODO move to wasi
1148 else => |err| return posix.unexpectedErrno(err),
1149 }
1150 }
1151}
1152
1153fn dirAccessWasi(
1154 userdata: ?*anyopaque,
1155 dir: Io.Dir,
1156 sub_path: []const u8,
1157 options: Io.File.OpenFlags,
1158) Io.File.AccessError!void {
1159 if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);
1160 const t: *Threaded = @ptrCast(@alignCast(userdata));
1161 const wasi = std.os.wasi;
1162 const flags: wasi.lookupflags_t = .{
1163 .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
1164 };
1165 const stat = while (true) {
1166 var stat: wasi.filestat_t = undefined;
1167 try t.checkCancel();
1168 switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
1169 .SUCCESS => break statFromWasi(stat),
1170 .INTR => continue,
1171 .CANCELED => return error.Canceled,
1172
1173 .INVAL => |err| return errnoBug(err),
1174 .BADF => |err| return errnoBug(err), // Always a race condition.
1175 .NOMEM => return error.SystemResources,
1176 .ACCES => return error.AccessDenied,
1177 .FAULT => |err| return errnoBug(err),
1178 .NAMETOOLONG => return error.NameTooLong,
1179 .NOENT => return error.FileNotFound,
1180 .NOTDIR => return error.FileNotFound,
1181 .NOTCAPABLE => return error.AccessDenied,
1182 .ILSEQ => return error.BadPathName,
1183 else => |err| return posix.unexpectedErrno(err),
1184 }
1185 };
1186
1187 if (!options.mode.read and !options.mode.write and !options.mode.execute)
1188 return;
1189
1190 var directory: wasi.fdstat_t = undefined;
1191 if (wasi.fd_fdstat_get(dir.handle, &directory) != .SUCCESS)
1192 return error.AccessDenied;
1193
1194 var rights: wasi.rights_t = .{};
1195 if (options.mode.read) {
1196 if (stat.filetype == .DIRECTORY) {
1197 rights.FD_READDIR = true;
1198 } else {
1199 rights.FD_READ = true;
1200 }
1201 }
1202 if (options.mode.write)
1203 rights.FD_WRITE = true;
1204
1205 // No validation for execution.
1206
1207 // https://github.com/ziglang/zig/issues/18882
1208 const rights_int: u64 = @bitCast(rights);
1209 const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
1210 if ((rights_int & inheriting_int) != rights_int)
1211 return error.AccessDenied;
1212}
1213
11061214fn dirCreateFilePosix(
11071215 userdata: ?*anyopaque,
11081216 dir: Io.Dir,
lib/std/fs.zig+4-3
......@@ -1,14 +1,15 @@
11//! File System.
2const builtin = @import("builtin");
3const native_os = builtin.os.tag;
24
35const std = @import("std.zig");
4const builtin = @import("builtin");
6const Io = std.Io;
57const root = @import("root");
68const mem = std.mem;
79const base64 = std.base64;
810const crypto = std.crypto;
911const Allocator = std.mem.Allocator;
1012const assert = std.debug.assert;
11const native_os = builtin.os.tag;
1213const posix = std.posix;
1314const windows = std.os.windows;
1415
......@@ -274,7 +275,7 @@ pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) Fi
274275/// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
275276/// On WASI, `absolute_path` should be encoded as valid UTF-8.
276277/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
277pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void {
278pub fn accessAbsolute(absolute_path: []const u8, flags: Io.Dir.AccessOptions) Dir.AccessError!void {
278279 assert(path.isAbsolute(absolute_path));
279280 try cwd().access(absolute_path, flags);
280281}
lib/std/fs/Dir.zig+7-40
......@@ -2353,47 +2353,14 @@ pub fn writeFile(self: Dir, options: WriteFileOptions) WriteFileError!void {
23532353 try file.writeAll(options.data);
23542354}
23552355
2356pub const AccessError = posix.AccessError;
2356/// Deprecated in favor of `Io.Dir.AccessError`.
2357pub const AccessError = Io.Dir.AccessError;
23572358
2358/// Test accessing `sub_path`.
2359/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
2360/// On WASI, `sub_path` should be encoded as valid UTF-8.
2361/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
2362/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function.
2363/// For example, instead of testing if a file exists and then opening it, just
2364/// open it and handle the error for file not found.
2365pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
2366 if (native_os == .windows) {
2367 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
2368 return self.accessW(sub_path_w.span().ptr, flags);
2369 }
2370 const path_c = try posix.toPosixPath(sub_path);
2371 return self.accessZ(&path_c, flags);
2372}
2373
2374/// Same as `access` except the path parameter is null-terminated.
2375pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
2376 if (native_os == .windows) {
2377 const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path);
2378 return self.accessW(sub_path_w.span().ptr, flags);
2379 }
2380 const os_mode = switch (flags.mode) {
2381 .read_only => @as(u32, posix.F_OK),
2382 .write_only => @as(u32, posix.W_OK),
2383 .read_write => @as(u32, posix.R_OK | posix.W_OK),
2384 };
2385 const result = posix.faccessatZ(self.fd, sub_path, os_mode, 0);
2386 return result;
2387}
2388
2389/// Same as `access` except asserts the target OS is Windows and the path parameter is
2390/// * WTF-16 LE encoded
2391/// * null-terminated
2392/// * relative or has the NT namespace prefix
2393/// TODO currently this ignores `flags`.
2394pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
2395 _ = flags;
2396 return posix.faccessatW(self.fd, sub_path_w);
2359/// Deprecated in favor of `Io.Dir.access`.
2360pub fn access(self: Dir, sub_path: []const u8, options: Io.Dir.AccessOptions) AccessError!void {
2361 var threaded: Io.Threaded = .init_single_threaded;
2362 const io = threaded.io();
2363 return Io.Dir.access(self.adaptToNewApi(), io, sub_path, options);
23972364}
23982365
23992366pub const CopyFileOptions = struct {
lib/std/fs/File.zig+6-59
......@@ -40,65 +40,12 @@ pub const default_mode = switch (builtin.os.tag) {
4040
4141/// Deprecated in favor of `Io.File.OpenError`.
4242pub const OpenError = Io.File.OpenError || error{WouldBlock};
43
44pub const OpenMode = enum {
45 read_only,
46 write_only,
47 read_write,
48};
49
50pub const Lock = enum {
51 none,
52 shared,
53 exclusive,
54};
55
56pub const OpenFlags = struct {
57 mode: OpenMode = .read_only,
58
59 /// Open the file with an advisory lock to coordinate with other processes
60 /// accessing it at the same time. An exclusive lock will prevent other
61 /// processes from acquiring a lock. A shared lock will prevent other
62 /// processes from acquiring a exclusive lock, but does not prevent
63 /// other process from getting their own shared locks.
64 ///
65 /// The lock is advisory, except on Linux in very specific circumstances[1].
66 /// This means that a process that does not respect the locking API can still get access
67 /// to the file, despite the lock.
68 ///
69 /// On these operating systems, the lock is acquired atomically with
70 /// opening the file:
71 /// * Darwin
72 /// * DragonFlyBSD
73 /// * FreeBSD
74 /// * Haiku
75 /// * NetBSD
76 /// * OpenBSD
77 /// On these operating systems, the lock is acquired via a separate syscall
78 /// after opening the file:
79 /// * Linux
80 /// * Windows
81 ///
82 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
83 lock: Lock = .none,
84
85 /// Sets whether or not to wait until the file is locked to return. If set to true,
86 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
87 /// is available to proceed.
88 lock_nonblocking: bool = false,
89
90 /// Set this to allow the opened file to automatically become the
91 /// controlling TTY for the current process.
92 allow_ctty: bool = false,
93
94 pub fn isRead(self: OpenFlags) bool {
95 return self.mode != .write_only;
96 }
97
98 pub fn isWrite(self: OpenFlags) bool {
99 return self.mode != .read_only;
100 }
101};
43/// Deprecated in favor of `Io.File.OpenMode`.
44pub const OpenMode = Io.File.OpenMode;
45/// Deprecated in favor of `Io.File.Lock`.
46pub const Lock = Io.File.Lock;
47/// Deprecated in favor of `Io.File.OpenFlags`.
48pub const OpenFlags = Io.File.OpenFlags;
10249
10350pub const CreateFlags = struct {
10451 /// Whether the file will be created with read access.
lib/std/posix.zig+2-118
......@@ -4360,8 +4360,7 @@ pub const FStatAtError = FStatError || error{
43604360 NameTooLong,
43614361 FileNotFound,
43624362 SymLinkLoop,
4363 /// WASI-only; file paths must be valid UTF-8.
4364 InvalidUtf8,
4363 BadPathName,
43654364};
43664365
43674366/// 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 {
49004899 _ = try windows.GetFileAttributesW(path_w.span().ptr);
49014900 return;
49024901 } else if (native_os == .wasi and !builtin.link_libc) {
4903 return faccessat(AT.FDCWD, path, mode, 0);
4902 @compileError("wasi doesn't support absolute paths");
49044903 }
49054904 const path_c = try toPosixPath(path);
49064905 return accessZ(&path_c, mode);
......@@ -4934,121 +4933,6 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
49344933 }
49354934}
49364935
4937/// Check user's permissions for a file, based on an open directory handle.
4938///
4939/// * On Windows, asserts `path` is valid [WTF-8](https://wtf-8.codeberg.page/).
4940/// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`.
4941/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
4942///
4943/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by
4944/// Windows. See `fs` for the cross-platform file system API.
4945pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
4946 if (native_os == .windows) {
4947 const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
4948 return faccessatW(dirfd, path_w.span().ptr);
4949 } else if (native_os == .wasi and !builtin.link_libc) {
4950 const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path };
4951
4952 const st = try std.os.fstatat_wasi(dirfd, path, .{
4953 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4954 });
4955
4956 if (mode != F_OK) {
4957 var directory: wasi.fdstat_t = undefined;
4958 if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
4959 return error.AccessDenied;
4960 }
4961
4962 var rights: wasi.rights_t = .{};
4963 if (mode & R_OK != 0) {
4964 if (st.filetype == .DIRECTORY) {
4965 rights.FD_READDIR = true;
4966 } else {
4967 rights.FD_READ = true;
4968 }
4969 }
4970 if (mode & W_OK != 0) {
4971 rights.FD_WRITE = true;
4972 }
4973 // No validation for X_OK
4974
4975 // https://github.com/ziglang/zig/issues/18882
4976 const rights_int: u64 = @bitCast(rights);
4977 const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
4978 if ((rights_int & inheriting_int) != rights_int) {
4979 return error.AccessDenied;
4980 }
4981 }
4982 return;
4983 }
4984 const path_c = try toPosixPath(path);
4985 return faccessatZ(dirfd, &path_c, mode, flags);
4986}
4987
4988/// Same as `faccessat` except the path parameter is null-terminated.
4989pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
4990 if (native_os == .windows) {
4991 const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
4992 return faccessatW(dirfd, path_w.span().ptr);
4993 } else if (native_os == .wasi and !builtin.link_libc) {
4994 return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
4995 }
4996 switch (errno(system.faccessat(dirfd, path, mode, flags))) {
4997 .SUCCESS => return,
4998 .ACCES => return error.AccessDenied,
4999 .PERM => return error.PermissionDenied,
5000 .ROFS => return error.ReadOnlyFileSystem,
5001 .LOOP => return error.SymLinkLoop,
5002 .TXTBSY => return error.FileBusy,
5003 .NOTDIR => return error.FileNotFound,
5004 .NOENT => return error.FileNotFound,
5005 .NAMETOOLONG => return error.NameTooLong,
5006 .INVAL => unreachable,
5007 .FAULT => unreachable,
5008 .IO => return error.InputOutput,
5009 .NOMEM => return error.SystemResources,
5010 .ILSEQ => return error.BadPathName,
5011 else => |err| return unexpectedErrno(err),
5012 }
5013}
5014
5015/// Same as `faccessat` except asserts the target is Windows and the path parameter
5016/// is NtDll-prefixed, null-terminated, WTF-16 encoded.
5017pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16) AccessError!void {
5018 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
5019 return;
5020 }
5021 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
5022 return;
5023 }
5024
5025 const path_len_bytes = cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
5026 var nt_name = windows.UNICODE_STRING{
5027 .Length = path_len_bytes,
5028 .MaximumLength = path_len_bytes,
5029 .Buffer = @constCast(sub_path_w),
5030 };
5031 var attr = windows.OBJECT_ATTRIBUTES{
5032 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
5033 .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
5034 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
5035 .ObjectName = &nt_name,
5036 .SecurityDescriptor = null,
5037 .SecurityQualityOfService = null,
5038 };
5039 var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
5040 switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
5041 .SUCCESS => return,
5042 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
5043 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
5044 .OBJECT_NAME_INVALID => unreachable,
5045 .INVALID_PARAMETER => unreachable,
5046 .ACCESS_DENIED => return error.AccessDenied,
5047 .OBJECT_PATH_SYNTAX_BAD => unreachable,
5048 else => |rc| return windows.unexpectedStatus(rc),
5049 }
5050}
5051
50524936pub const PipeError = error{
50534937 SystemFdQuotaExceeded,
50544938 ProcessFdQuotaExceeded,