authorgravatar for jan.hafer@rwth-aachen.deJan Philipp Hafer <jan.hafer@rwth-aachen.de> 2023-08-24 02:11:01+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-23 20:11:01-04:00
log7a834e258120865b55e32508bc1c6a5b192af5a0
tree2b8e608ef9737496fb3d308eec33363d3d4289e0
parent6780a6bbfa7bbd44036de3b1338011d1e5bb43f3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.windows: use atomic rename, if possible (#16717)

Mitigates #14978. Uses the same strategy as in #16499 suggested by @squeek502

2 files changed, 87 insertions(+), 24 deletions(-)

lib/std/os.zig+64-23
......@@ -2629,33 +2629,74 @@ pub fn renameatW(
26292629 };
26302630 defer windows.CloseHandle(src_fd);
26312631
2632 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
2633 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
2634 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2;
2635 if (struct_len > struct_buf_len) return error.NameTooLong;
2636
2637 const rename_info = @as(*windows.FILE_RENAME_INFORMATION, @ptrCast(&rename_info_buf));
2638
2639 rename_info.* = .{
2640 .ReplaceIfExists = ReplaceIfExists,
2641 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
2642 .FileNameLength = @as(u32, @intCast(new_path_w.len * 2)), // already checked error.NameTooLong
2643 .FileName = undefined,
2644 };
2645 @memcpy(@as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w);
2632 var need_fallback = true;
2633 var rc: windows.NTSTATUS = undefined;
2634 if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) {
2635 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) + (MAX_PATH_BYTES - 1);
2636 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION_EX)) = undefined;
2637 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) - 1 + new_path_w.len * 2;
2638 if (struct_len > struct_buf_len) return error.NameTooLong;
2639
2640 const rename_info = @as(*windows.FILE_RENAME_INFORMATION_EX, @ptrCast(&rename_info_buf));
2641 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
26462642
2647 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
2643 var flags: windows.ULONG = windows.FILE_RENAME_POSIX_SEMANTICS | windows.FILE_RENAME_IGNORE_READONLY_ATTRIBUTE;
2644 if (ReplaceIfExists == windows.TRUE) flags |= windows.FILE_RENAME_REPLACE_IF_EXISTS;
2645 rename_info.* = .{
2646 .Flags = flags,
2647 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
2648 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2649 .FileName = undefined,
2650 };
2651 @memcpy(@as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w);
2652 rc = windows.ntdll.NtSetInformationFile(
2653 src_fd,
2654 &io_status_block,
2655 rename_info,
2656 @intCast(struct_len), // already checked for error.NameTooLong
2657 .FileRenameInformationEx,
2658 );
2659 switch (rc) {
2660 .SUCCESS => return,
2661 // INVALID_PARAMETER here means that the filesystem does not support FileRenameInformationEx
2662 .INVALID_PARAMETER => {},
2663 .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
2664 .FILE_IS_A_DIRECTORY => return error.IsDir,
2665 .NOT_A_DIRECTORY => return error.NotDir,
2666 // For all other statuses, fall down to the switch below to handle them.
2667 else => need_fallback = false,
2668 }
2669 }
26482670
2649 const rc = windows.ntdll.NtSetInformationFile(
2650 src_fd,
2651 &io_status_block,
2652 rename_info,
2653 @as(u32, @intCast(struct_len)), // already checked for error.NameTooLong
2654 .FileRenameInformation,
2655 );
2671 if (need_fallback) {
2672 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
2673 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
2674 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2;
2675 if (struct_len > struct_buf_len) return error.NameTooLong;
2676
2677 const rename_info = @as(*windows.FILE_RENAME_INFORMATION, @ptrCast(&rename_info_buf));
2678 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
2679
2680 rename_info.* = .{
2681 .Flags = ReplaceIfExists,
2682 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
2683 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2684 .FileName = undefined,
2685 };
2686 @memcpy(@as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w);
2687
2688 rc =
2689 windows.ntdll.NtSetInformationFile(
2690 src_fd,
2691 &io_status_block,
2692 rename_info,
2693 @intCast(struct_len), // already checked for error.NameTooLong
2694 .FileRenameInformation,
2695 );
2696 }
26562697
26572698 switch (rc) {
2658 .SUCCESS => return,
2699 .SUCCESS => {},
26592700 .INVALID_HANDLE => unreachable,
26602701 .INVALID_PARAMETER => unreachable,
26612702 .OBJECT_PATH_SYNTAX_BAD => unreachable,
lib/std/os/windows.zig+23-1
......@@ -1009,6 +1009,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
10091009 .FileDispositionInformationEx,
10101010 );
10111011 switch (rc) {
1012 .SUCCESS => return,
10121013 // INVALID_PARAMETER here means that the filesystem does not support FileDispositionInformationEx
10131014 .INVALID_PARAMETER => {},
10141015 // For all other statuses, fall down to the switch below to handle them.
......@@ -2856,8 +2857,29 @@ const FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK: ULONG = 0x00000004;
28562857const FILE_DISPOSITION_ON_CLOSE: ULONG = 0x00000008;
28572858const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: ULONG = 0x00000010;
28582859
2860// FILE_RENAME_INFORMATION.Flags
2861pub const FILE_RENAME_REPLACE_IF_EXISTS = 0x00000001;
2862pub const FILE_RENAME_POSIX_SEMANTICS = 0x00000002;
2863pub const FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE = 0x00000004;
2864pub const FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE = 0x00000008;
2865pub const FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE = 0x00000010;
2866pub const FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE = 0x00000020;
2867pub const FILE_RENAME_PRESERVE_AVAILABLE_SPACE = 0x00000030;
2868pub const FILE_RENAME_IGNORE_READONLY_ATTRIBUTE = 0x00000040;
2869pub const FILE_RENAME_FORCE_RESIZE_TARGET_SR = 0x00000080;
2870pub const FILE_RENAME_FORCE_RESIZE_SOURCE_SR = 0x00000100;
2871pub const FILE_RENAME_FORCE_RESIZE_SR = 0x00000180;
2872
28592873pub const FILE_RENAME_INFORMATION = extern struct {
2860 ReplaceIfExists: BOOLEAN,
2874 Flags: BOOLEAN,
2875 RootDirectory: ?HANDLE,
2876 FileNameLength: ULONG,
2877 FileName: [1]WCHAR,
2878};
2879
2880// FileRenameInformationEx (since .win10_rs1)
2881pub const FILE_RENAME_INFORMATION_EX = extern struct {
2882 Flags: ULONG,
28612883 RootDirectory: ?HANDLE,
28622884 FileNameLength: ULONG,
28632885 FileName: [1]WCHAR,