authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-23 09:40:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:51-07:00
log5c527a18544a16c86c3036c143d44b7ed427d2ec
treea5e25e22857cdb221d586fb3b7a7639847bd41cd
parent5d7672f2adc8f1058aa1f49f80055b8816a06371

std.Io.Threaded: implement fileStat for Windows


2 files changed, 62 insertions(+), 66 deletions(-)

lib/std/Io/Threaded.zig+62-22
...@@ -1033,7 +1033,7 @@ fn dirMakePathPosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mo...@@ -1033,7 +1033,7 @@ fn dirMakePathPosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mo
1033 _ = dir;1033 _ = dir;
1034 _ = sub_path;1034 _ = sub_path;
1035 _ = mode;1035 _ = mode;
1036 @panic("TODO");1036 @panic("TODO implement dirMakePathPosix");
1037}1037}
10381038
1039fn dirMakePathWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {1039fn dirMakePathWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {
...@@ -1042,7 +1042,7 @@ fn dirMakePathWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8,...@@ -1042,7 +1042,7 @@ fn dirMakePathWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8,
1042 _ = dir;1042 _ = dir;
1043 _ = sub_path;1043 _ = sub_path;
1044 _ = mode;1044 _ = mode;
1045 @panic("TODO");1045 @panic("TODO implement dirMakePathWindows");
1046}1046}
10471047
1048fn dirMakeOpenPathPosix(1048fn dirMakeOpenPathPosix(
...@@ -1176,7 +1176,7 @@ fn dirMakeOpenPathWasi(...@@ -1176,7 +1176,7 @@ fn dirMakeOpenPathWasi(
1176 _ = dir;1176 _ = dir;
1177 _ = sub_path;1177 _ = sub_path;
1178 _ = mode;1178 _ = mode;
1179 @panic("TODO");1179 @panic("TODO implement dirMakeOpenPathWindows");
1180}1180}
11811181
1182fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {1182fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {
...@@ -1184,7 +1184,7 @@ fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {...@@ -1184,7 +1184,7 @@ fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {
1184 try t.checkCancel();1184 try t.checkCancel();
11851185
1186 _ = dir;1186 _ = dir;
1187 @panic("TODO");1187 @panic("TODO implement dirStat");
1188}1188}
11891189
1190fn dirStatPathLinux(1190fn dirStatPathLinux(
...@@ -1375,8 +1375,48 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File...@@ -1375,8 +1375,48 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File
1375fn fileStatWindows(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {1375fn fileStatWindows(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
1376 const t: *Threaded = @ptrCast(@alignCast(userdata));1376 const t: *Threaded = @ptrCast(@alignCast(userdata));
1377 try t.checkCancel();1377 try t.checkCancel();
1378 _ = file;1378
1379 @panic("TODO");1379 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1380 var info: windows.FILE_ALL_INFORMATION = undefined;
1381 const rc = windows.ntdll.NtQueryInformationFile(file.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
1382 switch (rc) {
1383 .SUCCESS => {},
1384 // Buffer overflow here indicates that there is more information available than was able to be stored in the buffer
1385 // size provided. This is treated as success because the type of variable-length information that this would be relevant for
1386 // (name, volume name, etc) we don't care about.
1387 .BUFFER_OVERFLOW => {},
1388 .INVALID_PARAMETER => unreachable,
1389 .ACCESS_DENIED => return error.AccessDenied,
1390 else => return windows.unexpectedStatus(rc),
1391 }
1392 return .{
1393 .inode = info.InternalInformation.IndexNumber,
1394 .size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)),
1395 .mode = 0,
1396 .kind = if (info.BasicInformation.FileAttributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) reparse_point: {
1397 var tag_info: windows.FILE_ATTRIBUTE_TAG_INFO = undefined;
1398 const tag_rc = windows.ntdll.NtQueryInformationFile(file.handle, &io_status_block, &tag_info, @sizeOf(windows.FILE_ATTRIBUTE_TAG_INFO), .FileAttributeTagInformation);
1399 switch (tag_rc) {
1400 .SUCCESS => {},
1401 // INFO_LENGTH_MISMATCH and ACCESS_DENIED are the only documented possible errors
1402 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e
1403 .INFO_LENGTH_MISMATCH => unreachable,
1404 .ACCESS_DENIED => return error.AccessDenied,
1405 else => return windows.unexpectedStatus(rc),
1406 }
1407 if (tag_info.ReparseTag & windows.reparse_tag_name_surrogate_bit != 0) {
1408 break :reparse_point .sym_link;
1409 }
1410 // Unknown reparse point
1411 break :reparse_point .unknown;
1412 } else if (info.BasicInformation.FileAttributes & windows.FILE_ATTRIBUTE_DIRECTORY != 0)
1413 .directory
1414 else
1415 .file,
1416 .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
1417 .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
1418 .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime),
1419 };
1380}1420}
13811421
1382fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {1422fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
...@@ -2490,7 +2530,7 @@ fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekErr...@@ -2490,7 +2530,7 @@ fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekErr
24902530
2491 _ = file;2531 _ = file;
2492 _ = offset;2532 _ = offset;
2493 @panic("TODO");2533 @panic("TODO implement fileSeekBy");
2494}2534}
24952535
2496fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekError!void {2536fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekError!void {
...@@ -2571,7 +2611,7 @@ fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelf...@@ -2571,7 +2611,7 @@ fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelf
25712611
2572 return dirOpenFileWindowsInner(t, .{ .handle = cwd_handle }, image_path_name, flags);2612 return dirOpenFileWindowsInner(t, .{ .handle = cwd_handle }, image_path_name, flags);
2573 }2613 }
2574 @panic("TODO");2614 @panic("TODO implement openSelfExe");
2575}2615}
25762616
2577fn fileWritePositional(2617fn fileWritePositional(
...@@ -2586,7 +2626,7 @@ fn fileWritePositional(...@@ -2586,7 +2626,7 @@ fn fileWritePositional(
2586 _ = file;2626 _ = file;
2587 _ = buffer;2627 _ = buffer;
2588 _ = offset;2628 _ = offset;
2589 @panic("TODO");2629 @panic("TODO implement fileWritePositional");
2590 }2630 }
2591}2631}
25922632
...@@ -2596,7 +2636,7 @@ fn fileWriteStreaming(userdata: ?*anyopaque, file: Io.File, buffer: [][]const u8...@@ -2596,7 +2636,7 @@ fn fileWriteStreaming(userdata: ?*anyopaque, file: Io.File, buffer: [][]const u8
2596 try t.checkCancel();2636 try t.checkCancel();
2597 _ = file;2637 _ = file;
2598 _ = buffer;2638 _ = buffer;
2599 @panic("TODO");2639 @panic("TODO implement fileWriteStreaming");
2600 }2640 }
2601}2641}
26022642
...@@ -2922,7 +2962,7 @@ fn netListenUnixWindows(...@@ -2922,7 +2962,7 @@ fn netListenUnixWindows(
2922 try t.checkCancel();2962 try t.checkCancel();
2923 _ = address;2963 _ = address;
2924 _ = options;2964 _ = options;
2925 @panic("TODO");2965 @panic("TODO implement netListenUnixWindows");
2926}2966}
29272967
2928fn posixBindUnix(t: *Threaded, fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void {2968fn posixBindUnix(t: *Threaded, fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void {
...@@ -3122,7 +3162,7 @@ fn netConnectIpPosix(...@@ -3122,7 +3162,7 @@ fn netConnectIpPosix(
3122 options: IpAddress.ConnectOptions,3162 options: IpAddress.ConnectOptions,
3123) IpAddress.ConnectError!net.Stream {3163) IpAddress.ConnectError!net.Stream {
3124 if (!have_networking) return error.NetworkDown;3164 if (!have_networking) return error.NetworkDown;
3125 if (options.timeout != .none) @panic("TODO");3165 if (options.timeout != .none) @panic("TODO implement netConnectIpPosix with timeout");
3126 const t: *Threaded = @ptrCast(@alignCast(userdata));3166 const t: *Threaded = @ptrCast(@alignCast(userdata));
3127 const family = posixAddressFamily(address);3167 const family = posixAddressFamily(address);
3128 const socket_fd = try openSocketPosix(t, family, .{3168 const socket_fd = try openSocketPosix(t, family, .{
...@@ -3146,7 +3186,7 @@ fn netConnectIpWindows(...@@ -3146,7 +3186,7 @@ fn netConnectIpWindows(
3146 options: IpAddress.ConnectOptions,3186 options: IpAddress.ConnectOptions,
3147) IpAddress.ConnectError!net.Stream {3187) IpAddress.ConnectError!net.Stream {
3148 if (!have_networking) return error.NetworkDown;3188 if (!have_networking) return error.NetworkDown;
3149 if (options.timeout != .none) @panic("TODO");3189 if (options.timeout != .none) @panic("TODO implement netConnectIpWindows with timeout");
3150 const t: *Threaded = @ptrCast(@alignCast(userdata));3190 const t: *Threaded = @ptrCast(@alignCast(userdata));
3151 const family = posixAddressFamily(address);3191 const family = posixAddressFamily(address);
3152 const socket_handle = try openSocketWsa(t, family, .{3192 const socket_handle = try openSocketWsa(t, family, .{
...@@ -3220,7 +3260,7 @@ fn netConnectUnixWindows(...@@ -3220,7 +3260,7 @@ fn netConnectUnixWindows(
3220 const t: *Threaded = @ptrCast(@alignCast(userdata));3260 const t: *Threaded = @ptrCast(@alignCast(userdata));
3221 try t.checkCancel();3261 try t.checkCancel();
3222 _ = address;3262 _ = address;
3223 @panic("TODO");3263 @panic("TODO implement netConnectUnixWindows");
3224}3264}
32253265
3226fn netBindIpPosix(3266fn netBindIpPosix(
...@@ -3525,7 +3565,7 @@ fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8...@@ -3525,7 +3565,7 @@ fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8
3525 _ = t;3565 _ = t;
3526 _ = handle;3566 _ = handle;
3527 _ = data;3567 _ = data;
3528 @panic("TODO");3568 @panic("TODO implement netReadWindows");
3529}3569}
35303570
3531fn netSendPosix(3571fn netSendPosix(
...@@ -3569,7 +3609,7 @@ fn netSendWindows(...@@ -3569,7 +3609,7 @@ fn netSendWindows(
3569 _ = handle;3609 _ = handle;
3570 _ = messages;3610 _ = messages;
3571 _ = flags;3611 _ = flags;
3572 @panic("TODO");3612 @panic("TODO netSendWindows");
3573}3613}
35743614
3575fn netSendOne(3615fn netSendOne(
...@@ -3872,7 +3912,7 @@ fn netReceiveWindows(...@@ -3872,7 +3912,7 @@ fn netReceiveWindows(
3872 _ = data_buffer;3912 _ = data_buffer;
3873 _ = flags;3913 _ = flags;
3874 _ = timeout;3914 _ = timeout;
3875 @panic("TODO");3915 @panic("TODO implement netReceiveWindows");
3876}3916}
38773917
3878fn netWritePosix(3918fn netWritePosix(
...@@ -3970,7 +4010,7 @@ fn netWriteWindows(...@@ -3970,7 +4010,7 @@ fn netWriteWindows(
3970 _ = header;4010 _ = header;
3971 _ = data;4011 _ = data;
3972 _ = splat;4012 _ = splat;
3973 @panic("TODO");4013 @panic("TODO implement netWriteWindows");
3974}4014}
39754015
3976fn addBuf(v: []posix.iovec_const, i: *@FieldType(posix.msghdr_const, "iovlen"), bytes: []const u8) void {4016fn addBuf(v: []posix.iovec_const, i: *@FieldType(posix.msghdr_const, "iovlen"), bytes: []const u8) void {
...@@ -4036,7 +4076,7 @@ fn netInterfaceNameResolve(...@@ -4036,7 +4076,7 @@ fn netInterfaceNameResolve(
40364076
4037 if (native_os == .windows) {4077 if (native_os == .windows) {
4038 try t.checkCancel();4078 try t.checkCancel();
4039 @panic("TODO");4079 @panic("TODO implement netInterfaceNameResolve for Windows");
4040 }4080 }
40414081
4042 if (builtin.link_libc) {4082 if (builtin.link_libc) {
...@@ -4055,15 +4095,15 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa...@@ -4055,15 +4095,15 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa
40554095
4056 if (native_os == .linux) {4096 if (native_os == .linux) {
4057 _ = interface;4097 _ = interface;
4058 @panic("TODO");4098 @panic("TODO implement netInterfaceName for linux");
4059 }4099 }
40604100
4061 if (native_os == .windows) {4101 if (native_os == .windows) {
4062 @panic("TODO");4102 @panic("TODO implement netInterfaceName for windows");
4063 }4103 }
40644104
4065 if (builtin.link_libc) {4105 if (builtin.link_libc) {
4066 @panic("TODO");4106 @panic("TODO implement netInterfaceName for libc");
4067 }4107 }
40684108
4069 @panic("unimplemented");4109 @panic("unimplemented");
lib/std/fs/File.zig-44
...@@ -312,50 +312,6 @@ pub const StatError = posix.FStatError;...@@ -312,50 +312,6 @@ pub const StatError = posix.FStatError;
312312
313/// Returns `Stat` containing basic information about the `File`.313/// Returns `Stat` containing basic information about the `File`.
314pub fn stat(self: File) StatError!Stat {314pub fn stat(self: File) StatError!Stat {
315 if (builtin.os.tag == .windows) {
316 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
317 var info: windows.FILE_ALL_INFORMATION = undefined;
318 const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
319 switch (rc) {
320 .SUCCESS => {},
321 // Buffer overflow here indicates that there is more information available than was able to be stored in the buffer
322 // size provided. This is treated as success because the type of variable-length information that this would be relevant for
323 // (name, volume name, etc) we don't care about.
324 .BUFFER_OVERFLOW => {},
325 .INVALID_PARAMETER => unreachable,
326 .ACCESS_DENIED => return error.AccessDenied,
327 else => return windows.unexpectedStatus(rc),
328 }
329 return .{
330 .inode = info.InternalInformation.IndexNumber,
331 .size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)),
332 .mode = 0,
333 .kind = if (info.BasicInformation.FileAttributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) reparse_point: {
334 var tag_info: windows.FILE_ATTRIBUTE_TAG_INFO = undefined;
335 const tag_rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &tag_info, @sizeOf(windows.FILE_ATTRIBUTE_TAG_INFO), .FileAttributeTagInformation);
336 switch (tag_rc) {
337 .SUCCESS => {},
338 // INFO_LENGTH_MISMATCH and ACCESS_DENIED are the only documented possible errors
339 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e
340 .INFO_LENGTH_MISMATCH => unreachable,
341 .ACCESS_DENIED => return error.AccessDenied,
342 else => return windows.unexpectedStatus(rc),
343 }
344 if (tag_info.ReparseTag & windows.reparse_tag_name_surrogate_bit != 0) {
345 break :reparse_point .sym_link;
346 }
347 // Unknown reparse point
348 break :reparse_point .unknown;
349 } else if (info.BasicInformation.FileAttributes & windows.FILE_ATTRIBUTE_DIRECTORY != 0)
350 .directory
351 else
352 .file,
353 .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
354 .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
355 .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime),
356 };
357 }
358
359 var threaded: Io.Threaded = .init_single_threaded;315 var threaded: Io.Threaded = .init_single_threaded;
360 const io = threaded.io();316 const io = threaded.io();
361 return Io.File.stat(.{ .handle = self.handle }, io);317 return Io.File.stat(.{ .handle = self.handle }, io);