feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.dirDeleteWindows
fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) (Dir.DeleteDirError || Dir.DeleteFileError)!void
File
Code
fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) (Dir.DeleteDirError || Dir.DeleteFileError)!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
const w = windows;
if (std.mem.eql(u8, sub_path, "..")) {
return error.FileBusy;
}
var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path, .{});
if (std.mem.eql(u8, sub_path, ".")) {
sub_path_w.len = 0;
}
const attr: w.OBJECT.ATTRIBUTES = .{
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
.ObjectName = @constCast(&sub_path_w.string()),
};
var io_status_block: w.IO_STATUS_BLOCK = undefined;
var tmp_handle: w.HANDLE = undefined;
{
const syscall: Syscall = try .start();
while (true) switch (w.ntdll.NtCreateFile(
&tmp_handle,
.{ .STANDARD = .{
.RIGHTS = .{ .DELETE = true },
.SYNCHRONIZE = true,
} },
&attr,
&io_status_block,
null,
.{},
.VALID_FLAGS,
.OPEN,
.{
.DIRECTORY_FILE = remove_dir,
.IO = .SYNCHRONOUS_NONALERT,
.NON_DIRECTORY_FILE = !remove_dir,
.OPEN_REPARSE_POINT = true,
},
null,
0,
)) {
.SUCCESS => break syscall.finish(),
.OBJECT_NAME_INVALID => |err| return syscall.ntstatusBug(err),
.OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
.OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
.BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound),
.BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound),
.INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
.FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
.NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
.SHARING_VIOLATION => return syscall.fail(error.FileBusy),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.DELETE_PENDING => return syscall.finish(),
else => |rc| return syscall.unexpectedNtstatus(rc),
};
}
defer w.CloseHandle(tmp_handle);
// - FILE_DISPOSITION_INFORMATION_EX requires >= win10_rs1
// (INVALID_INFO_CLASS is returned if not supported)
// - Requires the NTFS filesystem
// (on filesystems like FAT32, INVALID_PARAMETER is returned)
// - FILE_DISPOSITION_POSIX_SEMANTICS requires >= win10_rs1
// - FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
// (NOT_SUPPORTED is returned if a flag is unsupported)
//
// The strategy here is just to try using FileDispositionInformationEx and fall back to
// FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
const rc = rc: {
var info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
.DELETE = true,
.POSIX_SEMANTICS = true,
.IGNORE_READONLY_ATTRIBUTE = true,
} };
const syscall: Syscall = try .start();
while (true) switch (w.ntdll.NtSetInformationFile(
tmp_handle,
&io_status_block,
&info,
@sizeOf(w.FILE.DISPOSITION.INFORMATION.EX),
.DispositionEx,
)) {
.CANCELLED => {
try syscall.checkCancel();
continue;
},
.INVALID_PARAMETER,
.INVALID_INFO_CLASS,
.NOT_SUPPORTED,
=> break,
// For all other statuses, fall down to the switch below to handle them.
else => |rc| {
syscall.finish();
break :rc rc;
},
};
// files to get them removed (from here).
var file_dispo: w.FILE.DISPOSITION.INFORMATION = .{ .DeleteFile = .TRUE };
while (true) switch (w.ntdll.NtSetInformationFile(
tmp_handle,
&io_status_block,
&file_dispo,
@sizeOf(w.FILE.DISPOSITION.INFORMATION),
.Disposition,
)) {
.CANCELLED => {
try syscall.checkCancel();
continue;
},
else => |rc| {
syscall.finish();
break :rc rc;
},
};
};
switch (rc) {
.SUCCESS => {},
.DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
.INVALID_PARAMETER => |err| return w.statusBug(err),
.CANNOT_DELETE => return error.AccessDenied,
.MEDIA_WRITE_PROTECTED => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
else => return w.unexpectedStatus(rc),
}
}