--- old/usr/src/cmd/sgs/elfdump/common/elfdump.msg	Thu Dec  5 11:52:58 2019
+++ new/usr/src/cmd/sgs/elfdump/common/elfdump.msg	Thu Dec  5 11:52:58 2019
@@ -468,7 +468,7 @@
 @ MSG_CNOTE_DESC_PSINFO_T		"desc: (psinfo_t)"
 @ MSG_CNOTE_DESC_PSTATUS_T		"desc: (pstatus_t)"
 @ MSG_CNOTE_DESC_STRUCT_UTSNAME		"desc: (struct utsname)"
-@ MSG_CNOTE_DESC_PRFDINFO_T		"desc: (prfdinfo_t)"
+@ MSG_CNOTE_DESC_PRFDINFO_T		"desc: (prfdinfov1_t)"
 @ MSG_CNOTE_DESC_PRSECFLAGS_T		"desc: (prsecflags_t)"
 @ MSG_CNOTE_DESC_PRLWPNAME_T		"desc: (prlwpname_t)"
 
--- old/usr/src/cmd/sgs/elfdump/common/gen_layout_obj.c	Thu Dec  5 11:52:59 2019
+++ new/usr/src/cmd/sgs/elfdump/common/gen_layout_obj.c	Thu Dec  5 11:52:59 2019
@@ -57,6 +57,6 @@
 sysset_t sysset;
 timestruc_t ts;
 struct utsname uts;
-prfdinfo_t ptfd;
+prfdinfov1_t ptfd;
 prsecflags_t psf;
 prlwpname_t psn;
--- old/usr/src/cmd/sgs/elfdump/common/gen_struct_layout.c	Thu Dec  5 11:52:59 2019
+++ new/usr/src/cmd/sgs/elfdump/common/gen_struct_layout.c	Thu Dec  5 11:52:59 2019
@@ -568,22 +568,22 @@
 static void
 gen_prfdinfo(void)
 {
-	START(prfdinfo, prfdinfo_t);
+	START(prfdinfo, prfdinfov1_t);
 
-	SCALAR_FIELD(prfdinfo_t,	pr_fd,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_mode,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_uid,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_gid,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_major,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_minor,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_rmajor,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_rminor,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_ino,		0);
-	SCALAR_FIELD(prfdinfo_t,	pr_offset,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_size,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_fileflags,	0);
-	SCALAR_FIELD(prfdinfo_t,	pr_fdflags,	0);
-	ARRAY_FIELD(prfdinfo_t,		pr_path,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_fd,		0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_mode,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_uid,		0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_gid,		0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_major,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_minor,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_rmajor,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_rminor,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_ino,		0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_offset,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_size,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_fileflags,	0);
+	SCALAR_FIELD(prfdinfov1_t,	pr_fdflags,	0);
+	ARRAY_FIELD(prfdinfov1_t,	pr_path,	0);
 
 	END;
 }
