authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-12-19 03:25:28-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:11-08:00
log3431f4503116e06b542eedf9c9bad6f769af3ebc
treed99949140dded4c58050445ac65223debda48521
parent1136d36362f87ebad83007ea31d88c8427c0a019

std: Enough fixes to get things compiling on Windows


5 files changed, 144 insertions(+), 86 deletions(-)

lib/std/Io/Dir.zig+2
......@@ -1028,6 +1028,8 @@ pub const SymLinkError = error{
10281028 FileNotFound,
10291029 SystemResources,
10301030 NoSpaceLeft,
1031 /// On Windows, `\\server` or `\\server\share` was not found.
1032 NetworkNotFound,
10311033 ReadOnlyFileSystem,
10321034 NotDir,
10331035} || PathNameError || Io.Cancelable || Io.UnexpectedError;
lib/std/Io/File.zig+2-2
......@@ -401,8 +401,8 @@ pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(
401401
402402 const windows = std.os.windows;
403403
404 pub fn toAttributes(self: @This()) windows.DWORD {
405 return @intFromEnum(self);
404 pub fn toAttributes(self: @This()) windows.FILE.ATTRIBUTE {
405 return @bitCast(@intFromEnum(self));
406406 }
407407
408408 pub fn readOnly(self: @This()) bool {
lib/std/Io/Terminal.zig+1-1
......@@ -60,7 +60,7 @@ pub const Mode = union(enum) {
6060 const windows = std.os.windows;
6161 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
6262 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != 0) {
63 return .{ .terminal_winapi = .{
63 return .{ .windows_api = .{
6464 .handle = file.handle,
6565 .reset_attributes = info.wAttributes,
6666 } };
lib/std/Io/Threaded.zig+138-82
......@@ -3889,9 +3889,10 @@ fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8,
38893889 return realPathWindows(current_thread, h_file, out_buffer);
38903890}
38913891
3892fn realPathWindows(current_thread: *Thread, h_file: windows.HANDLE, out_buffer: []u8) Dir.RealPathFileError {
3892fn realPathWindows(current_thread: *Thread, h_file: windows.HANDLE, out_buffer: []u8) File.RealPathError!usize {
38933893 _ = current_thread; // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks
3894 const wide_slice = windows.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
3894 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
3895 const wide_slice = try windows.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
38953896
38963897 const len = std.unicode.calcWtf8Len(wide_slice);
38973898 if (len > out_buffer.len)
......@@ -3981,7 +3982,19 @@ fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.Rea
39813982 return realPathWindows(current_thread, dir.handle, out_buffer);
39823983}
39833984
3984fn fileRealPath(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
3985const fileRealPath = switch (native_os) {
3986 .windows => fileRealPathWindows,
3987 else => fileRealPathPosix,
3988};
3989
3990fn fileRealPathWindows(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
3991 if (native_os == .wasi) return error.OperationUnsupported;
3992 const t: *Threaded = @ptrCast(@alignCast(userdata));
3993 const current_thread = Thread.getCurrent(t);
3994 return realPathWindows(current_thread, file.handle, out_buffer);
3995}
3996
3997fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
39853998 if (native_os == .wasi) return error.OperationUnsupported;
39863999 const t: *Threaded = @ptrCast(@alignCast(userdata));
39874000 const current_thread = Thread.getCurrent(t);
......@@ -4128,7 +4141,10 @@ const dirDeleteFile = switch (native_os) {
41284141};
41294142
41304143fn dirDeleteFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
4131 return dirDeleteWindows(userdata, dir, sub_path, false);
4144 return dirDeleteWindows(userdata, dir, sub_path, false) catch |err| switch (err) {
4145 error.DirNotEmpty => unreachable,
4146 else => |e| return e,
4147 };
41324148}
41334149
41344150fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
......@@ -4261,10 +4277,13 @@ const dirDeleteDir = switch (native_os) {
42614277};
42624278
42634279fn dirDeleteDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
4264 return dirDeleteWindows(userdata, dir, sub_path, true);
4280 return dirDeleteWindows(userdata, dir, sub_path, true) catch |err| switch (err) {
4281 error.IsDir => unreachable,
4282 else => |e| return e,
4283 };
42654284}
42664285
4267fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) Dir.DeleteFileError!void {
4286fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) (Dir.DeleteDirError || Dir.DeleteFileError)!void {
42684287 const t: *Threaded = @ptrCast(@alignCast(userdata));
42694288 const current_thread = Thread.getCurrent(t);
42704289 const w = windows;
......@@ -4352,18 +4371,18 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
43524371 try current_thread.checkCancel();
43534372
43544373 // Deletion with posix semantics if the filesystem supports it.
4355 var info: w.FILE_DISPOSITION_INFORMATION_EX = .{
4356 .Flags = w.FILE_DISPOSITION_DELETE |
4357 w.FILE_DISPOSITION_POSIX_SEMANTICS |
4358 w.FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
4359 };
4374 const info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
4375 .DELETE = true,
4376 .POSIX_SEMANTICS = true,
4377 .IGNORE_READONLY_ATTRIBUTE = true,
4378 } };
43604379
43614380 rc = w.ntdll.NtSetInformationFile(
43624381 tmp_handle,
43634382 &io_status_block,
43644383 &info,
4365 @sizeOf(w.FILE_DISPOSITION_INFORMATION_EX),
4366 .FileDispositionInformationEx,
4384 @sizeOf(w.FILE.DISPOSITION.INFORMATION.EX),
4385 .DispositionEx,
43674386 );
43684387 switch (rc) {
43694388 .SUCCESS => return,
......@@ -4384,7 +4403,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
43844403
43854404 // Deletion with file pending semantics, which requires waiting or moving
43864405 // files to get them removed (from here).
4387 var file_dispo: w.FILE_DISPOSITION_INFORMATION = .{
4406 const file_dispo: w.FILE.DISPOSITION.INFORMATION = .{
43884407 .DeleteFile = w.TRUE,
43894408 };
43904409
......@@ -4392,8 +4411,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
43924411 tmp_handle,
43934412 &io_status_block,
43944413 &file_dispo,
4395 @sizeOf(w.FILE_DISPOSITION_INFORMATION),
4396 .FileDispositionInformation,
4414 @sizeOf(w.FILE.DISPOSITION.INFORMATION),
4415 .Disposition,
43974416 );
43984417 }
43994418 switch (rc) {
......@@ -4555,29 +4574,23 @@ fn dirRenameWindows(
45554574 // The strategy here is just to try using FileRenameInformationEx and fall back to
45564575 // FileRenameInformation if the return value lets us know that some aspect of it is not supported.
45574576 const need_fallback = need_fallback: {
4558 const struct_buf_len = @sizeOf(w.FILE_RENAME_INFORMATION_EX) + (w.PATH_MAX_WIDE * 2);
4559 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(w.FILE_RENAME_INFORMATION_EX)) = undefined;
4560 const struct_len = @sizeOf(w.FILE_RENAME_INFORMATION_EX) + new_path_w.len * 2;
4561 if (struct_len > struct_buf_len) return error.NameTooLong;
4562
4563 const rename_info: *w.FILE_RENAME_INFORMATION_EX = @ptrCast(&rename_info_buf);
4564 var io_status_block: w.IO_STATUS_BLOCK = undefined;
4565
4566 var flags: w.ULONG = w.FILE_RENAME_POSIX_SEMANTICS | w.FILE_RENAME_IGNORE_READONLY_ATTRIBUTE;
4567 if (replace_if_exists) flags |= w.FILE_RENAME_REPLACE_IF_EXISTS;
4568 rename_info.* = .{
4569 .Flags = flags,
4577 const rename_info: w.FILE.RENAME_INFORMATION = .init(.{
4578 .Flags = .{
4579 .REPLACE_IF_EXISTS = replace_if_exists,
4580 .POSIX_SEMANTICS = true,
4581 .IGNORE_READONLY_ATTRIBUTE = true,
4582 },
45704583 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
4571 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
4572 .FileName = undefined,
4573 };
4574 @memcpy((&rename_info.FileName).ptr, new_path_w);
4584 .FileName = new_path_w,
4585 });
4586 var io_status_block: w.IO_STATUS_BLOCK = undefined;
4587 const rename_info_buf = rename_info.toBuffer();
45754588 rc = w.ntdll.NtSetInformationFile(
45764589 src_fd,
45774590 &io_status_block,
4578 rename_info,
4579 @intCast(struct_len), // already checked for error.NameTooLong
4580 .FileRenameInformationEx,
4591 rename_info_buf.ptr,
4592 @intCast(rename_info_buf.len),
4593 .RenameEx,
45814594 );
45824595 switch (rc) {
45834596 .SUCCESS => return,
......@@ -4594,28 +4607,19 @@ fn dirRenameWindows(
45944607 };
45954608
45964609 if (need_fallback) {
4597 const struct_buf_len = @sizeOf(w.FILE_RENAME_INFORMATION) + (w.PATH_MAX_WIDE * 2);
4598 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(w.FILE_RENAME_INFORMATION)) = undefined;
4599 const struct_len = @sizeOf(w.FILE_RENAME_INFORMATION) + new_path_w.len * 2;
4600 if (struct_len > struct_buf_len) return error.NameTooLong;
4601
4602 const rename_info: *w.FILE_RENAME_INFORMATION = @ptrCast(&rename_info_buf);
4603 var io_status_block: w.IO_STATUS_BLOCK = undefined;
4604
4605 rename_info.* = .{
4606 .Flags = @intFromBool(replace_if_exists),
4610 const rename_info: w.FILE.RENAME_INFORMATION = .init(.{
4611 .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
46074612 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
4608 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
4609 .FileName = undefined,
4610 };
4611 @memcpy((&rename_info.FileName).ptr, new_path_w);
4612
4613 .FileName = new_path_w,
4614 });
4615 var io_status_block: w.IO_STATUS_BLOCK = undefined;
4616 const rename_info_buf = rename_info.toBuffer();
46134617 rc = w.ntdll.NtSetInformationFile(
46144618 src_fd,
46154619 &io_status_block,
4616 rename_info,
4617 @intCast(struct_len), // already checked for error.NameTooLong
4618 .FileRenameInformation,
4620 rename_info_buf.ptr,
4621 @intCast(rename_info_buf.len),
4622 .Rename,
46194623 );
46204624 }
46214625
......@@ -4779,7 +4783,7 @@ fn dirSymLinkWindows(
47794783 const sym_link_path_w = try w.sliceToPrefixedFileW(dir.handle, sym_link_path);
47804784
47814785 const SYMLINK_DATA = extern struct {
4782 ReparseTag: w.ULONG,
4786 ReparseTag: w.IO_REPARSE_TAG,
47834787 ReparseDataLength: w.USHORT,
47844788 Reserved: w.USHORT,
47854789 SubstituteNameOffset: w.USHORT,
......@@ -4794,7 +4798,7 @@ fn dirSymLinkWindows(
47944798 .GENERIC = .{ .READ = true, .WRITE = true },
47954799 .STANDARD = .{ .SYNCHRONIZE = true },
47964800 },
4797 .dir = dir,
4801 .dir = dir.handle,
47984802 .creation = .CREATE,
47994803 .filter = if (flags.is_directory) .dir_only else .non_directory_only,
48004804 }) catch |err| switch (err) {
......@@ -4818,11 +4822,11 @@ fn dirSymLinkWindows(
48184822 // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
48194823 var is_target_absolute = false;
48204824 const final_target_path = target_path: {
4821 if (w.hasCommonNtPrefix(u16, target_path)) {
4825 if (w.hasCommonNtPrefix(u16, target_path_w.span())) {
48224826 // Already an NT path, no need to do anything to it
4823 break :target_path target_path;
4827 break :target_path target_path_w.span();
48244828 } else {
4825 switch (w.getWin32PathType(u16, target_path)) {
4829 switch (w.getWin32PathType(u16, target_path_w.span())) {
48264830 // Rooted paths need to avoid getting put through wToPrefixedFileW
48274831 // (and they are treated as relative in this context)
48284832 // Note: It seems that rooted paths in symbolic links are relative to
......@@ -4830,13 +4834,13 @@ fn dirSymLinkWindows(
48304834 // So, if the symlink is on C:\ and the CWD is on D:\,
48314835 // it will still resolve the path relative to the root of
48324836 // the C:\ drive.
4833 .rooted => break :target_path target_path,
4837 .rooted => break :target_path target_path_w.span(),
48344838 // Keep relative paths relative, but anything else needs to get NT-prefixed.
4835 else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path))
4836 break :target_path target_path,
4839 else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path_w.span()))
4840 break :target_path target_path_w.span(),
48374841 }
48384842 }
4839 var prefixed_target_path = try w.wToPrefixedFileW(dir, target_path);
4843 var prefixed_target_path = try w.wToPrefixedFileW(dir.handle, target_path_w.span());
48404844 // We do this after prefixing to ensure that drive-relative paths are treated as absolute
48414845 is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
48424846 break :target_path prefixed_target_path.span();
......@@ -4848,7 +4852,7 @@ fn dirSymLinkWindows(
48484852 const header_len = @sizeOf(w.ULONG) + @sizeOf(w.USHORT) * 2;
48494853 const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path);
48504854 const symlink_data = SYMLINK_DATA{
4851 .ReparseTag = w.IO_REPARSE_TAG_SYMLINK,
4855 .ReparseTag = .SYMLINK,
48524856 .ReparseDataLength = @intCast(buf_len - header_len),
48534857 .Reserved = 0,
48544858 .SubstituteNameOffset = @intCast(final_target_path.len * 2),
......@@ -4862,7 +4866,14 @@ fn dirSymLinkWindows(
48624866 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
48634867 const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
48644868 @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
4865 _ = try w.DeviceIoControl(symlink_handle, w.FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
4869 _ = w.DeviceIoControl(symlink_handle, w.FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] }) catch |err| switch (err) {
4870 error.PipeClosing => unreachable,
4871 error.PipeAlreadyConnected => unreachable,
4872 error.PipeAlreadyListening => unreachable,
4873 error.Pending => unreachable,
4874 error.UnrecognizedVolume => unreachable,
4875 else => |e| return e,
4876 };
48664877}
48674878
48684879fn dirSymLinkWasi(
......@@ -5503,16 +5514,22 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {
55035514 {
55045515 try current_thread.checkCancel();
55055516 var io_status: windows.IO_STATUS_BLOCK = undefined;
5506 var device_info: windows.FILE_FS_DEVICE_INFORMATION = undefined;
5507 const rc = windows.ntdll.NtQueryVolumeInformationFile(handle, &io_status, &device_info, @sizeOf(windows.FILE_FS_DEVICE_INFORMATION), .FileFsDeviceInformation);
5517 var device_info: windows.FILE.FS_DEVICE_INFORMATION = undefined;
5518 const rc = windows.ntdll.NtQueryVolumeInformationFile(
5519 handle,
5520 &io_status,
5521 &device_info,
5522 @sizeOf(windows.FILE.FS_DEVICE_INFORMATION),
5523 .Device,
5524 );
55085525 switch (rc) {
55095526 .SUCCESS => {},
55105527 else => return false,
55115528 }
5512 if (device_info.DeviceType != windows.FILE_DEVICE_NAMED_PIPE) return false;
5529 if (device_info.DeviceType.FileDevice != .NAMED_PIPE) return false;
55135530 }
55145531
5515 const name_bytes_offset = @offsetOf(windows.FILE_NAME_INFO, "FileName");
5532 const name_bytes_offset = @offsetOf(windows.FILE.NAME_INFORMATION, "FileName");
55165533 // `NAME_MAX` UTF-16 code units (2 bytes each)
55175534 // This buffer may not be long enough to handle *all* possible paths
55185535 // (PATH_MAX_WIDE would be necessary for that), but because we only care
......@@ -5520,11 +5537,17 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {
55205537 // we can use this smaller buffer and just return false on any error from
55215538 // NtQueryInformationFile.
55225539 const num_name_bytes = windows.MAX_PATH * 2;
5523 var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
5540 var name_info_bytes align(@alignOf(windows.FILE.NAME_INFORMATION)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
55245541
55255542 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
55265543 try current_thread.checkCancel();
5527 const rc = windows.ntdll.NtQueryInformationFile(handle, &io_status_block, &name_info_bytes, @intCast(name_info_bytes.len), .FileNameInformation);
5544 const rc = windows.ntdll.NtQueryInformationFile(
5545 handle,
5546 &io_status_block,
5547 &name_info_bytes,
5548 @intCast(name_info_bytes.len),
5549 .Name,
5550 );
55285551 switch (rc) {
55295552 .SUCCESS => {},
55305553 .INVALID_PARAMETER => unreachable,
......@@ -5551,7 +5574,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE
55515574 try current_thread.checkCancel();
55525575
55535576 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
5554 var eof_info: windows.FILE_END_OF_FILE_INFORMATION = .{
5577 const eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{
55555578 .EndOfFile = signed_len,
55565579 };
55575580
......@@ -5559,8 +5582,8 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE
55595582 file.handle,
55605583 &io_status_block,
55615584 &eof_info,
5562 @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
5563 .FileEndOfFileInformation,
5585 @sizeOf(windows.FILE.END_OF_FILE_INFORMATION),
5586 .EndOfFile,
55645587 );
55655588 switch (status) {
55665589 .SUCCESS => return,
......@@ -5643,19 +5666,19 @@ fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permi
56435666 .windows => {
56445667 try current_thread.checkCancel();
56455668 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
5646 var info: windows.FILE_BASIC_INFORMATION = .{
5669 const info: windows.FILE.BASIC_INFORMATION = .{
56475670 .CreationTime = 0,
56485671 .LastAccessTime = 0,
56495672 .LastWriteTime = 0,
56505673 .ChangeTime = 0,
5651 .FileAttributes = permissions.inner.attributes,
5674 .FileAttributes = permissions.toAttributes(),
56525675 };
56535676 const status = windows.ntdll.NtSetInformationFile(
56545677 file.handle,
56555678 &io_status_block,
56565679 &info,
5657 @sizeOf(windows.FILE_BASIC_INFORMATION),
5658 .FileBasicInformation,
5680 @sizeOf(windows.FILE.BASIC_INFORMATION),
5681 .Basic,
56595682 );
56605683 switch (status) {
56615684 .SUCCESS => return,
......@@ -5813,8 +5836,8 @@ fn fileSetTimestamps(
58135836 if (is_windows) {
58145837 try current_thread.checkCancel();
58155838
5816 const atime_ft = windows.nanoSecondsToFileTime(last_accessed.toNanoseconds());
5817 const mtime_ft = windows.nanoSecondsToFileTime(last_modified.toNanoseconds());
5839 const atime_ft = windows.nanoSecondsToFileTime(last_accessed);
5840 const mtime_ft = windows.nanoSecondsToFileTime(last_modified);
58185841
58195842 // https://github.com/ziglang/zig/issues/1840
58205843 const rc = windows.kernel32.SetFileTime(file.handle, null, &atime_ft, &mtime_ft);
......@@ -5958,7 +5981,24 @@ fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!v
59585981
59595982 if (is_windows) {
59605983 const exclusive = switch (lock) {
5961 .none => return,
5984 .none => {
5985 // To match the non-Windows behavior, unlock
5986 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
5987 const status = windows.ntdll.NtUnlockFile(
5988 file.handle,
5989 &io_status_block,
5990 &windows_lock_range_off,
5991 &windows_lock_range_len,
5992 0,
5993 );
5994 switch (status) {
5995 .SUCCESS => {},
5996 .RANGE_NOT_LOCKED => {},
5997 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
5998 else => return windows.unexpectedStatus(status),
5999 }
6000 return;
6001 },
59626002 .shared => false,
59636003 .exclusive => true,
59646004 };
......@@ -6020,7 +6060,23 @@ fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockErro
60206060
60216061 if (is_windows) {
60226062 const exclusive = switch (lock) {
6023 .none => return,
6063 .none => {
6064 // To match the non-Windows behavior, unlock
6065 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6066 const status = windows.ntdll.NtUnlockFile(
6067 file.handle,
6068 &io_status_block,
6069 &windows_lock_range_off,
6070 &windows_lock_range_len,
6071 0,
6072 );
6073 switch (status) {
6074 .SUCCESS => return true,
6075 .RANGE_NOT_LOCKED => return false,
6076 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
6077 else => return windows.unexpectedStatus(status),
6078 }
6079 },
60246080 .shared => false,
60256081 .exclusive => true,
60266082 };
......@@ -6093,7 +6149,7 @@ fn fileUnlock(userdata: ?*anyopaque, file: File) void {
60936149 &io_status_block,
60946150 &windows_lock_range_off,
60956151 &windows_lock_range_len,
6096 null,
6152 0,
60976153 );
60986154 if (is_debug) switch (status) {
60996155 .SUCCESS => {},
lib/std/posix/test.zig+1-1
......@@ -326,7 +326,7 @@ test "sync" {
326326
327327test "fsync" {
328328 switch (native_os) {
329 .linux, .windows, .illumos => {},
329 .linux, .illumos => {},
330330 else => return error.SkipZigTest,
331331 }
332332