Merge pull request #16499 from squeek502/posix-semantics-2

windows.DeleteFile: Use FileDispositionInformationEx if possible, but fallback if not
This commit is contained in:
Andrew Kelley 2023-07-23 11:37:11 -07:00 committed by GitHub
commit b35874a429
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 27 deletions

View file

@ -1416,34 +1416,13 @@ test "File.PermissionsUnix" {
try testing.expect(!permissions_unix.unixHas(.other, .execute)); try testing.expect(!permissions_unix.unixHas(.other, .execute));
} }
test "delete a read-only file on windows with file pending semantics" { test "delete a read-only file on windows" {
if (builtin.os.tag != .windows or builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) if (builtin.os.tag != .windows)
return error.SkipZigTest; return error.SkipZigTest;
var tmp = tmpDir(.{}); var tmp = testing.tmpDir(.{});
defer tmp.cleanup(); 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 }); const file = try tmp.dir.createFile("test_file", .{ .read = true });
defer file.close(); defer file.close();
// Create a file and make it read-only // Create a file and make it read-only
@ -1451,7 +1430,21 @@ test "delete a read-only file on windows with posix semantis" {
var permissions = metadata.permissions(); var permissions = metadata.permissions();
permissions.setReadOnly(true); permissions.setReadOnly(true);
try file.setPermissions(permissions); try file.setPermissions(permissions);
try tmp.dir.deleteFile("test_file"); // file is unmapped and deleted once last handle closed
// If the OS and filesystem support it, POSIX_SEMANTICS and IGNORE_READONLY_ATTRIBUTE
// is used meaning that the deletion of a read-only file will succeed.
// Otherwise, this delete will fail and the read-only flag must be unset before it's
// able to be deleted.
const delete_result = tmp.dir.deleteFile("test_file");
if (delete_result) {
try testing.expectError(error.FileNotFound, tmp.dir.deleteFile("test_file"));
} else |err| {
try testing.expectEqual(@as(anyerror, error.AccessDenied), err);
// Now make the file not read-only
permissions.setReadOnly(false);
try file.setPermissions(permissions);
try tmp.dir.deleteFile("test_file");
}
} }
test "delete a setAsCwd directory on Windows" { test "delete a setAsCwd directory on Windows" {

View file

@ -940,8 +940,13 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
} }
defer CloseHandle(tmp_handle); defer CloseHandle(tmp_handle);
// FileDispositionInformationEx (and therefore FILE_DISPOSITION_POSIX_SEMANTICS and FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE)
// are only supported on NTFS filesystems, so the version check on its own is only a partial solution. To support non-NTFS filesystems
// like FAT32, we need to fallback to FileDispositionInformation if the usage of FileDispositionInformationEx gives
// us INVALID_PARAMETER.
var need_fallback = true;
if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) { if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) {
// Deletion with posix semantics. // Deletion with posix semantics if the filesystem supports it.
var info = FILE_DISPOSITION_INFORMATION_EX{ var info = FILE_DISPOSITION_INFORMATION_EX{
.Flags = FILE_DISPOSITION_DELETE | .Flags = FILE_DISPOSITION_DELETE |
FILE_DISPOSITION_POSIX_SEMANTICS | FILE_DISPOSITION_POSIX_SEMANTICS |
@ -955,7 +960,14 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
@sizeOf(FILE_DISPOSITION_INFORMATION_EX), @sizeOf(FILE_DISPOSITION_INFORMATION_EX),
.FileDispositionInformationEx, .FileDispositionInformationEx,
); );
} else { switch (rc) {
// INVALID_PARAMETER here means that the filesystem does not support FileDispositionInformationEx
.INVALID_PARAMETER => {},
// For all other statuses, fall down to the switch below to handle them.
else => need_fallback = false,
}
}
if (need_fallback) {
// Deletion with file pending semantics, which requires waiting or moving // Deletion with file pending semantics, which requires waiting or moving
// files to get them removed (from here). // files to get them removed (from here).
var file_dispo = FILE_DISPOSITION_INFORMATION{ var file_dispo = FILE_DISPOSITION_INFORMATION{