diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/common/syscall/utssys.c | |
download | illumos-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.c | 954 |
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); +} |