| ... | ... | @@ -3889,9 +3889,10 @@ fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, |
| 3889 | 3889 | return realPathWindows(current_thread, h_file, out_buffer); |
| 3890 | 3890 | } |
| 3891 | 3891 | |
| 3892 | | fn realPathWindows(current_thread: *Thread, h_file: windows.HANDLE, out_buffer: []u8) Dir.RealPathFileError { |
| 3892 | fn realPathWindows(current_thread: *Thread, h_file: windows.HANDLE, out_buffer: []u8) File.RealPathError!usize { |
| 3893 | 3893 | _ = 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); |
| 3895 | 3896 | |
| 3896 | 3897 | const len = std.unicode.calcWtf8Len(wide_slice); |
| 3897 | 3898 | if (len > out_buffer.len) |
| ... | ... | @@ -3981,7 +3982,19 @@ fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.Rea |
| 3981 | 3982 | return realPathWindows(current_thread, dir.handle, out_buffer); |
| 3982 | 3983 | } |
| 3983 | 3984 | |
| 3984 | | fn fileRealPath(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize { |
| 3985 | const fileRealPath = switch (native_os) { |
| 3986 | .windows => fileRealPathWindows, |
| 3987 | else => fileRealPathPosix, |
| 3988 | }; |
| 3989 | |
| 3990 | fn 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 | |
| 3997 | fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize { |
| 3985 | 3998 | if (native_os == .wasi) return error.OperationUnsupported; |
| 3986 | 3999 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 3987 | 4000 | const current_thread = Thread.getCurrent(t); |
| ... | ... | @@ -4128,7 +4141,10 @@ const dirDeleteFile = switch (native_os) { |
| 4128 | 4141 | }; |
| 4129 | 4142 | |
| 4130 | 4143 | fn 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 | }; |
| 4132 | 4148 | } |
| 4133 | 4149 | |
| 4134 | 4150 | fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void { |
| ... | ... | @@ -4261,10 +4277,13 @@ const dirDeleteDir = switch (native_os) { |
| 4261 | 4277 | }; |
| 4262 | 4278 | |
| 4263 | 4279 | fn 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 | }; |
| 4265 | 4284 | } |
| 4266 | 4285 | |
| 4267 | | fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) Dir.DeleteFileError!void { |
| 4286 | fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) (Dir.DeleteDirError || Dir.DeleteFileError)!void { |
| 4268 | 4287 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 4269 | 4288 | const current_thread = Thread.getCurrent(t); |
| 4270 | 4289 | const w = windows; |
| ... | ... | @@ -4352,18 +4371,18 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 4352 | 4371 | try current_thread.checkCancel(); |
| 4353 | 4372 | |
| 4354 | 4373 | // 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 | } }; |
| 4360 | 4379 | |
| 4361 | 4380 | rc = w.ntdll.NtSetInformationFile( |
| 4362 | 4381 | tmp_handle, |
| 4363 | 4382 | &io_status_block, |
| 4364 | 4383 | &info, |
| 4365 | | @sizeOf(w.FILE_DISPOSITION_INFORMATION_EX), |
| 4366 | | .FileDispositionInformationEx, |
| 4384 | @sizeOf(w.FILE.DISPOSITION.INFORMATION.EX), |
| 4385 | .DispositionEx, |
| 4367 | 4386 | ); |
| 4368 | 4387 | switch (rc) { |
| 4369 | 4388 | .SUCCESS => return, |
| ... | ... | @@ -4384,7 +4403,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 4384 | 4403 | |
| 4385 | 4404 | // Deletion with file pending semantics, which requires waiting or moving |
| 4386 | 4405 | // files to get them removed (from here). |
| 4387 | | var file_dispo: w.FILE_DISPOSITION_INFORMATION = .{ |
| 4406 | const file_dispo: w.FILE.DISPOSITION.INFORMATION = .{ |
| 4388 | 4407 | .DeleteFile = w.TRUE, |
| 4389 | 4408 | }; |
| 4390 | 4409 | |
| ... | ... | @@ -4392,8 +4411,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 4392 | 4411 | tmp_handle, |
| 4393 | 4412 | &io_status_block, |
| 4394 | 4413 | &file_dispo, |
| 4395 | | @sizeOf(w.FILE_DISPOSITION_INFORMATION), |
| 4396 | | .FileDispositionInformation, |
| 4414 | @sizeOf(w.FILE.DISPOSITION.INFORMATION), |
| 4415 | .Disposition, |
| 4397 | 4416 | ); |
| 4398 | 4417 | } |
| 4399 | 4418 | switch (rc) { |
| ... | ... | @@ -4555,29 +4574,23 @@ fn dirRenameWindows( |
| 4555 | 4574 | // The strategy here is just to try using FileRenameInformationEx and fall back to |
| 4556 | 4575 | // FileRenameInformation if the return value lets us know that some aspect of it is not supported. |
| 4557 | 4576 | 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 | }, |
| 4570 | 4583 | .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(); |
| 4575 | 4588 | rc = w.ntdll.NtSetInformationFile( |
| 4576 | 4589 | src_fd, |
| 4577 | 4590 | &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, |
| 4581 | 4594 | ); |
| 4582 | 4595 | switch (rc) { |
| 4583 | 4596 | .SUCCESS => return, |
| ... | ... | @@ -4594,28 +4607,19 @@ fn dirRenameWindows( |
| 4594 | 4607 | }; |
| 4595 | 4608 | |
| 4596 | 4609 | 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 }, |
| 4607 | 4612 | .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(); |
| 4613 | 4617 | rc = w.ntdll.NtSetInformationFile( |
| 4614 | 4618 | src_fd, |
| 4615 | 4619 | &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, |
| 4619 | 4623 | ); |
| 4620 | 4624 | } |
| 4621 | 4625 | |
| ... | ... | @@ -4779,7 +4783,7 @@ fn dirSymLinkWindows( |
| 4779 | 4783 | const sym_link_path_w = try w.sliceToPrefixedFileW(dir.handle, sym_link_path); |
| 4780 | 4784 | |
| 4781 | 4785 | const SYMLINK_DATA = extern struct { |
| 4782 | | ReparseTag: w.ULONG, |
| 4786 | ReparseTag: w.IO_REPARSE_TAG, |
| 4783 | 4787 | ReparseDataLength: w.USHORT, |
| 4784 | 4788 | Reserved: w.USHORT, |
| 4785 | 4789 | SubstituteNameOffset: w.USHORT, |
| ... | ... | @@ -4794,7 +4798,7 @@ fn dirSymLinkWindows( |
| 4794 | 4798 | .GENERIC = .{ .READ = true, .WRITE = true }, |
| 4795 | 4799 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 4796 | 4800 | }, |
| 4797 | | .dir = dir, |
| 4801 | .dir = dir.handle, |
| 4798 | 4802 | .creation = .CREATE, |
| 4799 | 4803 | .filter = if (flags.is_directory) .dir_only else .non_directory_only, |
| 4800 | 4804 | }) catch |err| switch (err) { |
| ... | ... | @@ -4818,11 +4822,11 @@ fn dirSymLinkWindows( |
| 4818 | 4822 | // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw |
| 4819 | 4823 | var is_target_absolute = false; |
| 4820 | 4824 | const final_target_path = target_path: { |
| 4821 | | if (w.hasCommonNtPrefix(u16, target_path)) { |
| 4825 | if (w.hasCommonNtPrefix(u16, target_path_w.span())) { |
| 4822 | 4826 | // 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(); |
| 4824 | 4828 | } else { |
| 4825 | | switch (w.getWin32PathType(u16, target_path)) { |
| 4829 | switch (w.getWin32PathType(u16, target_path_w.span())) { |
| 4826 | 4830 | // Rooted paths need to avoid getting put through wToPrefixedFileW |
| 4827 | 4831 | // (and they are treated as relative in this context) |
| 4828 | 4832 | // Note: It seems that rooted paths in symbolic links are relative to |
| ... | ... | @@ -4830,13 +4834,13 @@ fn dirSymLinkWindows( |
| 4830 | 4834 | // So, if the symlink is on C:\ and the CWD is on D:\, |
| 4831 | 4835 | // it will still resolve the path relative to the root of |
| 4832 | 4836 | // the C:\ drive. |
| 4833 | | .rooted => break :target_path target_path, |
| 4837 | .rooted => break :target_path target_path_w.span(), |
| 4834 | 4838 | // 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(), |
| 4837 | 4841 | } |
| 4838 | 4842 | } |
| 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()); |
| 4840 | 4844 | // We do this after prefixing to ensure that drive-relative paths are treated as absolute |
| 4841 | 4845 | is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span()); |
| 4842 | 4846 | break :target_path prefixed_target_path.span(); |
| ... | ... | @@ -4848,7 +4852,7 @@ fn dirSymLinkWindows( |
| 4848 | 4852 | const header_len = @sizeOf(w.ULONG) + @sizeOf(w.USHORT) * 2; |
| 4849 | 4853 | const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path); |
| 4850 | 4854 | const symlink_data = SYMLINK_DATA{ |
| 4851 | | .ReparseTag = w.IO_REPARSE_TAG_SYMLINK, |
| 4855 | .ReparseTag = .SYMLINK, |
| 4852 | 4856 | .ReparseDataLength = @intCast(buf_len - header_len), |
| 4853 | 4857 | .Reserved = 0, |
| 4854 | 4858 | .SubstituteNameOffset = @intCast(final_target_path.len * 2), |
| ... | ... | @@ -4862,7 +4866,14 @@ fn dirSymLinkWindows( |
| 4862 | 4866 | @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path))); |
| 4863 | 4867 | const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2; |
| 4864 | 4868 | @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 | }; |
| 4866 | 4877 | } |
| 4867 | 4878 | |
| 4868 | 4879 | fn dirSymLinkWasi( |
| ... | ... | @@ -5503,16 +5514,22 @@ fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool { |
| 5503 | 5514 | { |
| 5504 | 5515 | try current_thread.checkCancel(); |
| 5505 | 5516 | 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 | ); |
| 5508 | 5525 | switch (rc) { |
| 5509 | 5526 | .SUCCESS => {}, |
| 5510 | 5527 | else => return false, |
| 5511 | 5528 | } |
| 5512 | | if (device_info.DeviceType != windows.FILE_DEVICE_NAMED_PIPE) return false; |
| 5529 | if (device_info.DeviceType.FileDevice != .NAMED_PIPE) return false; |
| 5513 | 5530 | } |
| 5514 | 5531 | |
| 5515 | | const name_bytes_offset = @offsetOf(windows.FILE_NAME_INFO, "FileName"); |
| 5532 | const name_bytes_offset = @offsetOf(windows.FILE.NAME_INFORMATION, "FileName"); |
| 5516 | 5533 | // `NAME_MAX` UTF-16 code units (2 bytes each) |
| 5517 | 5534 | // This buffer may not be long enough to handle *all* possible paths |
| 5518 | 5535 | // (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 { |
| 5520 | 5537 | // we can use this smaller buffer and just return false on any error from |
| 5521 | 5538 | // NtQueryInformationFile. |
| 5522 | 5539 | 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); |
| 5524 | 5541 | |
| 5525 | 5542 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 5526 | 5543 | 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 | ); |
| 5528 | 5551 | switch (rc) { |
| 5529 | 5552 | .SUCCESS => {}, |
| 5530 | 5553 | .INVALID_PARAMETER => unreachable, |
| ... | ... | @@ -5551,7 +5574,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE |
| 5551 | 5574 | try current_thread.checkCancel(); |
| 5552 | 5575 | |
| 5553 | 5576 | 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 = .{ |
| 5555 | 5578 | .EndOfFile = signed_len, |
| 5556 | 5579 | }; |
| 5557 | 5580 | |
| ... | ... | @@ -5559,8 +5582,8 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE |
| 5559 | 5582 | file.handle, |
| 5560 | 5583 | &io_status_block, |
| 5561 | 5584 | &eof_info, |
| 5562 | | @sizeOf(windows.FILE_END_OF_FILE_INFORMATION), |
| 5563 | | .FileEndOfFileInformation, |
| 5585 | @sizeOf(windows.FILE.END_OF_FILE_INFORMATION), |
| 5586 | .EndOfFile, |
| 5564 | 5587 | ); |
| 5565 | 5588 | switch (status) { |
| 5566 | 5589 | .SUCCESS => return, |
| ... | ... | @@ -5643,19 +5666,19 @@ fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permi |
| 5643 | 5666 | .windows => { |
| 5644 | 5667 | try current_thread.checkCancel(); |
| 5645 | 5668 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 5646 | | var info: windows.FILE_BASIC_INFORMATION = .{ |
| 5669 | const info: windows.FILE.BASIC_INFORMATION = .{ |
| 5647 | 5670 | .CreationTime = 0, |
| 5648 | 5671 | .LastAccessTime = 0, |
| 5649 | 5672 | .LastWriteTime = 0, |
| 5650 | 5673 | .ChangeTime = 0, |
| 5651 | | .FileAttributes = permissions.inner.attributes, |
| 5674 | .FileAttributes = permissions.toAttributes(), |
| 5652 | 5675 | }; |
| 5653 | 5676 | const status = windows.ntdll.NtSetInformationFile( |
| 5654 | 5677 | file.handle, |
| 5655 | 5678 | &io_status_block, |
| 5656 | 5679 | &info, |
| 5657 | | @sizeOf(windows.FILE_BASIC_INFORMATION), |
| 5658 | | .FileBasicInformation, |
| 5680 | @sizeOf(windows.FILE.BASIC_INFORMATION), |
| 5681 | .Basic, |
| 5659 | 5682 | ); |
| 5660 | 5683 | switch (status) { |
| 5661 | 5684 | .SUCCESS => return, |
| ... | ... | @@ -5813,8 +5836,8 @@ fn fileSetTimestamps( |
| 5813 | 5836 | if (is_windows) { |
| 5814 | 5837 | try current_thread.checkCancel(); |
| 5815 | 5838 | |
| 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); |
| 5818 | 5841 | |
| 5819 | 5842 | // https://github.com/ziglang/zig/issues/1840 |
| 5820 | 5843 | 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 |
| 5958 | 5981 | |
| 5959 | 5982 | if (is_windows) { |
| 5960 | 5983 | 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 | }, |
| 5962 | 6002 | .shared => false, |
| 5963 | 6003 | .exclusive => true, |
| 5964 | 6004 | }; |
| ... | ... | @@ -6020,7 +6060,23 @@ fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockErro |
| 6020 | 6060 | |
| 6021 | 6061 | if (is_windows) { |
| 6022 | 6062 | 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 | }, |
| 6024 | 6080 | .shared => false, |
| 6025 | 6081 | .exclusive => true, |
| 6026 | 6082 | }; |
| ... | ... | @@ -6093,7 +6149,7 @@ fn fileUnlock(userdata: ?*anyopaque, file: File) void { |
| 6093 | 6149 | &io_status_block, |
| 6094 | 6150 | &windows_lock_range_off, |
| 6095 | 6151 | &windows_lock_range_len, |
| 6096 | | null, |
| 6152 | 0, |
| 6097 | 6153 | ); |
| 6098 | 6154 | if (is_debug) switch (status) { |
| 6099 | 6155 | .SUCCESS => {}, |