Commit 397e6547a9

Kim SHrier <gitkim@westryn.net>
2022-07-11 21:54:40
add FreeBSD support to std.os.getFdPath
This implementation uses the F_KINFO fcntl command added in FreeBSD 13 release. FreeBSD 12 users get a compile error. Co-authored-by: Stephen Gregoratto <dev@sgregoratto.me>
1 parent 4c7fe74
Changed files (2)
lib/std/c/freebsd.zig
@@ -19,6 +19,8 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
 pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;
 pub extern "c" fn malloc_usable_size(?*const anyopaque) usize;
 
+pub extern "c" fn getpid() pid_t;
+
 pub const sf_hdtr = extern struct {
     headers: [*]const iovec_const,
     hdr_cnt: c_int,
@@ -397,6 +399,127 @@ pub const sockaddr = extern struct {
     };
 };
 
+pub const CAP_RIGHTS_VERSION = 0;
+
+pub const cap_rights = extern struct {
+    rights: [CAP_RIGHTS_VERSION + 2]u64,
+};
+
+pub const kinfo_file = extern struct {
+    /// Size of this record.
+    /// A zero value is for the sentinel record at the end of an array.
+    structsize: c_int,
+    /// Descriptor type.
+    @"type": c_int,
+    /// Array index.
+    fd: fd_t,
+    /// Reference count.
+    ref_count: c_int,
+    /// Flags.
+    flags: c_int,
+    // 64bit padding.
+    _pad0: c_int,
+    /// Seek location.
+    offset: i64,
+    un: extern union {
+        socket: extern struct {
+            /// Sendq size.
+            sendq: u32,
+            /// Socket domain.
+            domain: c_int,
+            /// Socket type.
+            @"type": c_int,
+            /// Socket protocol.
+            protocol: c_int,
+            /// Socket address.
+            address: sockaddr.storage,
+            /// Peer address.
+            peer: sockaddr.storage,
+            /// Address of so_pcb.
+            pcb: u64,
+            /// Address of inp_ppcb.
+            inpcb: u64,
+            /// Address of unp_conn.
+            unpconn: u64,
+            /// Send buffer state.
+            snd_sb_state: u16,
+            /// Receive buffer state.
+            rcv_sb_state: u16,
+            /// Recvq size.
+            recvq: u32,
+        },
+        file: extern struct {
+            /// Vnode type.
+            @"type": i32,
+            // Reserved for future use
+            _spare1: [3]i32,
+            _spare2: [30]u64,
+            /// Vnode filesystem id.
+            fsid: u64,
+            /// File device.
+            rdev: u64,
+            /// Global file id.
+            fileid: u64,
+            /// File size.
+            size: u64,
+            /// fsid compat for FreeBSD 11.
+            fsid_freebsd11: u32,
+            /// rdev compat for FreeBSD 11.
+            rdev_freebsd11: u32,
+            /// File mode.
+            mode: u16,
+            // 64bit padding.
+            _pad0: u16,
+            _pad1: u32,
+        },
+        sem: extern struct {
+            _spare0: [4]u32,
+            _spare1: [32]u64,
+            /// Semaphore value.
+            value: u32,
+            /// Semaphore mode.
+            mode: u16,
+        },
+        pipe: extern struct {
+            _spare1: [4]u32,
+            _spare2: [32]u64,
+            addr: u64,
+            peer: u64,
+            buffer_cnt: u32,
+            // 64bit padding.
+            kf_pipe_pad0: [3]u32,
+        },
+        proc: extern struct {
+            _spare1: [4]u32,
+            _spare2: [32]u64,
+            pid: pid_t,
+        },
+        eventfd: extern struct {
+            value: u64,
+            flags: u32,
+        },
+    },
+    /// Status flags.
+    status: u16,
+    // 32-bit alignment padding.
+    _pad1: u16,
+    // Reserved for future use.
+    _spare: c_int,
+    /// Capability rights.
+    cap_rights: cap_rights,
+    /// Reserved for future cap_rights
+    _cap_spare: u64,
+    /// Path to file, if any.
+    path: [PATH_MAX - 1:0]u8,
+};
+
+pub const KINFO_FILE_SIZE = 1392;
+
+comptime {
+    std.debug.assert(@sizeOf(kinfo_file) == KINFO_FILE_SIZE);
+    std.debug.assert(@alignOf(kinfo_file) == @sizeOf(u64));
+}
+
 pub const CTL = struct {
     pub const KERN = 1;
     pub const DEBUG = 5;
@@ -405,6 +528,7 @@ pub const CTL = struct {
 pub const KERN = struct {
     pub const PROC = 14; // struct: process entries
     pub const PROC_PATHNAME = 12; // path to executable
+    pub const PROC_FILEDESC = 33; // file descriptors for process
     pub const IOV_MAX = 35;
 };
 
@@ -613,23 +737,67 @@ pub const O = struct {
     pub const NDELAY = NONBLOCK;
 };
 
+/// Command flags for fcntl(2).
 pub const F = struct {
+    /// Duplicate file descriptor.
     pub const DUPFD = 0;
+    /// Get file descriptor flags.
     pub const GETFD = 1;
+    /// Set file descriptor flags.
     pub const SETFD = 2;
+    /// Get file status flags.
     pub const GETFL = 3;
+    /// Set file status flags.
     pub const SETFL = 4;
 
+    /// Get SIGIO/SIGURG proc/pgrrp.
     pub const GETOWN = 5;
+    /// Set SIGIO/SIGURG proc/pgrrp.
     pub const SETOWN = 6;
 
+    /// Get record locking information.
     pub const GETLK = 11;
+    /// Set record locking information.
     pub const SETLK = 12;
+    /// Set record locking information and wait if blocked.
     pub const SETLKW = 13;
 
+    /// Debugging support for remote locks.
+    pub const SETLK_REMOTE = 14;
+    /// Read ahead.
+    pub const READAHEAD = 15;
+
+    /// DUPFD with FD_CLOEXEC set.
+    pub const DUPFD_CLOEXEC = 17;
+    /// DUP2FD with FD_CLOEXEC set.
+    pub const DUP2FD_CLOEXEC = 18;
+
+    pub const ADD_SEALS = 19;
+    pub const GET_SEALS = 20;
+    /// Return `kinfo_file` for a file descriptor.
+    pub const KINFO = 22;
+
+    // Seals (ADD_SEALS, GET_SEALS)
+    /// Prevent adding sealings.
+    pub const SEAL_SEAL = 0x0001;
+    /// May not shrink
+    pub const SEAL_SHRINK = 0x0002;
+    /// May not grow.
+    pub const SEAL_GROW = 0x0004;
+    /// May not write.
+    pub const SEAL_WRITE = 0x0008;
+
+    // Record locking flags (GETLK, SETLK, SETLKW).
+    /// Shared or read lock.
     pub const RDLCK = 1;
-    pub const WRLCK = 3;
+    /// Unlock.
     pub const UNLCK = 2;
+    /// Exclusive or write lock.
+    pub const WRLCK = 3;
+    /// Purge locks for a given system ID.
+    pub const UNLCKSYS = 4;
+    /// Cancel an async lock request.
+    pub const CANCEL = 5;
 
     pub const SETOWN_EX = 15;
     pub const GETOWN_EX = 16;
lib/std/os.zig
@@ -5167,8 +5167,8 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
 
 /// 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 or Windows, it is unsupported
-/// on FreeBSD, or WASI.
+/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
+/// unsupported on WASI.
 pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
     switch (builtin.os.tag) {
         .windows => {
@@ -5217,6 +5217,22 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
             };
             return target;
         },
+        .freebsd => {
+            comptime if (builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0 }) == .lt)
+                @compileError("querying for canonical path of a handle is unsupported on FreeBSD 12 and below");
+
+            var kfile: system.kinfo_file = undefined;
+            kfile.structsize = system.KINFO_FILE_SIZE;
+            switch (errno(system.fcntl(fd, system.F.KINFO, @ptrToInt(&kfile)))) {
+                .SUCCESS => {},
+                .BADF => return error.FileNotFound,
+                else => |err| return unexpectedErrno(err),
+            }
+
+            const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse MAX_PATH_BYTES;
+            mem.copy(u8, out_buffer, kfile.path[0..len]);
+            return out_buffer[0..len];
+        },
         else => @compileError("querying for canonical path of a handle is unsupported on this host"),
     }
 }