feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.dirReadLinkWindows
fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize
File
Code
fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
// before converting back to `buffer`.
var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path, .{});
const attr: windows.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: windows.IO_STATUS_BLOCK = undefined;
var result_handle: windows.HANDLE = undefined;
var attempt: u5 = 0;
var syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtCreateFile(
&result_handle,
.{
.SPECIFIC = .{ .FILE = .{
.READ_ATTRIBUTES = true,
} },
.STANDARD = .{ .SYNCHRONIZE = true },
},
&attr,
&io_status_block,
null,
.{ .NORMAL = true },
.VALID_FLAGS,
.OPEN,
.{
.DIRECTORY_FILE = false,
.NON_DIRECTORY_FILE = false,
.IO = .SYNCHRONOUS_NONALERT,
.OPEN_REPARSE_POINT = true,
},
null,
0,
)) {
.SUCCESS => {
syscall.finish();
break;
},
.CANCELLED => {
try syscall.checkCancel();
continue;
},
.SHARING_VIOLATION => {
// executable. However, there's a kernel bug: the error may be
// incorrectly returned for an indeterminate amount of time
// after an executable file is closed. Here we work around the
// kernel bug with retry attempts.
syscall.finish();
if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
try parking_sleep.sleep(.{ .duration = .{
.raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
.clock = .awake,
} });
attempt += 1;
syscall = try .start();
continue;
},
.DELETE_PENDING => {
// the file system, but it was deleted. However, the OS is not
// finished with the deletion operation, and so this CreateFile
// call has failed. Here, we simulate the kernel bug being
// fixed by sleeping and retrying until the error goes away.
syscall.finish();
if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
try parking_sleep.sleep(.{ .duration = .{
.raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
.clock = .awake,
} });
attempt += 1;
syscall = try .start();
continue;
},
.OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
.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),
.NO_MEDIA_IN_DEVICE => return syscall.fail(error.FileNotFound),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.PIPE_BUSY => return syscall.fail(error.AccessDenied),
.PIPE_NOT_AVAILABLE => return syscall.fail(error.FileNotFound),
.USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
.VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
.INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
.OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
.INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
else => |status| return syscall.unexpectedNtstatus(status),
};
defer windows.CloseHandle(result_handle);
var reparse_buf: [windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(windows.REPARSE_DATA_BUFFER)) = undefined;
switch ((try deviceIoControl(&.{
.file = .{ .handle = result_handle, .flags = .{ .nonblocking = false } },
.code = .GET_REPARSE_POINT,
.out = &reparse_buf,
})).u.Status) {
.SUCCESS => {},
.CANCELLED => unreachable,
.NOT_A_REPARSE_POINT => return error.NotLink,
else => |status| return windows.unexpectedStatus(status),
}
const reparse_struct: *const windows.REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf));
const IoReparseTagInt = @typeInfo(windows.IO_REPARSE_TAG).@"struct".backing_integer.?;
const result_w = switch (@as(IoReparseTagInt, @bitCast(reparse_struct.ReparseTag))) {
@as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.SYMLINK)) => r: {
const buf: *const windows.SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0;
break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w.data);
},
@as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: {
const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w.data);
},
else => return error.UnsupportedReparsePointType,
};
const len = std.unicode.calcWtf8Len(result_w);
if (len > buffer.len) return error.NameTooLong;
return std.unicode.wtf16LeToWtf8(buffer, result_w);
}