Merge pull request #15316 from xEgoist/fileDisposition

windows: use NtSetInformationFile in DeleteFile.
This commit is contained in:
Andrew Kelley 2023-04-20 16:30:27 -07:00 committed by GitHub
commit a86759984c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 7 deletions

View File

@ -488,10 +488,7 @@ test "deleteDir" {
dir.close();
// deleting a non-empty directory
// TODO: Re-enable this check on Windows, see https://github.com/ziglang/zig/issues/5537
if (builtin.os.tag != .windows) {
try testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
}
try testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
dir = try tmp_dir.dir.openDir("test_dir", .{});
try dir.deleteFile("test_file");
@ -1418,3 +1415,22 @@ test "File.PermissionsUnix" {
try testing.expect(permissions_unix.unixHas(.user, .execute));
try testing.expect(!permissions_unix.unixHas(.other, .execute));
}
test "delete a read-only file on windows" {
if (builtin.os.tag != .windows) return error.SkipZigTest;
var tmp = tmpDir(.{});
defer tmp.cleanup();
const file = try tmp.dir.createFile("test_file", .{ .read = true });
// 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);
file.close();
try tmp.dir.deleteFile("test_file");
}

View File

@ -870,6 +870,7 @@ pub const DeleteFileError = error{
Unexpected,
NotDir,
IsDir,
DirNotEmpty,
};
pub const DeleteFileOptions = struct {
@ -879,9 +880,9 @@ pub const DeleteFileOptions = struct {
pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
const create_options_flags: ULONG = if (options.remove_dir)
FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
else
FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
const path_len_bytes = @intCast(u16, sub_path_w.len * 2);
var nt_name = UNICODE_STRING{
@ -924,7 +925,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
0,
);
switch (rc) {
.SUCCESS => return CloseHandle(tmp_handle),
.SUCCESS => {},
.OBJECT_NAME_INVALID => unreachable,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
@ -932,7 +933,28 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
.SHARING_VIOLATION => return error.FileBusy,
.ACCESS_DENIED => return error.AccessDenied,
.DELETE_PENDING => return,
else => return unexpectedStatus(rc),
}
var file_dispo = FILE_DISPOSITION_INFORMATION{
.DeleteFile = TRUE,
};
rc = ntdll.NtSetInformationFile(
tmp_handle,
&io,
&file_dispo,
@sizeOf(FILE_DISPOSITION_INFORMATION),
.FileDispositionInformation,
);
CloseHandle(tmp_handle);
switch (rc) {
.SUCCESS => return,
.DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
.INVALID_PARAMETER => unreachable,
.CANNOT_DELETE => return error.AccessDenied,
.MEDIA_WRITE_PROTECTED => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
else => return unexpectedStatus(rc),
}
}
@ -2470,6 +2492,10 @@ pub const FILE_INFORMATION_CLASS = enum(c_int) {
FileMaximumInformation,
};
pub const FILE_DISPOSITION_INFORMATION = extern struct {
DeleteFile: BOOLEAN,
};
pub const FILE_FS_DEVICE_INFORMATION = extern struct {
DeviceType: DEVICE_TYPE,
Characteristics: ULONG,