summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/syscall/utssys.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/common/syscall/utssys.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/common/syscall/utssys.c')
-rw-r--r--usr/src/uts/common/syscall/utssys.c954
1 files changed, 954 insertions, 0 deletions
diff --git a/usr/src/uts/common/syscall/utssys.c b/usr/src/uts/common/syscall/utssys.c
new file mode 100644
index 0000000000..380df8e8fc
--- /dev/null
+++ b/usr/src/uts/common/syscall/utssys.c
@@ -0,0 +1,954 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/inttypes.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/user.h>
+#include <sys/errno.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/session.h>
+#include <sys/var.h>
+#include <sys/utsname.h>
+#include <sys/utssys.h>
+#include <sys/ustat.h>
+#include <sys/statvfs.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/pathname.h>
+#include <sys/modctl.h>
+#include <sys/fs/snode.h>
+#include <sys/sunldi_impl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/ddipropdefs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/modctl.h>
+#include <sys/flock.h>
+#include <sys/share.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <vm/seg_vn.h>
+#include <util/qsort.h>
+#include <sys/zone.h>
+
+/*
+ * utssys()
+ */
+static int uts_fusers(char *, int, intptr_t);
+static int _statvfs64_by_dev(dev_t, struct statvfs64 *);
+
+#if defined(_ILP32) || defined(_SYSCALL32_IMPL)
+
+static int utssys_uname32(caddr_t, rval_t *);
+static int utssys_ustat32(dev_t, struct ustat32 *);
+
+int64_t
+utssys32(void *buf, int arg, int type, void *outbp)
+{
+ int error;
+ rval_t rv;
+
+ rv.r_vals = 0;
+
+ switch (type) {
+ case UTS_UNAME:
+ /*
+ * This is an obsolete way to get the utsname structure
+ * (it only gives you the first 8 characters of each field!)
+ * uname(2) is the preferred and better interface.
+ */
+ error = utssys_uname32(buf, &rv);
+ break;
+ case UTS_USTAT:
+ error = utssys_ustat32(expldev((dev32_t)arg), buf);
+ break;
+ case UTS_FUSERS:
+ error = uts_fusers(buf, arg, (intptr_t)outbp);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error == 0 ? rv.r_vals : (int64_t)set_errno(error));
+}
+
+static int
+utssys_uname32(caddr_t buf, rval_t *rvp)
+{
+ if (copyout(utsname.sysname, buf, 8))
+ return (EFAULT);
+ buf += 8;
+ if (subyte(buf, 0) < 0)
+ return (EFAULT);
+ buf++;
+ if (copyout(uts_nodename(), buf, 8))
+ return (EFAULT);
+ buf += 8;
+ if (subyte(buf, 0) < 0)
+ return (EFAULT);
+ buf++;
+ if (copyout(utsname.release, buf, 8))
+ return (EFAULT);
+ buf += 8;
+ if (subyte(buf, 0) < 0)
+ return (EFAULT);
+ buf++;
+ if (copyout(utsname.version, buf, 8))
+ return (EFAULT);
+ buf += 8;
+ if (subyte(buf, 0) < 0)
+ return (EFAULT);
+ buf++;
+ if (copyout(utsname.machine, buf, 8))
+ return (EFAULT);
+ buf += 8;
+ if (subyte(buf, 0) < 0)
+ return (EFAULT);
+ rvp->r_val1 = 1;
+ return (0);
+}
+
+static int
+utssys_ustat32(dev_t dev, struct ustat32 *cbuf)
+{
+ struct ustat32 ust32;
+ struct statvfs64 stvfs;
+ fsblkcnt64_t fsbc64;
+ char *cp, *cp2;
+ int i, error;
+
+ if ((error = _statvfs64_by_dev(dev, &stvfs)) != 0)
+ return (error);
+
+ fsbc64 = stvfs.f_bfree * (stvfs.f_frsize / 512);
+ /*
+ * Check to see if the number of free blocks can be expressed
+ * in 31 bits or whether the number of free files is more than
+ * can be expressed in 32 bits and is not -1 (UINT64_MAX). NFS
+ * Version 2 does not support the number of free files and
+ * hence will return -1. -1, when translated from a 32 bit
+ * quantity to an unsigned 64 bit quantity, turns into UINT64_MAX.
+ */
+ if (fsbc64 > INT32_MAX ||
+ (stvfs.f_ffree > UINT32_MAX && stvfs.f_ffree != UINT64_MAX))
+ return (EOVERFLOW);
+
+ ust32.f_tfree = (daddr32_t)fsbc64;
+ ust32.f_tinode = (ino32_t)stvfs.f_ffree;
+
+ cp = stvfs.f_fstr;
+ cp2 = ust32.f_fname;
+ i = 0;
+ while (i++ < sizeof (ust32.f_fname))
+ if (*cp != '\0')
+ *cp2++ = *cp++;
+ else
+ *cp2++ = '\0';
+ while (*cp != '\0' &&
+ (i++ < sizeof (stvfs.f_fstr) - sizeof (ust32.f_fpack)))
+ cp++;
+ (void) strncpy(ust32.f_fpack, cp + 1, sizeof (ust32.f_fpack));
+
+ if (copyout(&ust32, cbuf, sizeof (ust32)))
+ return (EFAULT);
+ return (0);
+}
+
+#endif /* _ILP32 || _SYSCALL32_IMPL */
+
+#ifdef _LP64
+
+static int uts_ustat64(dev_t, struct ustat *);
+
+int64_t
+utssys64(void *buf, long arg, int type, void *outbp)
+{
+ int error;
+ rval_t rv;
+
+ rv.r_vals = 0;
+
+ switch (type) {
+ case UTS_USTAT:
+ error = uts_ustat64((dev_t)arg, buf);
+ break;
+ case UTS_FUSERS:
+ error = uts_fusers(buf, (int)arg, (intptr_t)outbp);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error == 0 ? rv.r_vals : (int64_t)set_errno(error));
+}
+
+static int
+uts_ustat64(dev_t dev, struct ustat *cbuf)
+{
+ struct ustat ust;
+ struct statvfs64 stvfs;
+ fsblkcnt64_t fsbc64;
+ char *cp, *cp2;
+ int i, error;
+
+ if ((error = _statvfs64_by_dev(dev, &stvfs)) != 0)
+ return (error);
+
+ fsbc64 = stvfs.f_bfree * (stvfs.f_frsize / 512);
+ ust.f_tfree = (daddr_t)fsbc64;
+ ust.f_tinode = (ino_t)stvfs.f_ffree;
+
+ cp = stvfs.f_fstr;
+ cp2 = ust.f_fname;
+ i = 0;
+ while (i++ < sizeof (ust.f_fname))
+ if (*cp != '\0')
+ *cp2++ = *cp++;
+ else
+ *cp2++ = '\0';
+ while (*cp != '\0' &&
+ (i++ < sizeof (stvfs.f_fstr) - sizeof (ust.f_fpack)))
+ cp++;
+ (void) strncpy(ust.f_fpack, cp + 1, sizeof (ust.f_fpack));
+
+ if (copyout(&ust, cbuf, sizeof (ust)))
+ return (EFAULT);
+ return (0);
+}
+
+#endif /* _LP64 */
+
+/*
+ * Utility routine for the ustat implementations.
+ * (If it wasn't for the 'find-by-dev_t' semantic of ustat(2), we could push
+ * this all out into userland, sigh.)
+ */
+static int
+_statvfs64_by_dev(dev_t dev, struct statvfs64 *svp)
+{
+ vfs_t *vfsp;
+ int error;
+
+ if ((vfsp = vfs_dev2vfsp(dev)) == NULL) {
+ /*
+ * See if it's the root of our zone.
+ */
+ vfsp = curproc->p_zone->zone_rootvp->v_vfsp;
+ if (vfsp->vfs_dev == dev) {
+ VFS_HOLD(vfsp);
+ } else {
+ vfsp = NULL;
+ }
+ }
+ if (vfsp == NULL)
+ return (EINVAL);
+ error = VFS_STATVFS(vfsp, svp);
+ VFS_RELE(vfsp);
+ return (error);
+}
+
+/*
+ * Check if this pid has an NBMAND lock or share reservation
+ * on this vp. llp is a snapshoted list of all NBMAND locks
+ * set by this pid. Return 1 if there is an NBMAND lock else
+ * return 0.
+ */
+static int
+proc_has_nbmand_on_vp(vnode_t *vp, pid_t pid, locklist_t *llp)
+{
+ /*
+ * Any NBMAND lock held by the process on this vp?
+ */
+ while (llp) {
+ if (llp->ll_vp == vp) {
+ return (1);
+ }
+ llp = llp->ll_next;
+ }
+ /*
+ * Any NBMAND share reservation on the vp for this process?
+ */
+ return (proc_has_nbmand_share_on_vp(vp, pid));
+}
+
+static fu_data_t *
+dofusers(vnode_t *fvp, int flags)
+{
+ fu_data_t *fu_data;
+ proc_t *prp;
+ vfs_t *cvfsp;
+ pid_t npids, pidx, *pidlist;
+ int v_proc = v.v_proc; /* max # of procs */
+ int pcnt = 0;
+ int contained = (flags & F_CONTAINED);
+ int nbmandonly = (flags & F_NBMANDLIST);
+ int dip_usage = (flags & F_DEVINFO);
+ int fvp_isdev = vn_matchops(fvp, spec_getvnodeops());
+ zone_t *zone = curproc->p_zone;
+ int inglobal = INGLOBALZONE(curproc);
+
+ /* get a pointer to the file system containing this vnode */
+ cvfsp = fvp->v_vfsp;
+ ASSERT(cvfsp);
+
+ /* allocate the data structure to return our results in */
+ fu_data = kmem_alloc(fu_data_size(v_proc), KM_SLEEP);
+ fu_data->fud_user_max = v_proc;
+ fu_data->fud_user_count = 0;
+
+ /* get a snapshot of all the pids we're going to check out */
+ pidlist = kmem_alloc(v_proc * sizeof (pid_t), KM_SLEEP);
+ mutex_enter(&pidlock);
+ for (npids = 0, prp = practive; prp != NULL; prp = prp->p_next) {
+ if (inglobal || prp->p_zone == zone)
+ pidlist[npids++] = prp->p_pid;
+ }
+ mutex_exit(&pidlock);
+
+ /* grab each process and check its file usage */
+ for (pidx = 0; pidx < npids; pidx++) {
+ locklist_t *llp = NULL;
+ uf_info_t *fip;
+ vnode_t *vp;
+ user_t *up;
+ sess_t *sp;
+ uid_t uid;
+ pid_t pid = pidlist[pidx];
+ int i, use_flag = 0;
+
+ /*
+ * grab prp->p_lock using sprlock()
+ * if sprlock() fails the process does not exists anymore
+ */
+ prp = sprlock(pid);
+ if (prp == NULL)
+ continue;
+
+ /* get the processes credential info in case we need it */
+ mutex_enter(&prp->p_crlock);
+ uid = crgetruid(prp->p_cred);
+ mutex_exit(&prp->p_crlock);
+
+ /*
+ * it's safe to drop p_lock here because we
+ * called sprlock() before and it set the SPRLOCK
+ * flag for the process so it won't go away.
+ */
+ mutex_exit(&prp->p_lock);
+
+ /*
+ * now we want to walk a processes open file descriptors
+ * to do this we need to grab the fip->fi_lock. (you
+ * can't hold p_lock when grabbing the fip->fi_lock.)
+ */
+ fip = P_FINFO(prp);
+ mutex_enter(&fip->fi_lock);
+
+ /*
+ * Snapshot nbmand locks for pid
+ */
+ llp = flk_active_nbmand_locks(prp->p_pid);
+ for (i = 0; i < fip->fi_nfiles; i++) {
+ uf_entry_t *ufp;
+ file_t *fp;
+
+ UF_ENTER(ufp, fip, i);
+ if (((fp = ufp->uf_file) == NULL) ||
+ ((vp = fp->f_vnode) == NULL)) {
+ UF_EXIT(ufp);
+ continue;
+ }
+
+ /*
+ * if the target file (fvp) is not a device
+ * and corrosponds to the root of a filesystem
+ * (cvfsp), then check if it contains the file
+ * is use by this process (vp).
+ */
+ if (contained && (vp->v_vfsp == cvfsp))
+ use_flag |= F_OPEN;
+
+ /*
+ * if the target file (fvp) is not a device,
+ * then check if it matches the file in use
+ * by this process (vp).
+ */
+ if (!fvp_isdev && VN_CMP(fvp, vp))
+ use_flag |= F_OPEN;
+
+ /*
+ * if the target file (fvp) is a device,
+ * then check if the current file in use
+ * by this process (vp) maps to the same device
+ * minor node.
+ */
+ if (fvp_isdev &&
+ vn_matchops(vp, spec_getvnodeops()) &&
+ (fvp->v_rdev == vp->v_rdev))
+ use_flag |= F_OPEN;
+
+ /*
+ * if the target file (fvp) is a device,
+ * and we're checking for device instance
+ * usage, then check if the current file in use
+ * by this process (vp) maps to the same device
+ * instance.
+ */
+ if (dip_usage &&
+ vn_matchops(vp, spec_getvnodeops()) &&
+ (VTOCS(fvp)->s_dip == VTOCS(vp)->s_dip))
+ use_flag |= F_OPEN;
+
+ /*
+ * if the current file in use by this process (vp)
+ * doesn't match what we're looking for, move on
+ * to the next file in the process.
+ */
+ if ((use_flag & F_OPEN) == 0) {
+ UF_EXIT(ufp);
+ continue;
+ }
+
+ if (proc_has_nbmand_on_vp(vp, prp->p_pid, llp)) {
+ /* A nbmand found so we're done. */
+ use_flag |= F_NBM;
+ UF_EXIT(ufp);
+ break;
+ }
+ UF_EXIT(ufp);
+ }
+ if (llp)
+ flk_free_locklist(llp);
+
+ mutex_exit(&fip->fi_lock);
+
+ /*
+ * If nbmand usage tracking is desired and no nbmand was
+ * found for this process, then no need to do further
+ * usage tracking for this process.
+ */
+ if (nbmandonly && (!(use_flag & F_NBM))) {
+ /*
+ * grab the process lock again, clear the SPRLOCK
+ * flag, release the process, and continue.
+ */
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ continue;
+ }
+
+ /*
+ * All other types of usage.
+ * For the next few checks we need to hold p_lock.
+ */
+ mutex_enter(&prp->p_lock);
+ up = PTOU(prp);
+ if (fvp_isdev) {
+ /*
+ * if the target file (fvp) is a device
+ * then check if it matches the processes tty
+ *
+ * we grab s_lock to protect ourselves against
+ * freectty() freeing the vnode out from under us.
+ */
+ sp = prp->p_sessp;
+ mutex_enter(&sp->s_lock);
+ vp = prp->p_sessp->s_vp;
+ if (vp != NULL) {
+ if (fvp->v_rdev == vp->v_rdev)
+ use_flag |= F_TTY;
+
+ if (dip_usage &&
+ (VTOCS(fvp)->s_dip == VTOCS(vp)->s_dip))
+ use_flag |= F_TTY;
+ }
+ mutex_exit(&sp->s_lock);
+ } else {
+ /* check the processes current working directory */
+ if (up->u_cdir &&
+ (VN_CMP(fvp, up->u_cdir) ||
+ (contained && (up->u_cdir->v_vfsp == cvfsp))))
+ use_flag |= F_CDIR;
+
+ /* check the processes root directory */
+ if (up->u_rdir &&
+ (VN_CMP(fvp, up->u_rdir) ||
+ (contained && (up->u_rdir->v_vfsp == cvfsp))))
+ use_flag |= F_RDIR;
+
+ /* check the program text vnode */
+ if (prp->p_exec &&
+ (VN_CMP(fvp, prp->p_exec) ||
+ (contained && (prp->p_exec->v_vfsp == cvfsp))))
+ use_flag |= F_TEXT;
+ }
+
+ /* Now we can drop p_lock again */
+ mutex_exit(&prp->p_lock);
+
+ /*
+ * now we want to walk a processes memory mappings.
+ * to do this we need to grab the prp->p_as lock. (you
+ * can't hold p_lock when grabbing the prp->p_as lock.)
+ */
+ if (prp->p_as != &kas) {
+ struct seg *seg;
+ struct as *as = prp->p_as;
+
+ AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
+ for (seg = AS_SEGFIRST(as); seg;
+ seg = AS_SEGNEXT(as, seg)) {
+ /*
+ * if we can't get a backing vnode for this
+ * segment then skip it
+ */
+ vp = NULL;
+ if ((SEGOP_GETVP(seg, seg->s_base, &vp)) ||
+ (vp == NULL))
+ continue;
+
+ /*
+ * if the target file (fvp) is not a device
+ * and corrosponds to the root of a filesystem
+ * (cvfsp), then check if it contains the
+ * vnode backing this segment (vp).
+ */
+ if (contained && (vp->v_vfsp == cvfsp)) {
+ use_flag |= F_MAP;
+ break;
+ }
+
+ /*
+ * if the target file (fvp) is not a device,
+ * check if it matches the the vnode backing
+ * this segment (vp).
+ */
+ if (!fvp_isdev && VN_CMP(fvp, vp)) {
+ use_flag |= F_MAP;
+ break;
+ }
+
+ /*
+ * if the target file (fvp) isn't a device,
+ * or the the vnode backing this segment (vp)
+ * isn't a device then continue.
+ */
+ if (!fvp_isdev ||
+ !vn_matchops(vp, spec_getvnodeops()))
+ continue;
+
+ /*
+ * check if the vnode backing this segment
+ * (vp) maps to the same device minor node
+ * as the target device (fvp)
+ */
+ if (fvp->v_rdev == vp->v_rdev) {
+ use_flag |= F_MAP;
+ break;
+ }
+
+ /*
+ * if we're checking for device instance
+ * usage, then check if the vnode backing
+ * this segment (vp) maps to the same device
+ * instance as the target device (fvp).
+ */
+ if (dip_usage &&
+ (VTOCS(fvp)->s_dip == VTOCS(vp)->s_dip)) {
+ use_flag |= F_MAP;
+ break;
+ }
+ }
+ AS_LOCK_EXIT(as, &as->a_lock);
+ }
+
+ if (use_flag) {
+ ASSERT(pcnt < fu_data->fud_user_max);
+ fu_data->fud_user[pcnt].fu_flags = use_flag;
+ fu_data->fud_user[pcnt].fu_pid = pid;
+ fu_data->fud_user[pcnt].fu_uid = uid;
+ pcnt++;
+ }
+
+ /*
+ * grab the process lock again, clear the SPRLOCK
+ * flag, release the process, and continue.
+ */
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ }
+
+ kmem_free(pidlist, v_proc * sizeof (pid_t));
+
+ fu_data->fud_user_count = pcnt;
+ return (fu_data);
+}
+
+typedef struct dofkusers_arg {
+ vnode_t *fvp;
+ int flags;
+ int *error;
+ fu_data_t *fu_data;
+} dofkusers_arg_t;
+
+static int
+dofkusers_walker(const ldi_usage_t *ldi_usage, void *arg)
+{
+ dofkusers_arg_t *dofkusers_arg = (dofkusers_arg_t *)arg;
+
+ vnode_t *fvp = dofkusers_arg->fvp;
+ int flags = dofkusers_arg->flags;
+ int *error = dofkusers_arg->error;
+ fu_data_t *fu_data = dofkusers_arg->fu_data;
+
+ modid_t modid;
+ minor_t minor;
+ int instance;
+ int dip_usage = (flags & F_DEVINFO);
+
+ ASSERT(*error == 0);
+ ASSERT(vn_matchops(fvp, spec_getvnodeops()));
+
+ /*
+ * check if the dev_t of the target device matches the dev_t
+ * of the device we're trying to find usage info for.
+ */
+ if (fvp->v_rdev != ldi_usage->tgt_devt) {
+
+ /*
+ * if the dev_ts don't match and we're not trying
+ * to find usage information for device instances
+ * then return
+ */
+ if (!dip_usage)
+ return (LDI_USAGE_CONTINUE);
+
+
+ /*
+ * we're trying to find usage information for an
+ * device instance instead of just a minor node.
+ *
+ * check if the dip for the target device matches the
+ * dip of the device we're trying to find usage info for.
+ */
+ if (VTOCS(fvp)->s_dip != ldi_usage->tgt_dip)
+ return (LDI_USAGE_CONTINUE);
+ }
+
+ if (fu_data->fud_user_count >= fu_data->fud_user_max) {
+ *error = E2BIG;
+ return (LDI_USAGE_TERMINATE);
+ }
+
+ /* get the device vnode user information */
+ modid = ldi_usage->src_modid;
+ ASSERT(modid != -1);
+
+ minor = instance = -1;
+ if (ldi_usage->src_dip != NULL) {
+ instance = DEVI(ldi_usage->src_dip)->devi_instance;
+ }
+ if (ldi_usage->src_devt != DDI_DEV_T_NONE) {
+ minor = getminor(ldi_usage->src_devt);
+ }
+
+ /* set the device vnode user information */
+ fu_data->fud_user[fu_data->fud_user_count].fu_flags = F_KERNEL;
+ fu_data->fud_user[fu_data->fud_user_count].fu_modid = modid;
+ fu_data->fud_user[fu_data->fud_user_count].fu_instance = instance;
+ fu_data->fud_user[fu_data->fud_user_count].fu_minor = minor;
+
+ fu_data->fud_user_count++;
+
+ return (LDI_USAGE_CONTINUE);
+}
+
+int
+f_user_cmp(const void *arg1, const void *arg2)
+{
+ f_user_t *f_user1 = (f_user_t *)arg1;
+ f_user_t *f_user2 = (f_user_t *)arg2;
+
+ /*
+ * we should only be called for f_user_t entires that represent
+ * a kernel file consumer
+ */
+ ASSERT(f_user1->fu_flags & F_KERNEL);
+ ASSERT(f_user2->fu_flags & F_KERNEL);
+
+ if (f_user1->fu_modid != f_user2->fu_modid)
+ return ((f_user1->fu_modid < f_user2->fu_modid) ? -1 : 1);
+
+ if (f_user1->fu_instance != f_user2->fu_instance)
+ return ((f_user1->fu_instance < f_user2->fu_instance) ? -1 : 1);
+
+ if (f_user1->fu_minor != f_user2->fu_minor)
+ return ((f_user1->fu_minor < f_user2->fu_minor) ? -1 : 1);
+
+ return (0);
+}
+
+static fu_data_t *
+dofkusers(vnode_t *fvp, int flags, int *error)
+{
+ dofkusers_arg_t dofkusers_arg;
+ fu_data_t *fu_data;
+ int user_max, i;
+
+ /*
+ * we only keep track of kernel device consumers, so if the
+ * target vnode isn't a device then there's nothing to do here
+ */
+ if (!vn_matchops(fvp, spec_getvnodeops()))
+ return (NULL);
+
+ /* allocate the data structure to return our results in */
+ user_max = ldi_usage_count();
+ fu_data = kmem_alloc(fu_data_size(user_max), KM_SLEEP);
+ fu_data->fud_user_max = user_max;
+ fu_data->fud_user_count = 0;
+
+ /* invoke the callback to collect device usage information */
+ dofkusers_arg.fvp = fvp;
+ dofkusers_arg.flags = flags;
+ dofkusers_arg.error = error;
+ dofkusers_arg.fu_data = fu_data;
+ ldi_usage_walker(&dofkusers_arg, dofkusers_walker);
+
+ /* check for errors */
+ if (*error != 0)
+ return (fu_data);
+
+ /* if there aren't any file consumers then return */
+ if (fu_data->fud_user_count == 0)
+ return (fu_data);
+
+ /*
+ * since we ignore the spec_type of the target we're trying to
+ * access it's possible that we could have duplicates entries in
+ * the list of consumers.
+ *
+ * we don't want to check for duplicate in the callback because
+ * we're holding locks in the ldi when the callback is invoked.
+ *
+ * so here we need to go through the array of file consumers
+ * and remove duplicate entries.
+ */
+
+ /* first sort the array of file consumers */
+ qsort((caddr_t)fu_data->fud_user, fu_data->fud_user_count,
+ sizeof (f_user_t), f_user_cmp);
+
+ /* then remove any duplicate entires */
+ i = 1;
+ while (i < fu_data->fud_user_count) {
+
+ if (f_user_cmp(&fu_data->fud_user[i],
+ &fu_data->fud_user[i - 1]) != 0) {
+ /*
+ * the current element is unique, move onto
+ * the next one
+ */
+ i++;
+ continue;
+ }
+
+ /*
+ * this entry is a duplicate so if it's not the last
+ * entry in the array then remove it.
+ */
+ fu_data->fud_user_count--;
+ if (i == fu_data->fud_user_count)
+ break;
+
+ bcopy(&fu_data->fud_user[i + 1], &fu_data->fud_user[i],
+ sizeof (f_user_t) * (fu_data->fud_user_count - i));
+ }
+
+ return (fu_data);
+}
+
+/*
+ * Determine the ways in which processes and the kernel are using a named
+ * file or mounted file system (path). Normally return 0. In case of an
+ * error appropriate errno will be returned.
+ *
+ * Upon success, uts_fusers will also copyout the file usage information
+ * in the form of an array of f_user_t's that are contained within an
+ * fu_data_t pointed to by userbp.
+ */
+static int
+uts_fusers(char *path, int flags, intptr_t userbp)
+{
+ fu_data_t *fu_data = NULL, *fuk_data = NULL;
+ fu_data_t fu_header;
+ vnode_t *fvp = NULL;
+ size_t bcount;
+ int error = 0;
+ int total_max, total_out;
+ int contained = (flags & F_CONTAINED);
+ int dip_usage = (flags & F_DEVINFO);
+ int fvp_isdev;
+
+
+ /* figure out how man f_user_t's we can safetly copy out */
+ if (copyin((const void *)userbp, &total_max, sizeof (total_max)))
+ return (EFAULT);
+
+ /*
+ * check if we only want a count of how many kernel device
+ * consumers exist
+ */
+ if (flags & F_KINFO_COUNT) {
+ fu_header.fud_user_max = total_max;
+ fu_header.fud_user_count = ldi_usage_count();
+ bcount = fu_data_size(0);
+ if (copyout(&fu_header, (void *)userbp, bcount))
+ return (EFAULT);
+ return (0);
+ }
+
+ /* get the vnode for the file we want to look up usage for */
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &fvp);
+ if (error != 0)
+ return (error);
+ ASSERT(fvp);
+ fvp_isdev = vn_matchops(fvp, spec_getvnodeops());
+
+ /*
+ * if we want to report usage for all files contained within a
+ * file system then the target file better correspond to the
+ * root node of a mounted file system, or the root of a zone.
+ */
+ if (contained && !(fvp->v_flag & VROOT) &&
+ fvp != curproc->p_zone->zone_rootvp) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * if we want to report usage for all files contained within a
+ * file system then the target file better not be a device.
+ */
+ if (contained && fvp_isdev) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * if we want to report usage for a device instance then the
+ * target file better corrospond to a device
+ */
+ if (dip_usage && !fvp_isdev) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * if the target vnode isn't a device and it has a reference count
+ * of one then no one else is going to have it open so we don't
+ * have any work to do.
+ */
+ if (!fvp_isdev && (fvp->v_count == 1)) {
+ goto out;
+ }
+
+ /* look up usage information for this vnode */
+ fu_data = dofusers(fvp, flags);
+ fuk_data = dofkusers(fvp, flags, &error);
+ if (error != 0)
+ goto out;
+
+ /* get a count of the number of f_user_t's we need to copy out */
+ total_out = 0;
+ if (fu_data)
+ total_out += fu_data->fud_user_count;
+ if (fuk_data)
+ total_out += fuk_data->fud_user_count;
+
+ /* check if there is enough space to copyout all results */
+ if (total_out > total_max) {
+ error = E2BIG;
+ goto out;
+ }
+
+ /* copyout file usage info counts */
+ fu_header.fud_user_max = total_max;
+ fu_header.fud_user_count = total_out;
+ bcount = fu_data_size(0);
+ if (copyout(&fu_header, (void *)userbp, bcount)) {
+ error = EFAULT;
+ goto out;
+ }
+
+ /* copyout userland process file usage info */
+ if ((fu_data != NULL) && (fu_data->fud_user_count > 0)) {
+ userbp += bcount;
+ bcount = fu_data->fud_user_count * sizeof (f_user_t);
+ if (copyout(fu_data->fud_user, (void *)userbp, bcount)) {
+ error = EFAULT;
+ goto out;
+ }
+ }
+
+ /* copyout kernel file usage info */
+ if ((fuk_data != NULL) && (fuk_data->fud_user_count > 0)) {
+ userbp += bcount;
+ bcount = fuk_data->fud_user_count * sizeof (f_user_t);
+ if (copyout(fuk_data->fud_user, (void *)userbp, bcount)) {
+ error = EFAULT;
+ goto out;
+ }
+ }
+
+out:
+ /* release the vnode that we were looking up usage for */
+ VN_RELE(fvp);
+
+ /* release any allocated memory */
+ if (fu_data)
+ kmem_free(fu_data, fu_data_size(fu_data->fud_user_max));
+ if (fuk_data)
+ kmem_free(fuk_data, fu_data_size(fuk_data->fud_user_max));
+
+ return (error);
+}