Function ReadLink [src]

Prototype

pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8

Parameters

dir: ?HANDLEsub_path_w: []const u16out_buffer: []u8

Possible Errors

AccessDenied
FileNotFound
NameTooLong
NetworkNotFound
Unexpected
UnsupportedReparsePointType

Source

pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 { // Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper. // With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that // failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible // to open the symlink there and then. const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; var nt_name = UNICODE_STRING{ .Length = path_len_bytes, .MaximumLength = path_len_bytes, .Buffer = @constCast(sub_path_w.ptr), }; var attr = OBJECT_ATTRIBUTES{ .Length = @sizeOf(OBJECT_ATTRIBUTES), .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else dir, .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. .ObjectName = &nt_name, .SecurityDescriptor = null, .SecurityQualityOfService = null, }; var result_handle: HANDLE = undefined; var io: IO_STATUS_BLOCK = undefined; const rc = ntdll.NtCreateFile( &result_handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &io, null, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT, null, 0, ); switch (rc) { .SUCCESS => {}, .OBJECT_NAME_INVALID => unreachable, .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, .NO_MEDIA_IN_DEVICE => return error.FileNotFound, .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't .INVALID_PARAMETER => unreachable, .SHARING_VIOLATION => return error.AccessDenied, .ACCESS_DENIED => return error.AccessDenied, .PIPE_BUSY => return error.AccessDenied, .OBJECT_PATH_SYNTAX_BAD => unreachable, .OBJECT_NAME_COLLISION => unreachable, .FILE_IS_A_DIRECTORY => unreachable, else => return unexpectedStatus(rc), } defer CloseHandle(result_handle); var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined; _ = DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) { error.AccessDenied => return error.Unexpected, error.UnrecognizedVolume => return error.Unexpected, else => |e| return e, }; const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0])); switch (reparse_struct.ReparseTag) { IO_REPARSE_TAG_SYMLINK => { const buf: *const 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 & SYMLINK_FLAG_RELATIVE != 0; return parseReadlinkPath(path_buf[offset..][0..len], is_relative, out_buffer); }, IO_REPARSE_TAG_MOUNT_POINT => { const buf: *const 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); return parseReadlinkPath(path_buf[offset..][0..len], false, out_buffer); }, else => |value| { std.debug.print("unsupported symlink type: {}", .{value}); return error.UnsupportedReparsePointType; }, } }