| 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,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 | } |
| 1418 | 1418 | ||
| 1419 | test "delete a read-only file on windows" { | 1419 | test "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 | |||
| 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 | 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-only | 1449 | // 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 | } |
| 1437 | 1456 | ||
| 1438 | test "delete a setAsCwd directory on Windows" { | 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,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 | }; |
| 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 | pub const FILE_RENAME_INFORMATION = extern struct { | 2610 | pub const FILE_RENAME_INFORMATION = extern struct { |
| 2578 | ReplaceIfExists: BOOLEAN, | 2611 | ReplaceIfExists: BOOLEAN, |
| 2579 | RootDirectory: ?HANDLE, | 2612 | RootDirectory: ?HANDLE, |