DoxigAlpha

getFdPath

Return canonical path of handle fd.

This function is very host-specific and is not universally supported by all hosts. For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is unsupported on WASI.

  • On Windows, the result is encoded as WTF-8.
  • On other platforms, the result is an opaque sequence of bytes with no particular encoding.

Calling this function is usually a bug.

Function parameters

Parameters

#
fd:std.posix.fd_t
out_buffer:*[max_path_bytes]u8

Type definitions in this namespace

Types

#

Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string.

Functions

#
accessW
Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string.
getFdPath
Return canonical path of handle `fd`.
fstatat_wasi
WASI-only.

See also `getenv`.

Values

#
environ
See also `getenv`.
argv
Populated by startup code before main().

Source

Implementation

#
pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix.RealPathError![]u8 {
    if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
        @compileError("querying for canonical path of a handle is unsupported on this host");
    }
    switch (native_os) {
        .windows => {
            var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
            const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);

            const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
            return out_buffer[0..end_index];
        },
        .macos, .ios, .watchos, .tvos, .visionos => {
            // On macOS, we can use F.GETPATH fcntl command to query the OS for
            // the path to the file descriptor.
            @memset(out_buffer[0..max_path_bytes], 0);
            switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, out_buffer))) {
                .SUCCESS => {},
                .BADF => return error.FileNotFound,
                .NOSPC => return error.NameTooLong,
                .NOENT => return error.FileNotFound,
                // TODO man pages for fcntl on macOS don't really tell you what
                // errno values to expect when command is F.GETPATH...
                else => |err| return posix.unexpectedErrno(err),
            }
            const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
            return out_buffer[0..len];
        },
        .linux, .serenity => {
            var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
            const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable;

            const target = posix.readlinkZ(proc_path, out_buffer) catch |err| {
                switch (err) {
                    error.NotLink => unreachable,
                    error.BadPathName => unreachable,
                    error.InvalidUtf8 => unreachable, // WASI-only
                    error.InvalidWtf8 => unreachable, // Windows-only
                    error.UnsupportedReparsePointType => unreachable, // Windows-only
                    error.NetworkNotFound => unreachable, // Windows-only
                    else => |e| return e,
                }
            };
            return target;
        },
        .solaris, .illumos => {
            var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
            const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/path/{d}", .{fd}) catch unreachable;

            const target = posix.readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
                error.UnsupportedReparsePointType => unreachable,
                error.NotLink => unreachable,
                error.InvalidUtf8 => unreachable, // WASI-only
                else => |e| return e,
            };
            return target;
        },
        .freebsd => {
            var kfile: std.c.kinfo_file = undefined;
            kfile.structsize = std.c.KINFO_FILE_SIZE;
            switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) {
                .SUCCESS => {},
                .BADF => return error.FileNotFound,
                else => |err| return posix.unexpectedErrno(err),
            }
            const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse max_path_bytes;
            if (len == 0) return error.NameTooLong;
            const result = out_buffer[0..len];
            @memcpy(result, kfile.path[0..len]);
            return result;
        },
        .dragonfly => {
            @memset(out_buffer[0..max_path_bytes], 0);
            switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
                .SUCCESS => {},
                .BADF => return error.FileNotFound,
                .RANGE => return error.NameTooLong,
                else => |err| return posix.unexpectedErrno(err),
            }
            const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
            return out_buffer[0..len];
        },
        .netbsd => {
            @memset(out_buffer[0..max_path_bytes], 0);
            switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
                .SUCCESS => {},
                .ACCES => return error.AccessDenied,
                .BADF => return error.FileNotFound,
                .NOENT => return error.FileNotFound,
                .NOMEM => return error.SystemResources,
                .RANGE => return error.NameTooLong,
                else => |err| return posix.unexpectedErrno(err),
            }
            const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse max_path_bytes;
            return out_buffer[0..len];
        },
        else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above
    }
}