--- old/usr/src/cmd/sgs/elfdump/common/struct_layout.h	Thu Dec  5 11:52:59 2019
+++ new/usr/src/cmd/sgs/elfdump/common/struct_layout.h	Thu Dec  5 11:52:59 2019
@@ -507,7 +507,7 @@
 } sl_utsname_layout_t;
 
 /*
- * Layout description of prdinfo_t, from <sys/procfs.h>.
+ * Layout description of prfdinfov1_t, from <sys/procfs.h>.
  */
 typedef struct {
 	sl_field_t		sizeof_struct;
--- old/usr/src/cmd/sgs/elfdump/common/struct_layout_amd64.c	Thu Dec  5 11:52:59 2019
+++ new/usr/src/cmd/sgs/elfdump/common/struct_layout_amd64.c	Thu Dec  5 11:52:59 2019
@@ -360,7 +360,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfov1_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- old/usr/src/cmd/sgs/elfdump/common/struct_layout_i386.c	Thu Dec  5 11:52:59 2019
+++ new/usr/src/cmd/sgs/elfdump/common/struct_layout_i386.c	Thu Dec  5 11:52:59 2019
@@ -360,7 +360,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfov1_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- old/usr/src/cmd/sgs/elfdump/common/struct_layout_sparc.c	Thu Dec  5 11:52:59 2019
+++ new/usr/src/cmd/sgs/elfdump/common/struct_layout_sparc.c	Thu Dec  5 11:52:59 2019
@@ -360,7 +360,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfov1_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- old/usr/src/cmd/sgs/elfdump/common/struct_layout_sparcv9.c	Thu Dec  5 11:53:00 2019
+++ new/usr/src/cmd/sgs/elfdump/common/struct_layout_sparcv9.c	Thu Dec  5 11:53:00 2019
@@ -360,7 +360,7 @@
 
 
 static const sl_prfdinfo_layout_t prfdinfo_layout = {
-	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfo_t) */
+	{ 0,	1088,	0,	0 },		/* sizeof (prfdinfov1_t) */
 	{ 0,	4,	0,	0 },		/* pr_fd */
 	{ 4,	4,	0,	0 },		/* pr_mode */
 	{ 8,	4,	0,	0 },		/* pr_uid */
--- old/usr/src/man/man4/proc.4	Thu Dec  5 11:53:00 2019
+++ new/usr/src/man/man4/proc.4	Thu Dec  5 11:53:00 2019
@@ -1,6 +1,7 @@
 .\" Copyright 1989 AT&T
 .\" Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved.
 .\" Copyright 2019, Joyent, Inc.
+.\" Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
 .\"
 .\" The contents of this file are subject to the terms of the
 .\" Common Development and Distribution License (the "License").
@@ -17,7 +18,7 @@
 .\" fields enclosed by brackets "[]" replaced with your own identifying
 .\" information: Portions Copyright [yyyy] [name of copyright owner]
 .\"
-.Dd January 11, 2019
+.Dd December 3, 2019
 .Dt PROC 4
 .Os
 .Sh NAME
@@ -1382,6 +1383,139 @@
 .Pa /proc/ Ns Em pid Ns Pa /cwd .
 An attempt to open any other type of entry fails with
 .Er EACCES .
+.Ss fdinfo
+A directory containing information about each of the process's open files.
+Each entry is a decimal number corresponding to an open file descriptor in the
+process.
+Each file contains a
+.Sy prfdinfov2_t
+structure as follows:
+.Bd -literal -offset 2
+typedef struct prfdinfov2 {
+    int     pr_fd;          /* file descriptor number */
+    mode_t  pr_mode;        /* (see st_mode in stat(2)) */
+    uint64_t pr_ino;        /* inode number */
+    uint64_t pr_size;       /* file size */
+    int64_t pr_offset;      /* current offset of file descriptor */
+    uid_t   pr_uid;         /* owner's user id */
+    gid_t   pr_gid;         /* owner's group id */
+    major_t pr_major;       /* major number of device containing file */
+    minor_t pr_minor;       /* minor number of device containing file */
+    major_t pr_rmajor;      /* major number (if special file) */
+    minor_t pr_rminor;      /* minor number (if special file) */
+    int     pr_fileflags;   /* (see F_GETXFL in fcntl(2)) */
+    int     pr_fdflags;     /* (see F_GETFD in fcntl(2)) */
+    short   pr_locktype;    /* (see F_GETLK in fcntl(2)) */
+    pid_t   pr_lockpid;     /* process holding file lock (see F_GETLK) */
+    int     pr_locksysid;   /* sysid of locking process (see F_GETLK) */
+    pid_t   pr_peerpid;     /* peer process (socket, door) */
+    int     pr_filler[25];  /* reserved for future use */
+    char    pr_peername[PRFNSZ]; /* peer process name */
+#if __STDC_VERSION__ >= 199901L
+    char    pr_misc[];      /* self describing structures */
+#else
+    char    pr_misc[1];
+#endif
+} prfdinfov2_t;
+.Ed
+.Pp
+The
+.Sy pr_misc
+element points to a list of additional miscellaneous data items, each of which
+has a header of type
+.Sy pr_misc_header_t
+specifying the size and type, and some data which immediately follow
+the header.
+.Bd -literal -offset 2
+typedef struct pr_misc_header {
+    uint_t          pr_misc_size;
+    uint_t          pr_misc_type;
+} pr_misc_header_t;
+.Ed
+.Pp
+The
+.Sy pr_misc_size
+field is the sum of the sizes of the header and the associated data.
+The end of the list is indicated by a header with a zero size.
+.Pp
+The following miscellaneous data types can be present:
+.Bl -tag -width "PR_SOCKOPT_TCP_CONGESTION" -offset left
+.It Sy PR_PATHNAME
+The file descriptor's path in the filesystem.
+This is a sequence of characters of the length indicated by
+.Sy pr_misc_size
+and it not guaranteed to be null-terminated.
+.It Sy PR_SOCKETNAME
+A
+.Sy sockaddr
+structure representing the local socket name for this file descriptor, as
+would be returned by calling
+.Fn getsockname
+within the process.
+.It Sy PR_PEERSOCKNAME
+A
+.Sy sockaddr
+structure representing the peer socket name for this file desciptor, as
+would be returned by calling
+.Fn getpeername
+within the process.
+.It Sy PR_SOCKOPTS_BOOL_OPTS
+An unsigned integer which has bits set corresponding to options which are
+set on the underlying socket.
+The following bits may be set:
+.Bl -tag -width "PR_SO_PASSIVE_CONNECT"
+.It Sy PR_SO_DEBUG
+.It Sy PR_SO_REUSEADDR
+.It Sy PR_SO_REUSEPORT
+.It Sy PR_SO_KEEPALIVE
+.It Sy PR_SO_DONTROUTE
+.It Sy PR_SO_BROADCAST
+.It Sy PR_SO_OOBINLINE
+.It Sy PR_SO_DGRAM_ERRIND
+.It Sy PR_SO_ALLZONES
+.It Sy PR_SO_MAC_EXEMPT
+.It Sy PR_SO_EXCLBIND
+.It Sy PR_SO_PASSIVE_CONNECT
+.It Sy PR_SO_ACCEPTCONN
+.It Sy PR_UDP_NAT_T_ENDPOINT
+.It Sy PR_SO_VRRP
+.It Sy PR_SO_MAC_IMPLICIT
+.El
+.It Sy PR_SOCKOPT_LINGER
+A
+.Sy struct linger
+as would be returned by calling
+.Fn getsockopt SO_LINGER
+within the process.
+.It Sy PR_SOCKOPT_SNDBUF
+The data that would be returned by calling
+.Fn getsockopt SO_SNDBUF
+within the process.
+.It Sy PR_SOCKOPT_RCVBUF
+The data that would be returned by calling
+.Fn getsockopt SO_RCVBUF
+within the process.
+.It Sy PR_SOCKOPT_IP_NEXTHOP
+The data that would be returned by calling
+.Fn getsockopt IPPROTO_IP IP_NEXTHOP
+within the process.
+.It Sy PR_SOCKOPT_IPV6_NEXTHOP
+The data that would be returned by calling
+.Fn getsockopt IPPROTO_IPV6 IPV6_NEXTHOP
+within the process.
+.It Sy PR_SOCKOPT_TYPE
+The data that would be returned by calling
+.Fn getsockopt SO_TYPE
+within the process.
+.It Sy PR_SOCKOPT_TCP_CONGESTION
+For TCP sockets, the data that would be returned by calling
+.Fn getsockopt IPPROTO_TCP TCP_CONGESTION
+within the process.
+This is a character array containing the name of the congestion algorithm
+in use for the socket.
+.It Sy PR_SOCKFILTERS_PRIV
+Private data relating to any socket filters pushed on this descriptor.
+.El
 .Ss object
 A directory containing read-only files with names corresponding to the
 .Sy pr_mapname
--- old/usr/src/uts/common/exec/elf/elf_notes.c	Thu Dec  5 11:53:00 2019
+++ new/usr/src/uts/common/exec/elf/elf_notes.c	Thu Dec  5 11:53:00 2019
@@ -108,7 +108,7 @@
 	    + (nlwp + nzomb) * roundup(sizeof (lwpsinfo_t), sizeof (Word))
 	    + nlwp * roundup(sizeof (lwpstatus_t), sizeof (Word))
 	    + nlwp * roundup(sizeof (prlwpname_t), sizeof (Word))
-	    + nfd * roundup(sizeof (prfdinfo_t), sizeof (Word));
+	    + nfd * roundup(sizeof (prfdinfov1_t), sizeof (Word));
 
 	if (curproc->p_agenttp != NULL) {
 		v[0].p_filesz += sizeof (Note) +
@@ -350,7 +350,7 @@
 		vnode_t *fvp;
 		struct file *fp;
 		vattr_t vattr;
-		prfdinfo_t fdinfo;
+		prfdinfov1_t fdinfo;
 
 		bzero(&fdinfo, sizeof (fdinfo));
 
--- old/usr/src/uts/common/fs/proc/prdata.h	Thu Dec  5 11:53:00 2019
+++ new/usr/src/uts/common/fs/proc/prdata.h	Thu Dec  5 11:53:00 2019
@@ -28,6 +28,7 @@
 
 /*
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef _SYS_PROC_PRDATA_H
@@ -131,6 +132,8 @@
 	PR_ROOTDIR,		/* /proc/<pid>/root			*/
 	PR_FDDIR,		/* /proc/<pid>/fd			*/
 	PR_FD,			/* /proc/<pid>/fd/nn			*/
+	PR_FDINFODIR,		/* /proc/<pid>/fdinfo			*/
+	PR_FDINFO,		/* /proc/<pid>/fdinfo/nn		*/
 	PR_OBJECTDIR,		/* /proc/<pid>/object			*/
 	PR_OBJECT,		/* /proc/<pid>/object/xxx		*/
 	PR_LWPDIR,		/* /proc/<pid>/lwp			*/
@@ -288,7 +291,7 @@
  *
  *	pr_iol_initlist(&listhead, sizeof (*mp), n);
  *	while (whatever) {
- *		mp = pr_iol_newbuf(&listhead, sizeof (*mp);
+ *		mp = pr_iol_newbuf(&listhead, sizeof (*mp));
  *		...
  *		error = ...
  *	}
@@ -313,6 +316,7 @@
 extern	void *	pr_iol_newbuf(list_t *head, size_t itemsize);
 extern	int	pr_iol_copyout_and_free(list_t *head, caddr_t *tgt, int errin);
 extern	int	pr_iol_uiomove_and_free(list_t *head, uio_t *uiop, int errin);
+extern	void	pr_iol_freelist(list_t *);
 
 #if defined(_SYSCALL32_IMPL)
 
@@ -347,6 +351,8 @@
 extern	int	pr_set(proc_t *, long);
 extern	int	pr_unset(proc_t *, long);
 extern	void	pr_sethold(prnode_t *, sigset_t *);
+extern	file_t	*pr_getf(proc_t *, uint_t, short *);
+extern	void	pr_releasef(proc_t *, uint_t);
 extern	void	pr_setfault(proc_t *, fltset_t *);
 extern	int	prusrio(proc_t *, enum uio_rw, struct uio *, int);
 extern	int	prwritectl(vnode_t *, struct uio *, cred_t *);
--- old/usr/src/uts/common/fs/proc/prsubr.c	Thu Dec  5 11:53:01 2019
+++ new/usr/src/uts/common/fs/proc/prsubr.c	Thu Dec  5 11:53:01 2019
@@ -22,10 +22,11 @@
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
-/*	  All Rights Reserved  	*/
+/*	  All Rights Reserved	*/
 
 #include <sys/types.h>
 #include <sys/t_lock.h>
@@ -65,6 +66,18 @@
 #include <sys/copyops.h>
 #include <sys/time.h>
 #include <sys/msacct.h>
+#include <sys/flock_impl.h>
+#include <sys/stropts.h>
+#include <sys/strsubr.h>
+#include <sys/pathname.h>
+#include <sys/mode.h>
+#include <sys/socketvar.h>
+#include <sys/autoconf.h>
+#include <sys/dtrace.h>
+#include <sys/timod.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <inet/cc.h>
 #include <vm/as.h>
 #include <vm/rm.h>
 #include <vm/seg.h>
@@ -1454,7 +1467,58 @@
 	return (len);
 }
 
+file_t *
+pr_getf(proc_t *p, uint_t fd, short *flag)
+{
+	uf_entry_t *ufp;
+	uf_info_t *fip;
+	file_t *fp;
+
+	ASSERT(MUTEX_HELD(&p->p_lock));
+
+	fip = P_FINFO(p);
+
+	if (fd >= fip->fi_nfiles)
+		return (NULL);
+
+	mutex_exit(&p->p_lock);
+	mutex_enter(&fip->fi_lock);
+	UF_ENTER(ufp, fip, fd);
+	if ((fp = ufp->uf_file) != NULL && fp->f_count > 0) {
+		if (flag != NULL)
+			*flag = ufp->uf_flag;
+		ufp->uf_refcnt++;
+	} else {
+		fp = NULL;
+	}
+	UF_EXIT(ufp);
+	mutex_exit(&fip->fi_lock);
+	mutex_enter(&p->p_lock);
+
+	return (fp);
+}
+
 void
+pr_releasef(proc_t *p, uint_t fd)
+{
+	uf_entry_t *ufp;
+	uf_info_t *fip;
+
+	ASSERT(MUTEX_HELD(&p->p_lock));
+
+	fip = P_FINFO(p);
+
+	mutex_exit(&p->p_lock);
+	mutex_enter(&fip->fi_lock);
+	UF_ENTER(ufp, fip, fd);
+	ASSERT(ufp->uf_refcnt > 0);
+	ufp->uf_refcnt--;
+	UF_EXIT(ufp);
+	mutex_exit(&fip->fi_lock);
+	mutex_enter(&p->p_lock);
+}
+
+void
 pr_object_name(char *name, vnode_t *vp, struct vattr *vattr)
 {
 	char *s = name;
@@ -1563,6 +1627,18 @@
 	return (new);
 }
 
+void
+pr_iol_freelist(list_t *iolhead)
+{
+	piol_t	*iol;
+
+	while ((iol = list_head(iolhead)) != NULL) {
+		list_remove(iolhead, iol);
+		kmem_free(iol, iol->piol_size);
+	}
+	list_destroy(iolhead);
+}
+
 int
 pr_iol_copyout_and_free(list_t *iolhead, caddr_t *tgt, int errin)
 {
@@ -2375,6 +2451,475 @@
 	}
 }
 
+static size_t
+prfdinfomisc(list_t *data, uint_t type, void *val, off_t vlen)
+{
+	pr_misc_header_t *misc;
+	size_t len;
+
+	len = sizeof (*misc) + vlen;
+
+	if (data != NULL) {
+		misc = pr_iol_newbuf(data, len);
+		misc->pr_misc_type = type;
+		misc->pr_misc_size = len;
+		misc++;
+		bcopy((char *)val, (char *)misc, vlen);
+	}
+
+	return (len);
+}
+
+/*
+ * There's no elegant way to determine if a character device
+ * supports TLI, so just check a hardcoded list of known TLI
+ * devices.
+ */
+
+static boolean_t
+pristli(vnode_t *vp)
+{
+	static const char *tlidevs[] = {
+	    "udp", "udp6", "tcp", "tcp6", NULL
+	};
+	struct devnames *dnp;
+	major_t major;
+	uint_t i;
+
+	ASSERT(vp != NULL);
+	ASSERT(vp->v_type == VCHR);
+
+	if (vp->v_rdev == 0)
+		return (B_FALSE);
+
+	major = getmajor(vp->v_rdev);
+	if (major == DDI_MAJOR_T_NONE || major > devcnt)
+		return (B_FALSE);
+
+	dnp = &devnamesp[major];
+
+	for (i = 0; tlidevs[i] != NULL; i++) {
+		if (strcmp(dnp->dn_name, tlidevs[i]) == 0)
+			return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
+static size_t
+prfdinfopath(proc_t *p, vnode_t *vp, list_t *data, cred_t *cred)
+{
+	char pathname[MAXPATHLEN];
+	vnode_t *vrootp;
+	size_t sz = 0;
+
+	mutex_enter(&p->p_lock);
+	if ((vrootp = PTOU(p)->u_rdir) == NULL)
+		vrootp = rootdir;
+	VN_HOLD(vrootp);
+	mutex_exit(&p->p_lock);
+
+	if (vnodetopath(vrootp, vp, pathname, sizeof (pathname), cred) == 0) {
+		sz += prfdinfomisc(data, PR_PATHNAME,
+		    pathname, strlen(pathname));
+	}
+	VN_RELE(vrootp);
+
+	return (sz);
+}
+
+static size_t
+prfdinfotlisockopt(vnode_t *vp, list_t *data, cred_t *cred)
+{
+	strcmd_t strcmd;
+	int32_t rval;
+	size_t sz = 0;
+
+	strcmd.sc_cmd = TI_GETMYNAME;
+	strcmd.sc_timeout = 1;
+	strcmd.sc_len = STRCMDBUFSIZE;
+
+	if (VOP_IOCTL(vp, _I_CMD, (intptr_t)&strcmd, FKIOCTL, cred,
+	    &rval, NULL) == 0 && strcmd.sc_len > 0) {
+		sz += prfdinfomisc(data, PR_SOCKETNAME, strcmd.sc_buf,
+		    strcmd.sc_len);
+	}
+
+	strcmd.sc_cmd = TI_GETPEERNAME;
+	strcmd.sc_timeout = 1;
+	strcmd.sc_len = STRCMDBUFSIZE;
+
+	if (VOP_IOCTL(vp, _I_CMD, (intptr_t)&strcmd, FKIOCTL, cred,
+	    &rval, NULL) == 0 && strcmd.sc_len > 0) {
+		sz += prfdinfomisc(data, PR_PEERSOCKNAME, strcmd.sc_buf,
+		    strcmd.sc_len);
+	}
+
+	return (sz);
+}
+
+static size_t
+prfdinfosockopt(vnode_t *vp, list_t *data, cred_t *cred)
+{
+	sonode_t *so;
+	socklen_t vlen;
+	size_t sz = 0;
+	uint_t i;
+
+	if (vp->v_stream) {
+		so = VTOSO(vp->v_stream->sd_vnode);
+
+		if (so->so_version == SOV_STREAM)
+			so = NULL;
+	} else {
+		so = VTOSO(vp);
+	}
+
+	if (so == NULL)
+		return (0);
+
+	DTRACE_PROBE1(sonode, sonode_t *, so);
+
+	/* prmisc - PR_SOCKETNAME */
+
+	struct sockaddr_storage buf;
+	struct sockaddr *name = (struct sockaddr *)&buf;
+
+	vlen = sizeof (buf);
+	if (SOP_GETSOCKNAME(so, name, &vlen, cred) == 0 && vlen > 0)
+		sz += prfdinfomisc(data, PR_SOCKETNAME, name, vlen);
+
+	/* prmisc - PR_PEERSOCKNAME */
+
+	vlen = sizeof (buf);
+	if (SOP_GETPEERNAME(so, name, &vlen, B_FALSE, cred) == 0 && vlen > 0)
+		sz += prfdinfomisc(data, PR_PEERSOCKNAME, name, vlen);
+
+	/* prmisc - PR_SOCKOPTS_BOOL_OPTS */
+
+	static struct boolopt {
+		int		level;
+		int		opt;
+		int		bopt;
+	} boolopts[] = {
+		{ SOL_SOCKET, SO_DEBUG,		PR_SO_DEBUG },
+		{ SOL_SOCKET, SO_REUSEADDR,	PR_SO_REUSEADDR },
+#ifdef SO_REUSEPORT
+		/* SmartOS and OmniOS have SO_REUSEPORT */
+		{ SOL_SOCKET, SO_REUSEPORT,	PR_SO_REUSEPORT },
+#endif
+		{ SOL_SOCKET, SO_KEEPALIVE,	PR_SO_KEEPALIVE },
+		{ SOL_SOCKET, SO_DONTROUTE,	PR_SO_DONTROUTE },
+		{ SOL_SOCKET, SO_BROADCAST,	PR_SO_BROADCAST },
+		{ SOL_SOCKET, SO_OOBINLINE,	PR_SO_OOBINLINE },
+		{ SOL_SOCKET, SO_DGRAM_ERRIND,	PR_SO_DGRAM_ERRIND },
+		{ SOL_SOCKET, SO_ALLZONES,	PR_SO_ALLZONES },
+		{ SOL_SOCKET, SO_MAC_EXEMPT,	PR_SO_MAC_EXEMPT },
+		{ SOL_SOCKET, SO_MAC_IMPLICIT,	PR_SO_MAC_IMPLICIT },
+		{ SOL_SOCKET, SO_EXCLBIND,	PR_SO_EXCLBIND },
+		{ SOL_SOCKET, SO_VRRP,		PR_SO_VRRP },
+		{ IPPROTO_UDP, UDP_NAT_T_ENDPOINT,
+		    PR_UDP_NAT_T_ENDPOINT }
+	};
+	prsockopts_bool_opts_t opts;
+	int val;
+
+	if (data != NULL) {
+		opts.prsock_bool_opts = 0;
+
+		for (i = 0; i < sizeof (boolopts) / sizeof (boolopts[0]);
+		    i++) {
+			vlen = sizeof (val);
+			if (SOP_GETSOCKOPT(so, boolopts[i].level,
+			    boolopts[i].opt, &val, &vlen, 0, cred) == 0 &&
+			    val != 0) {
+				opts.prsock_bool_opts |= boolopts[i].bopt;
+			}
+		}
+	}
+
+	sz += prfdinfomisc(data, PR_SOCKOPTS_BOOL_OPTS, &opts, sizeof (opts));
+
+	/* prmisc - PR_SOCKOPT_LINGER */
+
+	struct linger l;
+
+	vlen = sizeof (l);
+	if (SOP_GETSOCKOPT(so, SOL_SOCKET, SO_LINGER, &l, &vlen,
+	    0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_LINGER, &l, vlen);
+	}
+
+	/* prmisc - PR_SOCKOPT_* int types */
+
+	static struct sopt {
+		int		level;
+		int		opt;
+		int		bopt;
+	} sopts[] = {
+		{ SOL_SOCKET, SO_TYPE,		PR_SOCKOPT_TYPE },
+		{ SOL_SOCKET, SO_SNDBUF,	PR_SOCKOPT_SNDBUF },
+		{ SOL_SOCKET, SO_RCVBUF,	PR_SOCKOPT_RCVBUF }
+	};
+
+	for (i = 0; i < sizeof (sopts) / sizeof (sopts[0]); i++) {
+		vlen = sizeof (val);
+		if (SOP_GETSOCKOPT(so, sopts[i].level, sopts[i].opt,
+		    &val, &vlen, 0, cred) == 0 && vlen > 0) {
+			sz += prfdinfomisc(data, sopts[i].bopt, &val, vlen);
+		}
+	}
+
+	/* prmisc - PR_SOCKOPT_IP_NEXTHOP */
+
+	in_addr_t nexthop_val;
+
+	vlen = sizeof (nexthop_val);
+	if (SOP_GETSOCKOPT(so, IPPROTO_IP, IP_NEXTHOP,
+	    &nexthop_val, &vlen, 0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_IP_NEXTHOP,
+		    &nexthop_val, vlen);
+	}
+
+	/* prmisc - PR_SOCKOPT_IPV6_NEXTHOP */
+
+	struct sockaddr_in6 nexthop6_val;
+
+	vlen = sizeof (nexthop6_val);
+	if (SOP_GETSOCKOPT(so, IPPROTO_IPV6, IPV6_NEXTHOP,
+	    &nexthop6_val, &vlen, 0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_IPV6_NEXTHOP,
+		    &nexthop6_val, vlen);
+	}
+
+	/* prmisc - PR_SOCKOPT_TCP_CONGESTION */
+
+	char cong[CC_ALGO_NAME_MAX];
+
+	vlen = sizeof (cong);
+	if (SOP_GETSOCKOPT(so, IPPROTO_TCP, TCP_CONGESTION,
+	    &cong, &vlen, 0, cred) == 0 && vlen > 0) {
+		sz += prfdinfomisc(data, PR_SOCKOPT_TCP_CONGESTION, cong, vlen);
+	}
+
+	/* prmisc - PR_SOCKFILTERS_PRIV */
+
+	struct fil_info fi;
+
+	vlen = sizeof (fi);
+	if (SOP_GETSOCKOPT(so, SOL_FILTER, FIL_LIST,
+	    &fi, &vlen, 0, cred) == 0 && vlen != 0) {
+		pr_misc_header_t *misc;
+		size_t len;
+
+		/*
+		 * We limit the number of returned filters to 32.
+		 * This is the maximum number that pfiles will print
+		 * anyway.
+		 */
+		vlen = MIN(32, fi.fi_pos + 1);
+		vlen *= sizeof (fi);
+
+		len = sizeof (*misc) + vlen;
+		sz += len;
+
+		if (data != NULL) {
+			misc = pr_iol_newbuf(data, len);
+			misc->pr_misc_type = PR_SOCKFILTERS_PRIV;
+			misc->pr_misc_size = len;
+			misc++;
+			len = vlen;
+			if (SOP_GETSOCKOPT(so, SOL_FILTER, FIL_LIST,
+			    misc, &vlen, 0, cred) == 0) {
+				/*
+				 * In case the number of filters has reduced
+				 * since the first call, explicitly zero out
+				 * any unpopulated space.
+				 */
+				if (vlen < len)
+					bzero(misc + vlen, len - vlen);
+			} else {
+				/* Something went wrong, zero out the result */
+				bzero(misc, vlen);
+			}
+		}
+	}
+
+	return (sz);
+}
+
+u_offset_t
+prgetfdinfosize(proc_t *p, vnode_t *vp, cred_t *cred)
+{
+	u_offset_t sz;
+
+	/*
+	 * All fdinfo files will be at least this big -
+	 * sizeof fdinfo struct + zero length trailer
+	 */
+	sz = offsetof(prfdinfov2_t, pr_misc) + sizeof (pr_misc_header_t);
+
+	/* Pathname */
+	if (vp->v_type != VSOCK && vp->v_type != VDOOR)
+		sz += prfdinfopath(p, vp, NULL, cred);
+
+	/* Socket options */
+	if (vp->v_type == VSOCK)
+		sz += prfdinfosockopt(vp, NULL, cred);
+
+	/* TLI/XTI sockets */
+	if (vp->v_type == VCHR && pristli(vp) && vp->v_stream != NULL)
+		sz += prfdinfotlisockopt(vp, NULL, cred);
+
+	return (sz);
+}
+
+int
+prgetfdinfo(proc_t *p, vnode_t *vp, prfdinfov2_t *fdinfo, cred_t *cred,
+    list_t *data)
+{
+	vattr_t vattr;
+	int error;
+
+	if (vp == NULL)
+		return (ENOENT);
+
+	/*
+	 * Initialise defaults for values that do not default to zero.
+	 */
+	fdinfo->pr_uid = (uid_t)-1;
+	fdinfo->pr_gid = (gid_t)-1;
+	fdinfo->pr_size = -1;
+	fdinfo->pr_locktype = F_UNLCK;
+	fdinfo->pr_lockpid = -1;
+	fdinfo->pr_locksysid = -1;
+	fdinfo->pr_peerpid = -1;
+
+	/* Offset */
+
+	switch (vp->v_type) {
+	case VFIFO:
+	case VDOOR:
+	case VSOCK:
+		/* Do not provide an offset for these file types */
+		fdinfo->pr_offset = -1;
+		break;
+	}
+
+	/* Attributes */
+	vattr.va_mask = AT_STAT;
+	if (VOP_GETATTR(vp, &vattr, 0, cred, NULL) == 0) {
+		fdinfo->pr_major = getmajor(vattr.va_fsid);
+		fdinfo->pr_minor = getminor(vattr.va_fsid);
+		fdinfo->pr_rmajor = getmajor(vattr.va_rdev);
+		fdinfo->pr_rminor = getminor(vattr.va_rdev);
+		fdinfo->pr_ino = (ino64_t)vattr.va_nodeid;
+		fdinfo->pr_size = (off64_t)vattr.va_size;
+		fdinfo->pr_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
+		fdinfo->pr_uid = vattr.va_uid;
+		fdinfo->pr_gid = vattr.va_gid;
+		if (vp->v_type == VSOCK)
+			fdinfo->pr_fileflags |= sock_getfasync(vp);
+	}
+
+	/* locks */
+
+	flock64_t bf;
+
+	bzero(&bf, sizeof (bf));
+	bf.l_type = F_WRLCK;
+
+	if (VOP_FRLOCK(vp, F_GETLK, &bf,
+	    (uint16_t)(fdinfo->pr_fileflags & 0xffff), 0, NULL,
+	    cred, NULL) == 0 && bf.l_type != F_UNLCK) {
+		fdinfo->pr_locktype = bf.l_type;
+		fdinfo->pr_lockpid = bf.l_pid;
+		fdinfo->pr_locksysid = bf.l_sysid;
+	}
+
+	/* peer cred */
+
+	k_peercred_t kpc;
+
+	switch (vp->v_type) {
+	case VFIFO:
+	case VSOCK: {
+		int32_t rval;
+
+		error = VOP_IOCTL(vp, _I_GETPEERCRED, (intptr_t)&kpc,
+		    FKIOCTL, cred, &rval, NULL);
+		break;
+	}
+	case VCHR: {
+		struct strioctl strioc;
+		int32_t rval;
+
+		if (vp->v_stream == NULL) {
+			error = ENOTSUP;
+			break;
+		}
+		strioc.ic_cmd = _I_GETPEERCRED;
+		strioc.ic_timout = INFTIM;
+		strioc.ic_len = (int)sizeof (k_peercred_t);
+		strioc.ic_dp = (char *)&kpc;
+
+		error = strdoioctl(vp->v_stream, &strioc, FNATIVE | FKIOCTL,
+		    STR_NOSIG | K_TO_K, cred, &rval);
+		break;
+	}
+	default:
+		error = ENOTSUP;
+	}
+
+	if (error == 0 && kpc.pc_cr != NULL) {
+		proc_t *peerp;
+
+		fdinfo->pr_peerpid = kpc.pc_cpid;
+
+		crfree(kpc.pc_cr);
+
+		mutex_enter(&pidlock);
+		if ((peerp = prfind(fdinfo->pr_peerpid)) != NULL) {
+			user_t *up;
+
+			mutex_enter(&peerp->p_lock);
+			mutex_exit(&pidlock);
+
+			up = PTOU(peerp);
+			bcopy(up->u_comm, fdinfo->pr_peername,
+			    MIN(sizeof (up->u_comm),
+			    sizeof (fdinfo->pr_peername) - 1));
+
+			mutex_exit(&peerp->p_lock);
+		} else {
+			mutex_exit(&pidlock);
+		}
+	}
+
+	/*
+	 * Don't attempt to determine the vnode path for a socket or a door
+	 * as it will cause a linear scan of the dnlc table given there is no
+	 * v_path associated with the vnode.
+	 */
+	if (vp->v_type != VSOCK && vp->v_type != VDOOR)
+		(void) prfdinfopath(p, vp, data, cred);
+
+	if (vp->v_type == VSOCK)
+		(void) prfdinfosockopt(vp, data, cred);
+
+	/* TLI/XTI stream sockets */
+	if (vp->v_type == VCHR && pristli(vp) && vp->v_stream != NULL)
+		(void) prfdinfotlisockopt(vp, data, cred);
+
+	/*
+	 * Add a terminating record with a zero size (pr_iol_newbuf will
+	 * bzero the memory)
+	 */
+	(void) pr_iol_newbuf(data, sizeof (pr_misc_header_t));
+
+	return (0);
+}
+
 #ifdef _SYSCALL32_IMPL
 void
 prgetpsinfo32(proc_t *p, psinfo32_t *psp)
@@ -2670,7 +3215,7 @@
 #define	PR_COPY_TIMESPEC(s, d, field)				\
 	TIMESPEC_TO_TIMESPEC32(&d->field, &s->field);
 
-#define	PR_COPY_BUF(s, d, field)	 			\
+#define	PR_COPY_BUF(s, d, field)				\
 	bcopy(s->field, d->field, sizeof (d->field));
 
 #define	PR_IGNORE_FIELD(s, d, field)
--- old/usr/src/uts/common/fs/proc/prvnops.c	Thu Dec  5 11:53:01 2019
+++ new/usr/src/uts/common/fs/proc/prvnops.c	Thu Dec  5 11:53:01 2019
@@ -23,6 +23,7 @@
  * 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	*/
@@ -154,17 +155,19 @@
 		"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),
@@ -596,7 +599,8 @@
 #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				*/
@@ -625,6 +629,8 @@
 	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			*/
@@ -813,6 +819,90 @@
 }
 
 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;
@@ -1835,6 +1925,8 @@
 	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			*/
@@ -3080,9 +3172,30 @@
 		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.
@@ -3407,8 +3520,8 @@
  */
 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				*/
@@ -3437,6 +3550,8 @@
 	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			*/
@@ -3524,7 +3639,8 @@
 		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);
 
@@ -4138,8 +4254,6 @@
 	file_t *fp;
 	uint_t fd;
 	int c;
-	uf_entry_t *ufp;
-	uf_info_t *fip;
 
 	ASSERT(dpnp->pr_type == PR_FDDIR);
 
@@ -4149,8 +4263,8 @@
 		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);
 	}
 
