diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 88796e8e4c..4c51e512b6 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1416,23 +1416,42 @@ test "File.PermissionsUnix" { try testing.expect(!permissions_unix.unixHas(.other, .execute)); } -test "delete a read-only file on windows" { - if (builtin.os.tag != .windows) return error.SkipZigTest; +test "delete a read-only file on windows with file pending semantics" { + if (builtin.os.tag != .windows or builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) + return error.SkipZigTest; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + { + const file = try tmp.dir.createFile("test_file", .{ .read = true }); + defer file.close(); + // Create a file and make it read-only + const metadata = try file.metadata(); + var permissions = metadata.permissions(); + permissions.setReadOnly(true); + try file.setPermissions(permissions); + try testing.expectError(error.AccessDenied, tmp.dir.deleteFile("test_file")); + // Now make the file not read-only + permissions.setReadOnly(false); + try file.setPermissions(permissions); + } + try tmp.dir.deleteFile("test_file"); +} + +test "delete a read-only file on windows with posix semantis" { + if (builtin.os.tag != .windows or !builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) + return error.SkipZigTest; var tmp = tmpDir(.{}); defer tmp.cleanup(); const file = try tmp.dir.createFile("test_file", .{ .read = true }); + defer file.close(); // Create a file and make it read-only const metadata = try file.metadata(); var permissions = metadata.permissions(); permissions.setReadOnly(true); try file.setPermissions(permissions); - try testing.expectError(error.AccessDenied, tmp.dir.deleteFile("test_file")); - // Now make the file not read-only - permissions.setReadOnly(false); - try file.setPermissions(permissions); - file.close(); - try tmp.dir.deleteFile("test_file"); + try tmp.dir.deleteFile("test_file"); // file is unmapped and deleted once last handle closed } test "delete a setAsCwd directory on Windows" { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 389c4bea12..2c3b1ded8e 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -937,19 +937,40 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil .DELETE_PENDING => return, else => return unexpectedStatus(rc), } - var file_dispo = FILE_DISPOSITION_INFORMATION{ - .DeleteFile = TRUE, - }; - rc = ntdll.NtSetInformationFile( - tmp_handle, - &io, - &file_dispo, - @sizeOf(FILE_DISPOSITION_INFORMATION), - .FileDispositionInformation, - ); - CloseHandle(tmp_handle); + defer CloseHandle(tmp_handle); + + if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) { + // Deletion with posix semantics. + var info = FILE_DISPOSITION_INFORMATION_EX{ + .Flags = FILE_DISPOSITION_DELETE | + FILE_DISPOSITION_POSIX_SEMANTICS | + FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, + }; + + rc = ntdll.NtSetInformationFile( + tmp_handle, + &io, + &info, + @sizeOf(FILE_DISPOSITION_INFORMATION_EX), + .FileDispositionInformationEx, + ); + } else { + // Deletion with file pending semantics, which requires waiting or moving + // files to get them removed (from here). + var file_dispo = FILE_DISPOSITION_INFORMATION{ + .DeleteFile = TRUE, + }; + + rc = ntdll.NtSetInformationFile( + tmp_handle, + &io, + &file_dispo, + @sizeOf(FILE_DISPOSITION_INFORMATION), + .FileDispositionInformation, + ); + } switch (rc) { - .SUCCESS => return, + .SUCCESS => {}, .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, .INVALID_PARAMETER => unreachable, .CANNOT_DELETE => return error.AccessDenied, @@ -2574,6 +2595,18 @@ pub const FILE_NAME_INFORMATION = extern struct { FileName: [1]WCHAR, }; +pub const FILE_DISPOSITION_INFORMATION_EX = extern struct { + /// combination of FILE_DISPOSITION_* flags + Flags: ULONG, +}; + +const FILE_DISPOSITION_DO_NOT_DELETE: ULONG = 0x00000000; +const FILE_DISPOSITION_DELETE: ULONG = 0x00000001; +const FILE_DISPOSITION_POSIX_SEMANTICS: ULONG = 0x00000002; +const FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK: ULONG = 0x00000004; +const FILE_DISPOSITION_ON_CLOSE: ULONG = 0x00000008; +const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: ULONG = 0x00000010; + pub const FILE_RENAME_INFORMATION = extern struct { ReplaceIfExists: BOOLEAN, RootDirectory: ?HANDLE,