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)