authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-19 21:12:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:50-07:00
logaa6e8eff40bdf35c838c8cd93080f2e9d4fad3b2
tree3d39b4b01c9d6af60dc8a83f5950535ddecfc5a7
parent482343f2e253f2078627e696fb98ff0e1d8e82df

std.Io.Threaded: implement dirAccess for Windows


6 files changed, 66 insertions(+), 14 deletions(-)

lib/std/Io/Threaded.zig+48-3
...@@ -182,7 +182,7 @@ pub fn io(t: *Threaded) Io {...@@ -182,7 +182,7 @@ pub fn io(t: *Threaded) Io {
182 else => fileStatPosix,182 else => fileStatPosix,
183 },183 },
184 .dirAccess = switch (builtin.os.tag) {184 .dirAccess = switch (builtin.os.tag) {
185 .windows => @panic("TODO"),185 .windows => dirAccessWindows,
186 .wasi => dirAccessWasi,186 .wasi => dirAccessWasi,
187 else => dirAccessPosix,187 else => dirAccessPosix,
188 },188 },
...@@ -1315,6 +1315,51 @@ fn dirAccessWasi(...@@ -1315,6 +1315,51 @@ fn dirAccessWasi(
1315 return error.AccessDenied;1315 return error.AccessDenied;
1316}1316}
13171317
1318fn dirAccessWindows(
1319 userdata: ?*anyopaque,
1320 dir: Io.Dir,
1321 sub_path: []const u8,
1322 options: Io.Dir.AccessOptions,
1323) Io.Dir.AccessError!void {
1324 const t: *Threaded = @ptrCast(@alignCast(userdata));
1325 try t.checkCancel();
1326
1327 _ = options; // TODO
1328
1329 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
1330 const sub_path_w = sub_path_w_array.span();
1331
1332 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) return;
1333 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) return;
1334
1335 const path_len_bytes = std.math.cast(u16, std.mem.sliceTo(sub_path_w, 0).len * 2) orelse
1336 return error.NameTooLong;
1337 var nt_name: windows.UNICODE_STRING = .{
1338 .Length = path_len_bytes,
1339 .MaximumLength = path_len_bytes,
1340 .Buffer = @constCast(sub_path_w.ptr),
1341 };
1342 var attr = windows.OBJECT_ATTRIBUTES{
1343 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
1344 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
1345 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
1346 .ObjectName = &nt_name,
1347 .SecurityDescriptor = null,
1348 .SecurityQualityOfService = null,
1349 };
1350 var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
1351 switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
1352 .SUCCESS => return,
1353 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1354 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
1355 .OBJECT_NAME_INVALID => |err| return windows.statusBug(err),
1356 .INVALID_PARAMETER => |err| return windows.statusBug(err),
1357 .ACCESS_DENIED => return error.AccessDenied,
1358 .OBJECT_PATH_SYNTAX_BAD => |err| return windows.statusBug(err),
1359 else => |rc| return windows.unexpectedStatus(rc),
1360 }
1361}
1362
1318fn dirCreateFilePosix(1363fn dirCreateFilePosix(
1319 userdata: ?*anyopaque,1364 userdata: ?*anyopaque,
1320 dir: Io.Dir,1365 dir: Io.Dir,
...@@ -1818,7 +1863,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File...@@ -1818,7 +1863,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
1818 var n: DWORD = undefined;1863 var n: DWORD = undefined;
1819 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) == 0) {1864 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) == 0) {
1820 switch (windows.GetLastError()) {1865 switch (windows.GetLastError()) {
1821 .IO_PENDING => unreachable,1866 .IO_PENDING => |err| return windows.statusBug(err),
1822 .OPERATION_ABORTED => continue,1867 .OPERATION_ABORTED => continue,
1823 .BROKEN_PIPE => return 0,1868 .BROKEN_PIPE => return 0,
1824 .HANDLE_EOF => return 0,1869 .HANDLE_EOF => return 0,
...@@ -1935,7 +1980,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset...@@ -1935,7 +1980,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
1935 } else null;1980 } else null;
1936 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, overlapped) == 0) {1981 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, overlapped) == 0) {
1937 switch (windows.GetLastError()) {1982 switch (windows.GetLastError()) {
1938 .IO_PENDING => unreachable,1983 .IO_PENDING => |err| return windows.statusBug(err),
1939 .OPERATION_ABORTED => continue,1984 .OPERATION_ABORTED => continue,
1940 .BROKEN_PIPE => return 0,1985 .BROKEN_PIPE => return 0,
1941 .HANDLE_EOF => return 0,1986 .HANDLE_EOF => return 0,
lib/std/fs.zig+1-1
...@@ -263,7 +263,7 @@ pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.O...@@ -263,7 +263,7 @@ pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.O
263263
264/// Same as `openFileAbsolute` but the path parameter is WTF-16-encoded.264/// Same as `openFileAbsolute` but the path parameter is WTF-16-encoded.
265pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File {265pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File {
266 assert(path.isAbsoluteWindowsWTF16(absolute_path_w));266 assert(path.isAbsoluteWindowsWtf16(absolute_path_w));
267 return cwd().openFileW(absolute_path_w, flags);267 return cwd().openFileW(absolute_path_w, flags);
268}268}
269269
lib/std/fs/path.zig+1-1
...@@ -313,7 +313,7 @@ pub fn isAbsoluteWindowsW(path_w: [*:0]const u16) bool {...@@ -313,7 +313,7 @@ pub fn isAbsoluteWindowsW(path_w: [*:0]const u16) bool {
313 return isAbsoluteWindowsImpl(u16, mem.sliceTo(path_w, 0));313 return isAbsoluteWindowsImpl(u16, mem.sliceTo(path_w, 0));
314}314}
315315
316pub fn isAbsoluteWindowsWTF16(path: []const u16) bool {316pub fn isAbsoluteWindowsWtf16(path: []const u16) bool {
317 return isAbsoluteWindowsImpl(u16, path);317 return isAbsoluteWindowsImpl(u16, path);
318}318}
319319
lib/std/os/windows.zig+13-6
...@@ -89,7 +89,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -89,7 +89,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
89 };89 };
90 var attr = OBJECT_ATTRIBUTES{90 var attr = OBJECT_ATTRIBUTES{
91 .Length = @sizeOf(OBJECT_ATTRIBUTES),91 .Length = @sizeOf(OBJECT_ATTRIBUTES),
92 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,92 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
93 .Attributes = if (options.sa) |ptr| blk: { // Note we do not use OBJ_CASE_INSENSITIVE here.93 .Attributes = if (options.sa) |ptr| blk: { // Note we do not use OBJ_CASE_INSENSITIVE here.
94 const inherit: ULONG = if (ptr.bInheritHandle == TRUE) OBJ_INHERIT else 0;94 const inherit: ULONG = if (ptr.bInheritHandle == TRUE) OBJ_INHERIT else 0;
95 break :blk inherit;95 break :blk inherit;
...@@ -847,7 +847,7 @@ pub fn CreateSymbolicLink(...@@ -847,7 +847,7 @@ pub fn CreateSymbolicLink(
847 // the C:\ drive.847 // the C:\ drive.
848 .rooted => break :target_path target_path,848 .rooted => break :target_path target_path,
849 // Keep relative paths relative, but anything else needs to get NT-prefixed.849 // Keep relative paths relative, but anything else needs to get NT-prefixed.
850 else => if (!std.fs.path.isAbsoluteWindowsWTF16(target_path))850 else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path))
851 break :target_path target_path,851 break :target_path target_path,
852 },852 },
853 // Already an NT path, no need to do anything to it853 // Already an NT path, no need to do anything to it
...@@ -856,7 +856,7 @@ pub fn CreateSymbolicLink(...@@ -856,7 +856,7 @@ pub fn CreateSymbolicLink(
856 }856 }
857 var prefixed_target_path = try wToPrefixedFileW(dir, target_path);857 var prefixed_target_path = try wToPrefixedFileW(dir, target_path);
858 // We do this after prefixing to ensure that drive-relative paths are treated as absolute858 // We do this after prefixing to ensure that drive-relative paths are treated as absolute
859 is_target_absolute = std.fs.path.isAbsoluteWindowsWTF16(prefixed_target_path.span());859 is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
860 break :target_path prefixed_target_path.span();860 break :target_path prefixed_target_path.span();
861 };861 };
862862
...@@ -864,7 +864,7 @@ pub fn CreateSymbolicLink(...@@ -864,7 +864,7 @@ pub fn CreateSymbolicLink(
864 var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;864 var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
865 const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;865 const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
866 const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2;866 const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2;
867 const target_is_absolute = std.fs.path.isAbsoluteWindowsWTF16(final_target_path);867 const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path);
868 const symlink_data = SYMLINK_DATA{868 const symlink_data = SYMLINK_DATA{
869 .ReparseTag = IO_REPARSE_TAG_SYMLINK,869 .ReparseTag = IO_REPARSE_TAG_SYMLINK,
870 .ReparseDataLength = @intCast(buf_len - header_len),870 .ReparseDataLength = @intCast(buf_len - header_len),
...@@ -905,7 +905,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin...@@ -905,7 +905,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
905 };905 };
906 var attr = OBJECT_ATTRIBUTES{906 var attr = OBJECT_ATTRIBUTES{
907 .Length = @sizeOf(OBJECT_ATTRIBUTES),907 .Length = @sizeOf(OBJECT_ATTRIBUTES),
908 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else dir,908 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir,
909 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.909 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
910 .ObjectName = &nt_name,910 .ObjectName = &nt_name,
911 .SecurityDescriptor = null,911 .SecurityDescriptor = null,
...@@ -1035,7 +1035,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -1035,7 +1035,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
10351035
1036 var attr = OBJECT_ATTRIBUTES{1036 var attr = OBJECT_ATTRIBUTES{
1037 .Length = @sizeOf(OBJECT_ATTRIBUTES),1037 .Length = @sizeOf(OBJECT_ATTRIBUTES),
1038 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,1038 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
1039 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.1039 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
1040 .ObjectName = &nt_name,1040 .ObjectName = &nt_name,
1041 .SecurityDescriptor = null,1041 .SecurityDescriptor = null,
...@@ -2885,6 +2885,13 @@ pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError {...@@ -2885,6 +2885,13 @@ pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError {
2885 return error.Unexpected;2885 return error.Unexpected;
2886}2886}
28872887
2888pub fn statusBug(status: NTSTATUS) UnexpectedError {
2889 switch (builtin.mode) {
2890 .Debug => std.debug.panic("programmer bug caused syscall status: {t}", .{status}),
2891 else => return error.Unexpected,
2892 }
2893}
2894
2888pub const Win32Error = @import("windows/win32error.zig").Win32Error;2895pub const Win32Error = @import("windows/win32error.zig").Win32Error;
2889pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS;2896pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS;
2890pub const LANG = @import("windows/lang.zig");2897pub const LANG = @import("windows/lang.zig");
lib/std/posix.zig+2-2
...@@ -2617,7 +2617,7 @@ pub fn renameatW(...@@ -2617,7 +2617,7 @@ pub fn renameatW(
2617 if (ReplaceIfExists == windows.TRUE) flags |= windows.FILE_RENAME_REPLACE_IF_EXISTS;2617 if (ReplaceIfExists == windows.TRUE) flags |= windows.FILE_RENAME_REPLACE_IF_EXISTS;
2618 rename_info.* = .{2618 rename_info.* = .{
2619 .Flags = flags,2619 .Flags = flags,
2620 .RootDirectory = if (fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,2620 .RootDirectory = if (fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
2621 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong2621 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2622 .FileName = undefined,2622 .FileName = undefined,
2623 };2623 };
...@@ -2654,7 +2654,7 @@ pub fn renameatW(...@@ -2654,7 +2654,7 @@ pub fn renameatW(
26542654
2655 rename_info.* = .{2655 rename_info.* = .{
2656 .Flags = ReplaceIfExists,2656 .Flags = ReplaceIfExists,
2657 .RootDirectory = if (fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,2657 .RootDirectory = if (fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
2658 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong2658 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2659 .FileName = undefined,2659 .FileName = undefined,
2660 };2660 };
lib/std/start.zig+1-1
...@@ -708,7 +708,7 @@ pub inline fn callMain() u8 {...@@ -708,7 +708,7 @@ pub inline fn callMain() u8 {
708 switch (native_os) {708 switch (native_os) {
709 .freestanding, .other => {},709 .freestanding, .other => {},
710 else => if (@errorReturnTrace()) |trace| {710 else => if (@errorReturnTrace()) |trace| {
711 std.debug.dumpStackTrace(trace);711 std.debug.dumpStackTrace(trace.*);
712 },712 },
713 }713 }
714 return 1;714 return 1;