Skip to content

windows: use NtSetInformationFile in DeleteFile. #15316

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions lib/std/fs/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
}
32 changes: 29 additions & 3 deletions lib/std/os/windows.zig
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@ pub const DeleteFileError = error{
Unexpected,
NotDir,
IsDir,
DirNotEmpty,
};

pub const DeleteFileOptions = struct {
Expand All @@ -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{
Expand Down Expand Up @@ -924,15 +925,36 @@ 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,
.INVALID_PARAMETER => unreachable,
.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),
}
}
Expand Down Expand Up @@ -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,
Expand Down