authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-17 14:21:15-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-06-17 14:21:15-07:00
loge3736baddb8ecff90f0594be9f604c7484ce9aa2
treeb6b93f4565921de8452d66189642cb77a40e407c
parent8d0a8c28596985b1a308160b2f1b84df2b3a8a8b
parent7594d2c0977497c81db0c394f775832689bad492
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15501 from matu3ba/win_rmwithposix

std.windows: use posix semantics to delete files, if available

2 files changed, 72 insertions(+), 20 deletions(-)

lib/std/fs/test.zig+27-8
...@@ -1416,23 +1416,42 @@ test "File.PermissionsUnix" {...@@ -1416,23 +1416,42 @@ test "File.PermissionsUnix" {
1416 try testing.expect(!permissions_unix.unixHas(.other, .execute));1416 try testing.expect(!permissions_unix.unixHas(.other, .execute));
1417}1417}
14181418
1419test "delete a read-only file on windows" {1419test "delete a read-only file on windows with file pending semantics" {
1420 if (builtin.os.tag != .windows) return error.SkipZigTest;1420 if (builtin.os.tag != .windows or builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1))
1421 return error.SkipZigTest;
1422
1423 var tmp = tmpDir(.{});
1424 defer tmp.cleanup();
1425 {
1426 const file = try tmp.dir.createFile("test_file", .{ .read = true });
1427 defer file.close();
1428 // Create a file and make it read-only
1429 const metadata = try file.metadata();
1430 var permissions = metadata.permissions();
1431 permissions.setReadOnly(true);
1432 try file.setPermissions(permissions);
1433 try testing.expectError(error.AccessDenied, tmp.dir.deleteFile("test_file"));
1434 // Now make the file not read-only
1435 permissions.setReadOnly(false);
1436 try file.setPermissions(permissions);
1437 }
1438 try tmp.dir.deleteFile("test_file");
1439}
1440
1441test "delete a read-only file on windows with posix semantis" {
1442 if (builtin.os.tag != .windows or !builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1))
1443 return error.SkipZigTest;
14211444
1422 var tmp = tmpDir(.{});1445 var tmp = tmpDir(.{});
1423 defer tmp.cleanup();1446 defer tmp.cleanup();
1424 const file = try tmp.dir.createFile("test_file", .{ .read = true });1447 const file = try tmp.dir.createFile("test_file", .{ .read = true });
1448 defer file.close();
1425 // Create a file and make it read-only1449 // Create a file and make it read-only
1426 const metadata = try file.metadata();1450 const metadata = try file.metadata();
1427 var permissions = metadata.permissions();1451 var permissions = metadata.permissions();
1428 permissions.setReadOnly(true);1452 permissions.setReadOnly(true);
1429 try file.setPermissions(permissions);1453 try file.setPermissions(permissions);
1430 try testing.expectError(error.AccessDenied, tmp.dir.deleteFile("test_file"));1454 try tmp.dir.deleteFile("test_file"); // file is unmapped and deleted once last handle closed
1431 // Now make the file not read-only
1432 permissions.setReadOnly(false);
1433 try file.setPermissions(permissions);
1434 file.close();
1435 try tmp.dir.deleteFile("test_file");
1436}1455}
14371456
1438test "delete a setAsCwd directory on Windows" {1457test "delete a setAsCwd directory on Windows" {
lib/std/os/windows.zig+45-12
...@@ -937,19 +937,40 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -937,19 +937,40 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
937 .DELETE_PENDING => return,937 .DELETE_PENDING => return,
938 else => return unexpectedStatus(rc),938 else => return unexpectedStatus(rc),
939 }939 }
940 var file_dispo = FILE_DISPOSITION_INFORMATION{940 defer CloseHandle(tmp_handle);
941 .DeleteFile = TRUE,941
942 };942 if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) {
943 rc = ntdll.NtSetInformationFile(943 // Deletion with posix semantics.
944 tmp_handle,944 var info = FILE_DISPOSITION_INFORMATION_EX{
945 &io,945 .Flags = FILE_DISPOSITION_DELETE |
946 &file_dispo,946 FILE_DISPOSITION_POSIX_SEMANTICS |
947 @sizeOf(FILE_DISPOSITION_INFORMATION),947 FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
948 .FileDispositionInformation,948 };
949 );949
950 CloseHandle(tmp_handle);950 rc = ntdll.NtSetInformationFile(
951 tmp_handle,
952 &io,
953 &info,
954 @sizeOf(FILE_DISPOSITION_INFORMATION_EX),
955 .FileDispositionInformationEx,
956 );
957 } else {
958 // Deletion with file pending semantics, which requires waiting or moving
959 // files to get them removed (from here).
960 var file_dispo = FILE_DISPOSITION_INFORMATION{
961 .DeleteFile = TRUE,
962 };
963
964 rc = ntdll.NtSetInformationFile(
965 tmp_handle,
966 &io,
967 &file_dispo,
968 @sizeOf(FILE_DISPOSITION_INFORMATION),
969 .FileDispositionInformation,
970 );
971 }
951 switch (rc) {972 switch (rc) {
952 .SUCCESS => return,973 .SUCCESS => {},
953 .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,974 .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
954 .INVALID_PARAMETER => unreachable,975 .INVALID_PARAMETER => unreachable,
955 .CANNOT_DELETE => return error.AccessDenied,976 .CANNOT_DELETE => return error.AccessDenied,
...@@ -2574,6 +2595,18 @@ pub const FILE_NAME_INFORMATION = extern struct {...@@ -2574,6 +2595,18 @@ pub const FILE_NAME_INFORMATION = extern struct {
2574 FileName: [1]WCHAR,2595 FileName: [1]WCHAR,
2575};2596};
25762597
2598pub const FILE_DISPOSITION_INFORMATION_EX = extern struct {
2599 /// combination of FILE_DISPOSITION_* flags
2600 Flags: ULONG,
2601};
2602
2603const FILE_DISPOSITION_DO_NOT_DELETE: ULONG = 0x00000000;
2604const FILE_DISPOSITION_DELETE: ULONG = 0x00000001;
2605const FILE_DISPOSITION_POSIX_SEMANTICS: ULONG = 0x00000002;
2606const FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK: ULONG = 0x00000004;
2607const FILE_DISPOSITION_ON_CLOSE: ULONG = 0x00000008;
2608const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: ULONG = 0x00000010;
2609
2577pub const FILE_RENAME_INFORMATION = extern struct {2610pub const FILE_RENAME_INFORMATION = extern struct {
2578 ReplaceIfExists: BOOLEAN,2611 ReplaceIfExists: BOOLEAN,
2579 RootDirectory: ?HANDLE,2612 RootDirectory: ?HANDLE,