@@ -4167,49 +4281,90 @@
 		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) {
-			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);
-		}
-		UF_EXIT(ufp);
+	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);
 	}
-	mutex_exit(&fip->fi_lock);
-	mutex_enter(&p->p_lock);
+
 	prunlock(dpnp);
 
-	if (vp == NULL)
+	if (vp == NULL) {
 		prfreenode(pnp);
-	else {
-		/*
-		 * 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.
-		 */
-		pnp->pr_realvp = vp;
-		pnp->pr_parent = dp;		/* needed for prlookup */
-		VN_HOLD(dp);
-		vp = PTOV(pnp);
-		if (pnp->pr_realvp->v_type == VDIR) {
-			vp->v_type = VDIR;
-			vp->v_flag |= VTRAVERSE;
-		}
+		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.
+	 */
+	pnp->pr_realvp = vp;
+	pnp->pr_parent = dp;		/* needed for prlookup */
+	VN_HOLD(dp);
+	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)
 {
 	prnode_t *dpnp = VTOP(dp);
@@ -4667,6 +4822,7 @@
 	case PR_CURDIR:
 	case PR_ROOTDIR:
 	case PR_FDDIR:
+	case PR_FDINFODIR:
 	case PR_OBJECTDIR:
 	case PR_PATHDIR:
 	case PR_CTDIR:
@@ -4799,8 +4955,8 @@
  */
 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				*/
@@ -4829,6 +4985,8 @@
 	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			*/
@@ -5361,9 +5519,12 @@
 	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;
@@ -5373,8 +5534,6 @@
 	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;
@@ -5383,7 +5542,7 @@
 	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);
@@ -5414,7 +5573,7 @@
 		}
 
 		error = gfs_readdir_emitn(&gstate, uiop, n,
-		    pmkino(n, pslot, PR_FD), n);
+		    pmkino(n, pslot, entrytype), n);
 		if (error)
 			break;
 	}
