| author | |
| committer | |
| log | e3736baddb8ecff90f0594be9f604c7484ce9aa2 |
| tree | b6b93f4565921de8452d66189642cb77a40e407c |
| parent | 8d0a8c28596985b1a308160b2f1b84df2b3a8a8b |
| parent | 7594d2c0977497c81db0c394f775832689bad492 |
| signature |
std.windows: use posix semantics to delete files, if available2 files changed, 72 insertions(+), 20 deletions(-)
lib/std/fs/test.zig+27-8| ... | ... | @@ -1416,23 +1416,42 @@ test "File.PermissionsUnix" { |
| 1416 | 1416 | try testing.expect(!permissions_unix.unixHas(.other, .execute)); |
| 1417 | 1417 | } |
| 1418 | 1418 | |
| 1419 | test "delete a read-only file on windows" { | |
| 1420 | if (builtin.os.tag != .windows) return error.SkipZigTest; | |
| 1419 | test "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 | ||
| 1441 | test "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; | |
| 1421 | 1444 | |
| 1422 | 1445 | var tmp = tmpDir(.{}); |
| 1423 | 1446 | defer tmp.cleanup(); |
| 1424 | 1447 | const file = try tmp.dir.createFile("test_file", .{ .read = true }); |
| 1448 | defer file.close(); | |
| 1425 | 1449 | // Create a file and make it read-only |
| 1426 | 1450 | const metadata = try file.metadata(); |
| 1427 | 1451 | var permissions = metadata.permissions(); |
| 1428 | 1452 | permissions.setReadOnly(true); |
| 1429 | 1453 | 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 | |
| 1436 | 1455 | } |
| 1437 | 1456 | |
| 1438 | 1457 | test "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 | 937 | .DELETE_PENDING => return, |
| 938 | 938 | else => return unexpectedStatus(rc), |
| 939 | 939 | } |
| 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 | } | |
| 951 | 972 | switch (rc) { |
| 952 | .SUCCESS => return, | |
| 973 | .SUCCESS => {}, | |
| 953 | 974 | .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, |
| 954 | 975 | .INVALID_PARAMETER => unreachable, |
| 955 | 976 | .CANNOT_DELETE => return error.AccessDenied, |
| ... | ... | @@ -2574,6 +2595,18 @@ pub const FILE_NAME_INFORMATION = extern struct { |
| 2574 | 2595 | FileName: [1]WCHAR, |
| 2575 | 2596 | }; |
| 2576 | 2597 | |
| 2598 | pub const FILE_DISPOSITION_INFORMATION_EX = extern struct { | |
| 2599 | /// combination of FILE_DISPOSITION_* flags | |
| 2600 | Flags: ULONG, | |
| 2601 | }; | |
| 2602 | ||
| 2603 | const FILE_DISPOSITION_DO_NOT_DELETE: ULONG = 0x00000000; | |
| 2604 | const FILE_DISPOSITION_DELETE: ULONG = 0x00000001; | |
| 2605 | const FILE_DISPOSITION_POSIX_SEMANTICS: ULONG = 0x00000002; | |
| 2606 | const FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK: ULONG = 0x00000004; | |
| 2607 | const FILE_DISPOSITION_ON_CLOSE: ULONG = 0x00000008; | |
| 2608 | const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: ULONG = 0x00000010; | |
| 2609 | ||
| 2577 | 2610 | pub const FILE_RENAME_INFORMATION = extern struct { |
| 2578 | 2611 | ReplaceIfExists: BOOLEAN, |
| 2579 | 2612 | RootDirectory: ?HANDLE, |