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" {
14161416 try testing.expect(!permissions_unix.unixHas(.other, .execute));
14171417}
14181418
1419test "delete a read-only file on windows" {
1420 if (builtin.os.tag != .windows) return error.SkipZigTest;
1419test "delete a read-only file on windows with file pending semantics" {
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
14221445 var tmp = tmpDir(.{});
14231446 defer tmp.cleanup();
14241447 const file = try tmp.dir.createFile("test_file", .{ .read = true });
1448 defer file.close();
14251449 // Create a file and make it read-only
14261450 const metadata = try file.metadata();
14271451 var permissions = metadata.permissions();
14281452 permissions.setReadOnly(true);
14291453 try file.setPermissions(permissions);
1430 try testing.expectError(error.AccessDenied, tmp.dir.deleteFile("test_file"));
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");
1454 try tmp.dir.deleteFile("test_file"); // file is unmapped and deleted once last handle closed
14361455}
14371456
14381457test "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
937937 .DELETE_PENDING => return,
938938 else => return unexpectedStatus(rc),
939939 }
940 var file_dispo = FILE_DISPOSITION_INFORMATION{
941 .DeleteFile = TRUE,
942 };
943 rc = ntdll.NtSetInformationFile(
944 tmp_handle,
945 &io,
946 &file_dispo,
947 @sizeOf(FILE_DISPOSITION_INFORMATION),
948 .FileDispositionInformation,
949 );
950 CloseHandle(tmp_handle);
940 defer CloseHandle(tmp_handle);
941
942 if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) {
943 // Deletion with posix semantics.
944 var info = FILE_DISPOSITION_INFORMATION_EX{
945 .Flags = FILE_DISPOSITION_DELETE |
946 FILE_DISPOSITION_POSIX_SEMANTICS |
947 FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
948 };
949
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 }
951972 switch (rc) {
952 .SUCCESS => return,
973 .SUCCESS => {},
953974 .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
954975 .INVALID_PARAMETER => unreachable,
955976 .CANNOT_DELETE => return error.AccessDenied,
......@@ -2574,6 +2595,18 @@ pub const FILE_NAME_INFORMATION = extern struct {
25742595 FileName: [1]WCHAR,
25752596};
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
25772610pub const FILE_RENAME_INFORMATION = extern struct {
25782611 ReplaceIfExists: BOOLEAN,
25792612 RootDirectory: ?HANDLE,