@@ -5426,6 +5585,24 @@
 	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)
@@ -5735,6 +5912,7 @@
 	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 */
--- old/usr/src/uts/common/sys/procfs.h	Thu Dec  5 11:53:02 2019
+++ new/usr/src/uts/common/sys/procfs.h	Thu Dec  5 11:53:02 2019
@@ -26,6 +26,7 @@
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef _SYS_PROCFS_H
@@ -513,7 +514,7 @@
  * struct stat (e.g. times, device sizes, etc.) so we don't bother adding those
  * here.
  */
-typedef struct prfdinfo {
+typedef struct prfdinfov1 {
 	int		pr_fd;
 	mode_t		pr_mode;
 
@@ -534,8 +535,85 @@
 	int		pr_fdflags;	/* fcntl(F_GETFD), etc. */
 
 	char		pr_path[MAXPATHLEN];
-} prfdinfo_t;
+} prfdinfov1_t;
 
+typedef prfdinfov1_t prfdinfo_t;
+
+typedef struct prfdinfov2 {
+	int		pr_fd;		/* file descriptor number */
+	mode_t		pr_mode;	/* (see st_mode in stat(2)) */
+	ino64_t		pr_ino;		/* inode number */
+	off64_t		pr_size;	/* file size */
+	off64_t		pr_offset;	/* current offset of file descriptor */
+	uid_t		pr_uid;		/* owner's user id */
+	gid_t		pr_gid;		/* owner's group id */
+	major_t		pr_major;	/* major number of device */
+	minor_t		pr_minor;	/* minor number of device */
+	major_t		pr_rmajor;	/* major number (if special file) */
+	minor_t		pr_rminor;	/* minor number (if special file) */
+	int		pr_fileflags;	/* (see F_GETXFL in fcntl(2)) */
+	int		pr_fdflags;	/* (see F_GETFD in fcntl(2)) */
+	short		pr_locktype;	/* (see F_GETLK in fcntl(2)) */
+	pid_t		pr_lockpid;	/* process holding file lock */
+					/* (see F_GETLK) */
+	int		pr_locksysid;	/* sysid of locking process */
+					/* (see F_GETLK) */
+	pid_t		pr_peerpid;	/* peer process (socket, door) */
+	int		pr_filler[25];	/* reserved for future use */
+	char		pr_peername[PRFNSZ];	/* peer process name */
+#if	__STDC_VERSION__ >= 199901L
+	char	pr_misc[];	/* self describing structures */
+#else
+	char	pr_misc[1];
+#endif
+} prfdinfov2_t;
+
+typedef struct pr_misc_header {
+	uint_t		pr_misc_size;
+	uint_t		pr_misc_type;
+} pr_misc_header_t;
+
+enum PR_MISC_TYPES
+{
+	PR_PATHNAME,
+	PR_SOCKETNAME,
+	PR_PEERSOCKNAME,
+	PR_SOCKOPTS_BOOL_OPTS,
+	PR_SOCKOPT_LINGER,
+	PR_SOCKOPT_SNDBUF,
+	PR_SOCKOPT_RCVBUF,
+	PR_SOCKOPT_IP_NEXTHOP,
+	PR_SOCKOPT_IPV6_NEXTHOP,
+	PR_SOCKOPT_TYPE,
+	__UNIMPL_PR_SOCKOPT_LISTENQLIMIT,
+	PR_SOCKOPT_TCP_CONGESTION,
+	__UNIMPL_PR_SOCKOPT_FLOW_NAME,
+	__UNIMPL_PR_SOCKOPTS_PRIV,
+	PR_SOCKFILTERS_PRIV,
+	PR_MISC_TYPES_MAX
+};
+
+typedef struct prsockopts_bool_opts {
+	unsigned int prsock_bool_opts;
+} prsockopts_bool_opts_t;
+
+#define	PR_SO_DEBUG		(1 << 0)
+#define	PR_SO_REUSEADDR		(1 << 1)
+#define	PR_SO_REUSEPORT		(1 << 2)
+#define	PR_SO_KEEPALIVE		(1 << 3)
+#define	PR_SO_DONTROUTE		(1 << 4)
+#define	PR_SO_BROADCAST		(1 << 5)
+#define	PR_SO_OOBINLINE		(1 << 7)
+#define	PR_SO_DGRAM_ERRIND	(1 << 8)
+#define	PR_SO_ALLZONES		(1 << 9)
+#define	PR_SO_MAC_EXEMPT	(1 << 10)
+#define	PR_SO_EXCLBIND		(1 << 11)
+#define	PR_SO_PASSIVE_CONNECT	(1 << 12)
+#define	PR_SO_ACCEPTCONN	(1 << 13)
+#define	PR_UDP_NAT_T_ENDPOINT	(1 << 14)
+#define	PR_SO_VRRP		(1 << 15)
+#define	PR_SO_MAC_IMPLICIT	(1 << 16)
+
 /*
  * Representation of LWP name in core files.  In /proc, we use a simple char
  * array, but in core files we need to make it easy to correlate the note back
--- old/usr/src/uts/common/sys/prsystm.h	Thu Dec  5 11:53:02 2019
+++ new/usr/src/uts/common/sys/prsystm.h	Thu Dec  5 11:53:02 2019
@@ -29,6 +29,7 @@
 
 /*
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef _SYS_PRSYSTM_H
@@ -54,6 +55,7 @@
 struct prcred;
 struct prpriv;
 struct prsecflags;
+struct prfdinfov2;
 
 struct seg;
 struct regs;
@@ -77,16 +79,14 @@
 extern void prgetprfpregs(klwp_t *, struct prfpregset *);
 extern void prgetprxregs(klwp_t *, caddr_t);
 extern int  prgetprxregsize(proc_t *);
-#if defined(__lint)
-/* Work around lint confusion between old and new prcred definitions */
-extern void prgetcred();
-#else
 extern void prgetcred(proc_t *, struct prcred *);
-#endif
 extern void prgetpriv(proc_t *, struct prpriv *);
 extern size_t prgetprivsize(void);
 extern void prgetsecflags(proc_t *, struct prsecflags *);
 extern int  prnsegs(struct as *, int);
+extern u_offset_t prgetfdinfosize(proc_t *, vnode_t *, cred_t *);
+extern int prgetfdinfo(proc_t *, vnode_t *, struct prfdinfov2 *, cred_t *,
+    list_t *);
 extern void prexit(proc_t *);
 extern void prfree(proc_t *);
 extern void prlwpexit(kthread_t *);
