Print this page
12046 Provide /proc/<PID>/fdinfo/

@@ -21,10 +21,11 @@
 
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018, Joyent, Inc.
  * Copyright (c) 2017 by Delphix. All rights reserved.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*      Copyright (c) 1984,      1986, 1987, 1988, 1989 AT&T    */
 /*        All Rights Reserved   */
 

@@ -152,21 +153,23 @@
                 "cwd" },
         { PR_ROOTDIR,   20 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "root" },
         { PR_FDDIR,     21 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "fd" },
-        { PR_OBJECTDIR, 22 * sizeof (prdirent_t), sizeof (prdirent_t),
+        { PR_FDINFODIR, 22 * sizeof (prdirent_t), sizeof (prdirent_t),
+                "fdinfo" },
+        { PR_OBJECTDIR, 23 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "object" },
-        { PR_LWPDIR,    23 * sizeof (prdirent_t), sizeof (prdirent_t),
+        { PR_LWPDIR,    24 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "lwp" },
-        { PR_PRIV,      24 * sizeof (prdirent_t), sizeof (prdirent_t),
+        { PR_PRIV,      25 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "priv" },
-        { PR_PATHDIR,   25 * sizeof (prdirent_t), sizeof (prdirent_t),
+        { PR_PATHDIR,   26 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "path" },
-        { PR_CTDIR,     26 * sizeof (prdirent_t), sizeof (prdirent_t),
+        { PR_CTDIR,     27 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "contracts" },
-        { PR_SECFLAGS,  27 * sizeof (prdirent_t), sizeof (prdirent_t),
+        { PR_SECFLAGS,  28 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "secflags" },
 #if defined(__x86)
         { PR_LDT,       28 * sizeof (prdirent_t), sizeof (prdirent_t),
                 "ldt" },
 #endif

@@ -594,11 +597,12 @@
         pr_read_xregs(), pr_read_priv(),
         pr_read_spymaster(), pr_read_secflags(),
 #if defined(__sparc)
         pr_read_gwindows(), pr_read_asrs(),
 #endif
-        pr_read_piddir(), pr_read_pidfile(), pr_read_opagedata();
+        pr_read_piddir(), pr_read_pidfile(), pr_read_opagedata(),
+        pr_read_fdinfo();
 
 static int (*pr_read_function[PR_NFILES])() = {
         pr_read_inval,          /* /proc                                */
         pr_read_inval,          /* /proc/self                           */
         pr_read_piddir,         /* /proc/<pid> (old /proc read())       */

@@ -623,10 +627,12 @@
         pr_read_watch,          /* /proc/<pid>/watch                    */
         pr_read_inval,          /* /proc/<pid>/cwd                      */
         pr_read_inval,          /* /proc/<pid>/root                     */
         pr_read_inval,          /* /proc/<pid>/fd                       */
         pr_read_inval,          /* /proc/<pid>/fd/nn                    */
+        pr_read_inval,          /* /proc/<pid>/fdinfo                   */
+        pr_read_fdinfo,         /* /proc/<pid>/fdinfo/nn                */
         pr_read_inval,          /* /proc/<pid>/object                   */
         pr_read_inval,          /* /proc/<pid>/object/xxx               */
         pr_read_inval,          /* /proc/<pid>/lwp                      */
         pr_read_inval,          /* /proc/<pid>/lwp/<lwpid>              */
         pr_read_inval,          /* /proc/<pid>/lwp/<lwpid>/lwpctl       */

@@ -811,10 +817,94 @@
         }
         return (error);
 }
 
 static int
+pr_read_fdinfo(prnode_t *pnp, uio_t *uiop)
+{
+        prfdinfov2_t *fdinfo;
+        list_t data;
+        proc_t *p;
+        vnode_t *vp;
+        uint_t fd;
+        file_t *fp;
+        cred_t *cred;
+        short ufp_flag;
+        int error = 0;
+
+        ASSERT(pnp->pr_type == PR_FDINFO);
+
+        /*
+         * This is a guess at the size of the structure that needs to
+         * be returned. It's a balance between not allocating too much more
+         * space than is required and not requiring too many subsequent
+         * reallocations. Allocate it before acquiring the process lock.
+         */
+        pr_iol_initlist(&data, sizeof (prfdinfov2_t) + MAXPATHLEN + 2, 1);
+
+        if ((error = prlock(pnp, ZNO)) != 0) {
+                pr_iol_freelist(&data);
+                return (error);
+        }
+
+        p = pnp->pr_common->prc_proc;
+
+        if ((p->p_flag & SSYS) || p->p_as == &kas) {
+                prunlock(pnp);
+                pr_iol_freelist(&data);
+                return (0);
+        }
+
+        fd = pnp->pr_index;
+
+        /* Fetch and lock the file_t for this descriptor */
+        fp = pr_getf(p, fd, &ufp_flag);
+
+        if (fp == NULL) {
+                error = ENOENT;
+                prunlock(pnp);
+                goto out;
+        }
+
+        vp = fp->f_vnode;
+        VN_HOLD(vp);
+
+        /*
+         * For fdinfo, we don't want to include the placeholder pr_misc at the
+         * end of the struct. We'll termninate the data with an empty pr_misc
+         * header before returning.
+         */
+
+        fdinfo = pr_iol_newbuf(&data, offsetof(prfdinfov2_t, pr_misc));
+        fdinfo->pr_fd = fd;
+        fdinfo->pr_fdflags = ufp_flag;
+        fdinfo->pr_fileflags = fp->f_flag2 << 16 | fp->f_flag;
+        if ((fdinfo->pr_fileflags & (FSEARCH | FEXEC)) == 0)
+                fdinfo->pr_fileflags += FOPEN;
+        fdinfo->pr_offset = fp->f_offset;
+        cred = fp->f_cred;
+        crhold(cred);
+        pr_releasef(p, fd);
+
+        prunlock(pnp);
+
+        error = prgetfdinfo(p, vp, fdinfo, cred, &data);
+
+        crfree(cred);
+
+        VN_RELE(vp);
+
+out:
+        if (error == 0)
+                error = pr_iol_uiomove_and_free(&data, uiop, error);
+        else
+                pr_iol_freelist(&data);
+
+        return (error);
+}
+
+static int
 pr_read_lpsinfo(prnode_t *pnp, uio_t *uiop)
 {
         proc_t *p;
         kthread_t *t;
         lwpdir_t *ldp;

@@ -1833,10 +1923,12 @@
         pr_read_watch_32,       /* /proc/<pid>/watch                    */
         pr_read_inval,          /* /proc/<pid>/cwd                      */
         pr_read_inval,          /* /proc/<pid>/root                     */
         pr_read_inval,          /* /proc/<pid>/fd                       */
         pr_read_inval,          /* /proc/<pid>/fd/nn                    */
+        pr_read_inval,          /* /proc/<pid>/fdinfo                   */
+        pr_read_fdinfo,         /* /proc/<pid>/fdinfo/nn                */
         pr_read_inval,          /* /proc/<pid>/object                   */
         pr_read_inval,          /* /proc/<pid>/object/xxx               */
         pr_read_inval,          /* /proc/<pid>/lwp                      */
         pr_read_inval,          /* /proc/<pid>/lwp/<lwpid>              */
         pr_read_inval,          /* /proc/<pid>/lwp/<lwpid>/lwpctl       */

@@ -3078,13 +3170,34 @@
         case PR_CT:
                 vap->va_type = VLNK;
                 vap->va_size = 0;
                 break;
         case PR_FDDIR:
+        case PR_FDINFODIR:
                 vap->va_nlink = 2;
                 vap->va_size = (P_FINFO(p)->fi_nfiles + 2) * PRSDSIZE;
                 break;
+        case PR_FDINFO: {
+                file_t *fp;
+                vnode_t *vp;
+                int fd = pnp->pr_index;
+
+                fp = pr_getf(p, fd, NULL);
+                if (fp == NULL) {
+                        prunlock(pnp);
+                        return (ENOENT);
+                }
+                vp = fp->f_vnode;
+                VN_HOLD(vp);
+                pr_releasef(p, fd);
+                prunlock(pnp);
+                vap->va_size = prgetfdinfosize(p, vp, cr);
+                VN_RELE(vp);
+                /* prunlock() has already been called. */
+                vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+                return (0);
+        }
         case PR_LWPDIR:
                 /*
                  * va_nlink: count each lwp as a directory link.
                  * va_size: size of p_lwpdir + 2
                  */

@@ -3405,12 +3518,12 @@
 /*
  * Array of lookup functions, indexed by /proc file type.
  */
 static vnode_t *pr_lookup_notdir(), *pr_lookup_procdir(), *pr_lookup_piddir(),
         *pr_lookup_objectdir(), *pr_lookup_lwpdir(), *pr_lookup_lwpiddir(),
-        *pr_lookup_fddir(), *pr_lookup_pathdir(), *pr_lookup_tmpldir(),
-        *pr_lookup_ctdir();
+        *pr_lookup_fddir(), *pr_lookup_fdinfodir(), *pr_lookup_pathdir(),
+        *pr_lookup_tmpldir(), *pr_lookup_ctdir();
 
 static vnode_t *(*pr_lookup_function[PR_NFILES])() = {
         pr_lookup_procdir,      /* /proc                                */
         pr_lookup_notdir,       /* /proc/self                           */
         pr_lookup_piddir,       /* /proc/<pid>                          */

@@ -3435,10 +3548,12 @@
         pr_lookup_notdir,       /* /proc/<pid>/watch                    */
         pr_lookup_notdir,       /* /proc/<pid>/cwd                      */
         pr_lookup_notdir,       /* /proc/<pid>/root                     */
         pr_lookup_fddir,        /* /proc/<pid>/fd                       */
         pr_lookup_notdir,       /* /proc/<pid>/fd/nn                    */
+        pr_lookup_fdinfodir,    /* /proc/<pid>/fdinfo                   */
+        pr_lookup_notdir,       /* /proc/<pid>/fdinfo/nn                */
         pr_lookup_objectdir,    /* /proc/<pid>/object                   */
         pr_lookup_notdir,       /* /proc/<pid>/object/xxx               */
         pr_lookup_lwpdir,       /* /proc/<pid>/lwp                      */
         pr_lookup_lwpiddir,     /* /proc/<pid>/lwp/<lwpid>              */
         pr_lookup_notdir,       /* /proc/<pid>/lwp/<lwpid>/lwpctl       */

@@ -3522,11 +3637,12 @@
                     direntflags, realpnp));
         default:
                 break;
         }
 
-        if ((type == PR_OBJECTDIR || type == PR_FDDIR || type == PR_PATHDIR) &&
+        if ((type == PR_OBJECTDIR || type == PR_FDDIR ||
+            type == PR_FDINFODIR || type == PR_PATHDIR) &&
             (error = praccess(dp, VEXEC, 0, cr, ct)) != 0)
                 return (error);
 
         /* XXX - Do we need to pass ct, direntflags, or realpnp? */
         *vpp = (pr_lookup_function[type](dp, comp));

@@ -4136,23 +4252,21 @@
         vnode_t *vp = NULL;
         proc_t *p;
         file_t *fp;
         uint_t fd;
         int c;
-        uf_entry_t *ufp;
-        uf_info_t *fip;
 
         ASSERT(dpnp->pr_type == PR_FDDIR);
 
         fd = 0;
         while ((c = *comp++) != '\0') {
                 int ofd;
                 if (c < '0' || c > '9')
                         return (NULL);
                 ofd = fd;
-                fd = 10*fd + c - '0';
-                if (fd/10 != ofd)       /* integer overflow */
+                fd = 10 * fd + c - '0';
+                if (fd / 10 != ofd)     /* integer overflow */
                         return (NULL);
         }
 
         pnp = prgetnode(dp, PR_FD);
 

@@ -4165,33 +4279,28 @@
                 prunlock(dpnp);
                 prfreenode(pnp);
                 return (NULL);
         }
 
-        fip = P_FINFO(p);
-        mutex_exit(&p->p_lock);
-        mutex_enter(&fip->fi_lock);
-        if (fd < fip->fi_nfiles) {
-                UF_ENTER(ufp, fip, fd);
-                if ((fp = ufp->uf_file) != NULL) {
+        if ((fp = pr_getf(p, fd, NULL)) != NULL) {
                         pnp->pr_mode = 07111;
                         if (fp->f_flag & FREAD)
                                 pnp->pr_mode |= 0444;
                         if (fp->f_flag & FWRITE)
                                 pnp->pr_mode |= 0222;
                         vp = fp->f_vnode;
                         VN_HOLD(vp);
+                pr_releasef(p, fd);
                 }
-                UF_EXIT(ufp);
-        }
-        mutex_exit(&fip->fi_lock);
-        mutex_enter(&p->p_lock);
+
         prunlock(dpnp);
 
-        if (vp == NULL)
+        if (vp == NULL) {
                 prfreenode(pnp);
-        else {
+                return (NULL);
+        }
+
                 /*
                  * Fill in the prnode so future references will
                  * be able to find the underlying object's vnode.
                  * Don't link this prnode into the list of all
                  * prnodes for the process; this is a one-use node.

@@ -4202,12 +4311,58 @@
                 vp = PTOV(pnp);
                 if (pnp->pr_realvp->v_type == VDIR) {
                         vp->v_type = VDIR;
                         vp->v_flag |= VTRAVERSE;
                 }
+
+        return (vp);
+}
+
+static vnode_t *
+pr_lookup_fdinfodir(vnode_t *dp, char *comp)
+{
+        prnode_t *dpnp = VTOP(dp);
+        prnode_t *pnp;
+        vnode_t *vp = NULL;
+        proc_t *p;
+        uint_t fd;
+        int c;
+
+        ASSERT(dpnp->pr_type == PR_FDINFODIR);
+
+        fd = 0;
+        while ((c = *comp++) != '\0') {
+                int ofd;
+                if (c < '0' || c > '9')
+                        return (NULL);
+                ofd = fd;
+                fd = 10 * fd + c - '0';
+                if (fd / 10 != ofd)     /* integer overflow */
+                        return (NULL);
         }
 
+        pnp = prgetnode(dp, PR_FDINFO);
+
+        if (prlock(dpnp, ZNO) != 0) {
+                prfreenode(pnp);
+                return (NULL);
+        }
+        p = dpnp->pr_common->prc_proc;
+        if ((p->p_flag & SSYS) || p->p_as == &kas) {
+                prunlock(dpnp);
+                prfreenode(pnp);
+                return (NULL);
+        }
+
+        pnp->pr_common = dpnp->pr_common;
+        pnp->pr_pcommon = dpnp->pr_pcommon;
+        pnp->pr_parent = dp;
+        pnp->pr_index = fd;
+        VN_HOLD(dp);
+        prunlock(dpnp);
+        vp = PTOV(pnp);
+
         return (vp);
 }
 
 static vnode_t *
 pr_lookup_pathdir(vnode_t *dp, char *comp)

@@ -4665,10 +4820,11 @@
                 break;
 
         case PR_CURDIR:
         case PR_ROOTDIR:
         case PR_FDDIR:
+        case PR_FDINFODIR:
         case PR_OBJECTDIR:
         case PR_PATHDIR:
         case PR_CTDIR:
         case PR_TMPLDIR:
                 vp->v_type = VDIR;

@@ -4797,12 +4953,12 @@
 /*
  * Array of readdir functions, indexed by /proc file type.
  */
 static int pr_readdir_notdir(), pr_readdir_procdir(), pr_readdir_piddir(),
         pr_readdir_objectdir(), pr_readdir_lwpdir(), pr_readdir_lwpiddir(),
-        pr_readdir_fddir(), pr_readdir_pathdir(), pr_readdir_tmpldir(),
-        pr_readdir_ctdir();
+        pr_readdir_fddir(), pr_readdir_fdinfodir(), pr_readdir_pathdir(),
+        pr_readdir_tmpldir(), pr_readdir_ctdir();
 
 static int (*pr_readdir_function[PR_NFILES])() = {
         pr_readdir_procdir,     /* /proc                                */
         pr_readdir_notdir,      /* /proc/self                           */
         pr_readdir_piddir,      /* /proc/<pid>                          */

@@ -4827,10 +4983,12 @@
         pr_readdir_notdir,      /* /proc/<pid>/watch                    */
         pr_readdir_notdir,      /* /proc/<pid>/cwd                      */
         pr_readdir_notdir,      /* /proc/<pid>/root                     */
         pr_readdir_fddir,       /* /proc/<pid>/fd                       */
         pr_readdir_notdir,      /* /proc/<pid>/fd/nn                    */
+        pr_readdir_fdinfodir,   /* /proc/<pid>/fdinfo                   */
+        pr_readdir_notdir,      /* /proc/<pid>/fdinfo/nn                */
         pr_readdir_objectdir,   /* /proc/<pid>/object                   */
         pr_readdir_notdir,      /* /proc/<pid>/object/xxx               */
         pr_readdir_lwpdir,      /* /proc/<pid>/lwp                      */
         pr_readdir_lwpiddir,    /* /proc/<pid>/lwp/<lwpid>              */
         pr_readdir_notdir,      /* /proc/<pid>/lwp/<lwpid>/lwpctl       */

@@ -5359,33 +5517,34 @@
         if (eofp)
                 *eofp = (uiop->uio_offset >= sizeof (lwpiddir));
         return (0);
 }
 
-/* ARGSUSED */
+/*
+ * Helper function for reading a directory which lists open file desciptors
+ */
 static int
-pr_readdir_fddir(prnode_t *pnp, uio_t *uiop, int *eofp)
+pr_readdir_fdlist(prnode_t *pnp, uio_t *uiop, int *eofp,
+    prnodetype_t dirtype, prnodetype_t entrytype)
 {
         gfs_readdir_state_t gstate;
         int error, eof = 0;
         offset_t n;
         proc_t *p;
         int pslot;
         int fddirsize;
         uf_info_t *fip;
 
-        ASSERT(pnp->pr_type == PR_FDDIR);
-
         if ((error = prlock(pnp, ZNO)) != 0)
                 return (error);
         p = pnp->pr_common->prc_proc;
         pslot = p->p_slot;
         fip = P_FINFO(p);
         mutex_exit(&p->p_lock);
 
         if ((error = gfs_readdir_init(&gstate, PLNSIZ, PRSDSIZE, uiop,
-            pmkino(0, pslot, PR_PIDDIR), pmkino(0, pslot, PR_FDDIR), 0)) != 0) {
+            pmkino(0, pslot, PR_PIDDIR), pmkino(0, pslot, dirtype), 0)) != 0) {
                 mutex_enter(&p->p_lock);
                 prunlock(pnp);
                 return (error);
         }
 

@@ -5412,11 +5571,11 @@
                         eof = 1;
                         break;
                 }
 
                 error = gfs_readdir_emitn(&gstate, uiop, n,
-                    pmkino(n, pslot, PR_FD), n);
+                    pmkino(n, pslot, entrytype), n);
                 if (error)
                         break;
         }
 
         mutex_exit(&fip->fi_lock);

