Skip to content

Commit a867599

Browse files
authored
Merge pull request #15316 from xEgoist/fileDisposition
windows: use NtSetInformationFile in DeleteFile.
2 parents 8674418 + 8c79559 commit a867599

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

lib/std/fs/test.zig

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -488,10 +488,7 @@ test "deleteDir" {
488488
dir.close();
489489

490490
// deleting a non-empty directory
491-
// TODO: Re-enable this check on Windows, see https://github.com/ziglang/zig/issues/5537
492-
if (builtin.os.tag != .windows) {
493-
try testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
494-
}
491+
try testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
495492

496493
dir = try tmp_dir.dir.openDir("test_dir", .{});
497494
try dir.deleteFile("test_file");
@@ -1418,3 +1415,22 @@ test "File.PermissionsUnix" {
14181415
try testing.expect(permissions_unix.unixHas(.user, .execute));
14191416
try testing.expect(!permissions_unix.unixHas(.other, .execute));
14201417
}
1418+
1419+
test "delete a read-only file on windows" {
1420+
if (builtin.os.tag != .windows) return error.SkipZigTest;
1421+
1422+
var tmp = tmpDir(.{});
1423+
defer tmp.cleanup();
1424+
const file = try tmp.dir.createFile("test_file", .{ .read = true });
1425+
// Create a file and make it read-only
1426+
const metadata = try file.metadata();
1427+
var permissions = metadata.permissions();
1428+
permissions.setReadOnly(true);
1429+
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");
1436+
}

lib/std/os/windows.zig

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,7 @@ pub const DeleteFileError = error{
870870
Unexpected,
871871
NotDir,
872872
IsDir,
873+
DirNotEmpty,
873874
};
874875

875876
pub const DeleteFileOptions = struct {
@@ -879,9 +880,9 @@ pub const DeleteFileOptions = struct {
879880

880881
pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
881882
const create_options_flags: ULONG = if (options.remove_dir)
882-
FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
883+
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
883884
else
884-
FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
885+
FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
885886

886887
const path_len_bytes = @intCast(u16, sub_path_w.len * 2);
887888
var nt_name = UNICODE_STRING{
@@ -924,15 +925,36 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
924925
0,
925926
);
926927
switch (rc) {
927-
.SUCCESS => return CloseHandle(tmp_handle),
928+
.SUCCESS => {},
928929
.OBJECT_NAME_INVALID => unreachable,
929930
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
930931
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
931932
.INVALID_PARAMETER => unreachable,
932933
.FILE_IS_A_DIRECTORY => return error.IsDir,
933934
.NOT_A_DIRECTORY => return error.NotDir,
934935
.SHARING_VIOLATION => return error.FileBusy,
936+
.ACCESS_DENIED => return error.AccessDenied,
937+
.DELETE_PENDING => return,
938+
else => return unexpectedStatus(rc),
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);
951+
switch (rc) {
952+
.SUCCESS => return,
953+
.DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
954+
.INVALID_PARAMETER => unreachable,
935955
.CANNOT_DELETE => return error.AccessDenied,
956+
.MEDIA_WRITE_PROTECTED => return error.AccessDenied,
957+
.ACCESS_DENIED => return error.AccessDenied,
936958
else => return unexpectedStatus(rc),
937959
}
938960
}
@@ -2470,6 +2492,10 @@ pub const FILE_INFORMATION_CLASS = enum(c_int) {
24702492
FileMaximumInformation,
24712493
};
24722494

2495+
pub const FILE_DISPOSITION_INFORMATION = extern struct {
2496+
DeleteFile: BOOLEAN,
2497+
};
2498+
24732499
pub const FILE_FS_DEVICE_INFORMATION = extern struct {
24742500
DeviceType: DEVICE_TYPE,
24752501
Characteristics: ULONG,

0 commit comments

Comments
 (0)