@@ -5424,10 +5583,28 @@
         prunlock(pnp);
 
         return (gfs_readdir_fini(&gstate, error, eofp, eof));
 }
 
+static int
+pr_readdir_fddir(prnode_t *pnp, uio_t *uiop, int *eofp)
+{
+
+        ASSERT(pnp->pr_type == PR_FDDIR);
+
+        return (pr_readdir_fdlist(pnp, uiop, eofp, pnp->pr_type, PR_FD));
+}
+
+static int
+pr_readdir_fdinfodir(prnode_t *pnp, uio_t *uiop, int *eofp)
+{
+
+        ASSERT(pnp->pr_type == PR_FDINFODIR);
+
+        return (pr_readdir_fdlist(pnp, uiop, eofp, pnp->pr_type, PR_FDINFO));
+}
+
 /* ARGSUSED */
 static int
 pr_readdir_pathdir(prnode_t *pnp, uio_t *uiop, int *eofp)
 {
         longlong_t bp[DIRENT64_RECLEN(64) / sizeof (longlong_t)];

@@ -5733,10 +5910,11 @@
         prnode_t *opnp = NULL;
 
         switch (type) {
         case PR_OBJECT:
         case PR_FD:
+        case PR_FDDIR:
         case PR_SELF:
         case PR_PATH:
                 /* These are not linked into the usual lists */
                 ASSERT(vp->v_count == 1);
                 if ((dp = pnp->pr_parent) != NULL)