1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29/*	  All Rights Reserved	*/
30
31
32#include <sys/param.h>
33#include <sys/inttypes.h>
34#include <sys/types.h>
35#include <sys/sysmacros.h>
36#include <sys/systm.h>
37#include <sys/user.h>
38#include <sys/errno.h>
39#include <sys/vfs.h>
40#include <sys/vnode.h>
41#include <sys/file.h>
42#include <sys/proc.h>
43#include <sys/session.h>
44#include <sys/var.h>
45#include <sys/utsname.h>
46#include <sys/utssys.h>
47#include <sys/ustat.h>
48#include <sys/statvfs.h>
49#include <sys/kmem.h>
50#include <sys/debug.h>
51#include <sys/pathname.h>
52#include <sys/modctl.h>
53#include <sys/fs/snode.h>
54#include <sys/sunldi_impl.h>
55#include <sys/ddi.h>
56#include <sys/sunddi.h>
57#include <sys/cmn_err.h>
58#include <sys/ddipropdefs.h>
59#include <sys/ddi_impldefs.h>
60#include <sys/modctl.h>
61#include <sys/flock.h>
62#include <sys/share.h>
63#include <vm/as.h>
64#include <vm/seg.h>
65#include <vm/seg_vn.h>
66#include <util/qsort.h>
67#include <sys/zone.h>
68
69/*
70 * utssys()
71 */
72static int		uts_fusers(char *, int, intptr_t);
73static int		_statvfs64_by_dev(dev_t, struct statvfs64 *);
74
75#if defined(_ILP32) || defined(_SYSCALL32_IMPL)
76
77static int utssys_uname32(caddr_t, rval_t *);
78static int utssys_ustat32(dev_t, struct ustat32 *);
79
80int64_t
81utssys32(void *buf, int arg, int type, void *outbp)
82{
83	int error;
84	rval_t rv;
85
86	rv.r_vals = 0;
87
88	switch (type) {
89	case UTS_UNAME:
90		/*
91		 * This is an obsolete way to get the utsname structure
92		 * (it only gives you the first 8 characters of each field!)
93		 * uname(2) is the preferred and better interface.
94		 */
95		error = utssys_uname32(buf, &rv);
96		break;
97	case UTS_USTAT:
98		error = utssys_ustat32(expldev((dev32_t)arg), buf);
99		break;
100	case UTS_FUSERS:
101		error = uts_fusers(buf, arg, (intptr_t)outbp);
102		break;
103	default:
104		error = EINVAL;
105		break;
106	}
107
108	return (error == 0 ? rv.r_vals : (int64_t)set_errno(error));
109}
110
111static int
112utssys_uname32(caddr_t buf, rval_t *rvp)
113{
114	if (copyout(utsname.sysname, buf, 8))
115		return (EFAULT);
116	buf += 8;
117	if (subyte(buf, 0) < 0)
118		return (EFAULT);
119	buf++;
120	if (copyout(uts_nodename(), buf, 8))
121		return (EFAULT);
122	buf += 8;
123	if (subyte(buf, 0) < 0)
124		return (EFAULT);
125	buf++;
126	if (copyout(utsname.release, buf, 8))
127		return (EFAULT);
128	buf += 8;
129	if (subyte(buf, 0) < 0)
130		return (EFAULT);
131	buf++;
132	if (copyout(utsname.version, buf, 8))
133		return (EFAULT);
134	buf += 8;
135	if (subyte(buf, 0) < 0)
136		return (EFAULT);
137	buf++;
138	if (copyout(utsname.machine, buf, 8))
139		return (EFAULT);
140	buf += 8;
141	if (subyte(buf, 0) < 0)
142		return (EFAULT);
143	rvp->r_val1 = 1;
144	return (0);
145}
146
147static int
148utssys_ustat32(dev_t dev, struct ustat32 *cbuf)
149{
150	struct ustat32 ust32;
151	struct statvfs64 stvfs;
152	fsblkcnt64_t	fsbc64;
153	char *cp, *cp2;
154	int i, error;
155
156	if ((error = _statvfs64_by_dev(dev, &stvfs)) != 0)
157		return (error);
158
159	fsbc64 = stvfs.f_bfree * (stvfs.f_frsize / 512);
160	/*
161	 * Check to see if the number of free blocks can be expressed
162	 * in 31 bits or whether the number of free files is more than
163	 * can be expressed in 32 bits and is not -1 (UINT64_MAX).  NFS
164	 * Version 2 does not support the number of free files and
165	 * hence will return -1.  -1, when translated from a 32 bit
166	 * quantity to an unsigned 64 bit quantity, turns into UINT64_MAX.
167	 */
168	if (fsbc64 > INT32_MAX ||
169	    (stvfs.f_ffree > UINT32_MAX && stvfs.f_ffree != UINT64_MAX))
170		return (EOVERFLOW);
171
172	ust32.f_tfree = (daddr32_t)fsbc64;
173	ust32.f_tinode = (ino32_t)stvfs.f_ffree;
174
175	cp = stvfs.f_fstr;
176	cp2 = ust32.f_fname;
177	i = 0;
178	while (i++ < sizeof (ust32.f_fname))
179		if (*cp != '\0')
180			*cp2++ = *cp++;
181		else
182			*cp2++ = '\0';
183	while (*cp != '\0' &&
184	    (i++ < sizeof (stvfs.f_fstr) - sizeof (ust32.f_fpack)))
185		cp++;
186	(void) strncpy(ust32.f_fpack, cp + 1, sizeof (ust32.f_fpack));
187
188	if (copyout(&ust32, cbuf, sizeof (ust32)))
189		return (EFAULT);
190	return (0);
191}
192
193#endif	/* _ILP32 || _SYSCALL32_IMPL */
194
195#ifdef _LP64
196
197static int uts_ustat64(dev_t, struct ustat *);
198
199int64_t
200utssys64(void *buf, long arg, int type, void *outbp)
201{
202	int error;
203	rval_t rv;
204
205	rv.r_vals = 0;
206
207	switch (type) {
208	case UTS_USTAT:
209		error = uts_ustat64((dev_t)arg, buf);
210		break;
211	case UTS_FUSERS:
212		error = uts_fusers(buf, (int)arg, (intptr_t)outbp);
213		break;
214	default:
215		error = EINVAL;
216		break;
217	}
218
219	return (error == 0 ? rv.r_vals : (int64_t)set_errno(error));
220}
221
222static int
223uts_ustat64(dev_t dev, struct ustat *cbuf)
224{
225	struct ustat ust;
226	struct statvfs64 stvfs;
227	fsblkcnt64_t	fsbc64;
228	char *cp, *cp2;
229	int i, error;
230
231	if ((error = _statvfs64_by_dev(dev, &stvfs)) != 0)
232		return (error);
233
234	fsbc64 = stvfs.f_bfree * (stvfs.f_frsize / 512);
235	ust.f_tfree = (daddr_t)fsbc64;
236	ust.f_tinode = (ino_t)stvfs.f_ffree;
237
238	cp = stvfs.f_fstr;
239	cp2 = ust.f_fname;
240	i = 0;
241	while (i++ < sizeof (ust.f_fname))
242		if (*cp != '\0')
243			*cp2++ = *cp++;
244		else
245			*cp2++ = '\0';
246	while (*cp != '\0' &&
247	    (i++ < sizeof (stvfs.f_fstr) - sizeof (ust.f_fpack)))
248		cp++;
249	(void) strncpy(ust.f_fpack, cp + 1, sizeof (ust.f_fpack));
250
251	if (copyout(&ust, cbuf, sizeof (ust)))
252		return (EFAULT);
253	return (0);
254}
255
256#endif	/* _LP64 */
257
258/*
259 * Utility routine for the ustat implementations.
260 * (If it wasn't for the 'find-by-dev_t' semantic of ustat(2), we could push
261 * this all out into userland, sigh.)
262 */
263static int
264_statvfs64_by_dev(dev_t dev, struct statvfs64 *svp)
265{
266	vfs_t *vfsp;
267	int error;
268
269	if ((vfsp = vfs_dev2vfsp(dev)) == NULL) {
270		/*
271		 * See if it's the root of our zone.
272		 */
273		vfsp = curproc->p_zone->zone_rootvp->v_vfsp;
274		if (vfsp->vfs_dev == dev) {
275			VFS_HOLD(vfsp);
276		} else {
277			vfsp = NULL;
278		}
279	}
280	if (vfsp == NULL)
281		return (EINVAL);
282	error = VFS_STATVFS(vfsp, svp);
283	VFS_RELE(vfsp);
284	return (error);
285}
286
287/*
288 * Check if this pid has an NBMAND lock or share reservation
289 * on this vp. llp is a snapshoted list of all NBMAND locks
290 * set by this pid. Return 1 if there is an NBMAND lock else
291 * return 0.
292 */
293static int
294proc_has_nbmand_on_vp(vnode_t *vp, pid_t pid, locklist_t *llp)
295{
296	/*
297	 * Any NBMAND lock held by the process on this vp?
298	 */
299	while (llp) {
300		if (llp->ll_vp == vp) {
301			return (1);
302		}
303		llp = llp->ll_next;
304	}
305	/*
306	 * Any NBMAND share reservation on the vp for this process?
307	 */
308	return (proc_has_nbmand_share_on_vp(vp, pid));
309}
310
311static fu_data_t *
312dofusers(vnode_t *fvp, int flags)
313{
314	fu_data_t	*fu_data;
315	proc_t		*prp;
316	vfs_t		*cvfsp;
317	pid_t		npids, pidx, *pidlist;
318	int		v_proc = v.v_proc;	/* max # of procs */
319	int		pcnt = 0;
320	int		contained = (flags & F_CONTAINED);
321	int		nbmandonly = (flags & F_NBMANDLIST);
322	int		dip_usage = (flags & F_DEVINFO);
323	int		fvp_isdev = vn_matchops(fvp, spec_getvnodeops());
324	zone_t *zone = curproc->p_zone;
325	int inglobal = INGLOBALZONE(curproc);
326
327	/* get a pointer to the file system containing this vnode */
328	cvfsp = fvp->v_vfsp;
329	ASSERT(cvfsp);
330
331	/* allocate the data structure to return our results in */
332	fu_data = kmem_alloc(fu_data_size(v_proc), KM_SLEEP);
333	fu_data->fud_user_max = v_proc;
334	fu_data->fud_user_count = 0;
335
336	/* get a snapshot of all the pids we're going to check out */
337	pidlist = kmem_alloc(v_proc * sizeof (pid_t), KM_SLEEP);
338	mutex_enter(&pidlock);
339	for (npids = 0, prp = practive; prp != NULL; prp = prp->p_next) {
340		if (inglobal || prp->p_zone == zone)
341			pidlist[npids++] = prp->p_pid;
342	}
343	mutex_exit(&pidlock);
344
345	/* grab each process and check its file usage */
346	for (pidx = 0; pidx < npids; pidx++) {
347		locklist_t	*llp = NULL;
348		uf_info_t	*fip;
349		vnode_t		*vp;
350		user_t		*up;
351		sess_t		*sp;
352		uid_t		uid;
353		pid_t		pid = pidlist[pidx];
354		int		i, use_flag = 0;
355
356		/*
357		 * grab prp->p_lock using sprlock()
358		 * if sprlock() fails the process does not exists anymore
359		 */
360		prp = sprlock(pid);
361		if (prp == NULL)
362			continue;
363
364		/* get the processes credential info in case we need it */
365		mutex_enter(&prp->p_crlock);
366		uid = crgetruid(prp->p_cred);
367		mutex_exit(&prp->p_crlock);
368
369		/*
370		 * it's safe to drop p_lock here because we
371		 * called sprlock() before and it set the SPRLOCK
372		 * flag for the process so it won't go away.
373		 */
374		mutex_exit(&prp->p_lock);
375
376		/*
377		 * now we want to walk a processes open file descriptors
378		 * to do this we need to grab the fip->fi_lock.  (you
379		 * can't hold p_lock when grabbing the fip->fi_lock.)
380		 */
381		fip = P_FINFO(prp);
382		mutex_enter(&fip->fi_lock);
383
384		/*
385		 * Snapshot nbmand locks for pid
386		 */
387		llp = flk_active_nbmand_locks(prp->p_pid);
388		for (i = 0; i < fip->fi_nfiles; i++) {
389			uf_entry_t	*ufp;
390			file_t		*fp;
391
392			UF_ENTER(ufp, fip, i);
393			if (((fp = ufp->uf_file) == NULL) ||
394			    ((vp = fp->f_vnode) == NULL)) {
395				UF_EXIT(ufp);
396				continue;
397			}
398
399			/*
400			 * if the target file (fvp) is not a device
401			 * and corrosponds to the root of a filesystem
402			 * (cvfsp), then check if it contains the file
403			 * is use by this process (vp).
404			 */
405			if (contained && (vp->v_vfsp == cvfsp))
406				use_flag |= F_OPEN;
407
408			/*
409			 * if the target file (fvp) is not a device,
410			 * then check if it matches the file in use
411			 * by this process (vp).
412			 */
413			if (!fvp_isdev && VN_CMP(fvp, vp))
414				use_flag |= F_OPEN;
415
416			/*
417			 * if the target file (fvp) is a device,
418			 * then check if the current file in use
419			 * by this process (vp) maps to the same device
420			 * minor node.
421			 */
422			if (fvp_isdev &&
423			    vn_matchops(vp, spec_getvnodeops()) &&
424			    (fvp->v_rdev == vp->v_rdev))
425				use_flag |= F_OPEN;
426
427			/*
428			 * if the target file (fvp) is a device,
429			 * and we're checking for device instance
430			 * usage, then check if the current file in use
431			 * by this process (vp) maps to the same device
432			 * instance.
433			 */
434			if (dip_usage &&
435			    vn_matchops(vp, spec_getvnodeops()) &&
436			    (VTOCS(fvp)->s_dip == VTOCS(vp)->s_dip))
437				use_flag |= F_OPEN;
438
439			/*
440			 * if the current file in use by this process (vp)
441			 * doesn't match what we're looking for, move on
442			 * to the next file in the process.
443			 */
444			if ((use_flag & F_OPEN) == 0) {
445				UF_EXIT(ufp);
446				continue;
447			}
448
449			if (proc_has_nbmand_on_vp(vp, prp->p_pid, llp)) {
450				/* A nbmand found so we're done.  */
451				use_flag |= F_NBM;
452				UF_EXIT(ufp);
453				break;
454			}
455			UF_EXIT(ufp);
456		}
457		if (llp)
458			flk_free_locklist(llp);
459
460		mutex_exit(&fip->fi_lock);
461
462		/*
463		 * If nbmand usage tracking is desired and no nbmand was
464		 * found for this process, then no need to do further
465		 * usage tracking for this process.
466		 */
467		if (nbmandonly && (!(use_flag & F_NBM))) {
468			/*
469			 * grab the process lock again, clear the SPRLOCK
470			 * flag, release the process, and continue.
471			 */
472			mutex_enter(&prp->p_lock);
473			sprunlock(prp);
474			continue;
475		}
476
477		/*
478		 * All other types of usage.
479		 * For the next few checks we need to hold p_lock.
480		 */
481		mutex_enter(&prp->p_lock);
482		up = PTOU(prp);
483		if (fvp_isdev) {
484			/*
485			 * if the target file (fvp) is a device
486			 * then check if it matches the processes tty
487			 *
488			 * we grab s_lock to protect ourselves against
489			 * freectty() freeing the vnode out from under us.
490			 */
491			sp = prp->p_sessp;
492			mutex_enter(&sp->s_lock);
493			vp = prp->p_sessp->s_vp;
494			if (vp != NULL) {
495				if (fvp->v_rdev == vp->v_rdev)
496					use_flag |= F_TTY;
497
498				if (dip_usage &&
499				    (VTOCS(fvp)->s_dip == VTOCS(vp)->s_dip))
500					use_flag |= F_TTY;
501			}
502			mutex_exit(&sp->s_lock);
503		} else {
504			/* check the processes current working directory */
505			if (up->u_cdir &&
506			    (VN_CMP(fvp, up->u_cdir) ||
507			    (contained && (up->u_cdir->v_vfsp == cvfsp))))
508				use_flag |= F_CDIR;
509
510			/* check the processes root directory */
511			if (up->u_rdir &&
512			    (VN_CMP(fvp, up->u_rdir) ||
513			    (contained && (up->u_rdir->v_vfsp == cvfsp))))
514				use_flag |= F_RDIR;
515
516			/* check the program text vnode */
517			if (prp->p_exec &&
518			    (VN_CMP(fvp, prp->p_exec) ||
519			    (contained && (prp->p_exec->v_vfsp == cvfsp))))
520				use_flag |= F_TEXT;
521		}
522
523		/* Now we can drop p_lock again */
524		mutex_exit(&prp->p_lock);
525
526		/*
527		 * now we want to walk a processes memory mappings.
528		 * to do this we need to grab the prp->p_as lock.  (you
529		 * can't hold p_lock when grabbing the prp->p_as lock.)
530		 */
531		if (prp->p_as != &kas) {
532			struct seg	*seg;
533			struct as	*as = prp->p_as;
534
535			AS_LOCK_ENTER(as, RW_READER);
536			for (seg = AS_SEGFIRST(as); seg;
537			    seg = AS_SEGNEXT(as, seg)) {
538				/*
539				 * if we can't get a backing vnode for this
540				 * segment then skip it
541				 */
542				vp = NULL;
543				if ((SEGOP_GETVP(seg, seg->s_base, &vp)) ||
544				    (vp == NULL))
545					continue;
546
547				/*
548				 * if the target file (fvp) is not a device
549				 * and corrosponds to the root of a filesystem
550				 * (cvfsp), then check if it contains the
551				 * vnode backing this segment (vp).
552				 */
553				if (contained && (vp->v_vfsp == cvfsp)) {
554					use_flag |= F_MAP;
555					break;
556				}
557
558				/*
559				 * if the target file (fvp) is not a device,
560				 * check if it matches the the vnode backing
561				 * this segment (vp).
562				 */
563				if (!fvp_isdev && VN_CMP(fvp, vp)) {
564					use_flag |= F_MAP;
565					break;
566				}
567
568				/*
569				 * if the target file (fvp) isn't a device,
570				 * or the the vnode backing this segment (vp)
571				 * isn't a device then continue.
572				 */
573				if (!fvp_isdev ||
574				    !vn_matchops(vp, spec_getvnodeops()))
575					continue;
576
577				/*
578				 * check if the vnode backing this segment
579				 * (vp) maps to the same device minor node
580				 * as the target device (fvp)
581				 */
582				if (fvp->v_rdev == vp->v_rdev) {
583					use_flag |= F_MAP;
584					break;
585				}
586
587				/*
588				 * if we're checking for device instance
589				 * usage, then check if the vnode backing
590				 * this segment (vp) maps to the same device
591				 * instance as the target device (fvp).
592				 */
593				if (dip_usage &&
594				    (VTOCS(fvp)->s_dip == VTOCS(vp)->s_dip)) {
595					use_flag |= F_MAP;
596					break;
597				}
598			}
599			AS_LOCK_EXIT(as);
600		}
601
602		if (use_flag) {
603			ASSERT(pcnt < fu_data->fud_user_max);
604			fu_data->fud_user[pcnt].fu_flags = use_flag;
605			fu_data->fud_user[pcnt].fu_pid = pid;
606			fu_data->fud_user[pcnt].fu_uid = uid;
607			pcnt++;
608		}
609
610		/*
611		 * grab the process lock again, clear the SPRLOCK
612		 * flag, release the process, and continue.
613		 */
614		mutex_enter(&prp->p_lock);
615		sprunlock(prp);
616	}
617
618	kmem_free(pidlist, v_proc * sizeof (pid_t));
619
620	fu_data->fud_user_count = pcnt;
621	return (fu_data);
622}
623
624typedef struct dofkusers_arg {
625	vnode_t		*fvp;
626	int		flags;
627	int		*error;
628	fu_data_t	*fu_data;
629} dofkusers_arg_t;
630
631static int
632dofkusers_walker(const ldi_usage_t *ldi_usage, void *arg)
633{
634	dofkusers_arg_t	*dofkusers_arg = (dofkusers_arg_t *)arg;
635
636	vnode_t		*fvp = dofkusers_arg->fvp;
637	int		flags = dofkusers_arg->flags;
638	int		*error = dofkusers_arg->error;
639	fu_data_t	*fu_data = dofkusers_arg->fu_data;
640
641	modid_t		modid;
642	minor_t		minor;
643	int		instance;
644	int		dip_usage = (flags & F_DEVINFO);
645
646	ASSERT(*error == 0);
647	ASSERT(vn_matchops(fvp, spec_getvnodeops()));
648
649	/*
650	 * check if the dev_t of the target device matches the dev_t
651	 * of the device we're trying to find usage info for.
652	 */
653	if (fvp->v_rdev != ldi_usage->tgt_devt) {
654
655		/*
656		 * if the dev_ts don't match and we're not trying
657		 * to find usage information for device instances
658		 * then return
659		 */
660		if (!dip_usage)
661			return (LDI_USAGE_CONTINUE);
662
663
664		/*
665		 * we're trying to find usage information for an
666		 * device instance instead of just a minor node.
667		 *
668		 * check if the dip for the target device matches the
669		 * dip of the device we're trying to find usage info for.
670		 */
671		if (VTOCS(fvp)->s_dip != ldi_usage->tgt_dip)
672			return (LDI_USAGE_CONTINUE);
673	}
674
675	if (fu_data->fud_user_count >= fu_data->fud_user_max) {
676		*error = E2BIG;
677		return (LDI_USAGE_TERMINATE);
678	}
679
680	/* get the device vnode user information */
681	modid = ldi_usage->src_modid;
682	ASSERT(modid != -1);
683
684	minor = instance = -1;
685	if (ldi_usage->src_dip != NULL) {
686		instance = DEVI(ldi_usage->src_dip)->devi_instance;
687	}
688	if (ldi_usage->src_devt != DDI_DEV_T_NONE) {
689		minor = getminor(ldi_usage->src_devt);
690	}
691
692	/* set the device vnode user information */
693	fu_data->fud_user[fu_data->fud_user_count].fu_flags = F_KERNEL;
694	fu_data->fud_user[fu_data->fud_user_count].fu_modid = modid;
695	fu_data->fud_user[fu_data->fud_user_count].fu_instance = instance;
696	fu_data->fud_user[fu_data->fud_user_count].fu_minor = minor;
697
698	fu_data->fud_user_count++;
699
700	return (LDI_USAGE_CONTINUE);
701}
702
703int
704f_user_cmp(const void *arg1, const void *arg2)
705{
706	f_user_t *f_user1 = (f_user_t *)arg1;
707	f_user_t *f_user2 = (f_user_t *)arg2;
708
709	/*
710	 * we should only be called for f_user_t entires that represent
711	 * a kernel file consumer
712	 */
713	ASSERT(f_user1->fu_flags & F_KERNEL);
714	ASSERT(f_user2->fu_flags & F_KERNEL);
715
716	if (f_user1->fu_modid != f_user2->fu_modid)
717		return ((f_user1->fu_modid < f_user2->fu_modid) ? -1 : 1);
718
719	if (f_user1->fu_instance != f_user2->fu_instance)
720		return ((f_user1->fu_instance < f_user2->fu_instance) ? -1 : 1);
721
722	if (f_user1->fu_minor != f_user2->fu_minor)
723		return ((f_user1->fu_minor < f_user2->fu_minor) ? -1 : 1);
724
725	return (0);
726}
727
728static fu_data_t *
729dofkusers(vnode_t *fvp, int flags, int *error)
730{
731	dofkusers_arg_t	dofkusers_arg;
732	fu_data_t	*fu_data;
733	int		user_max, i;
734
735	/*
736	 * we only keep track of kernel device consumers, so if the
737	 * target vnode isn't a device then there's nothing to do here
738	 */
739	if (!vn_matchops(fvp, spec_getvnodeops()))
740		return (NULL);
741
742	/* allocate the data structure to return our results in */
743	user_max = ldi_usage_count();
744	fu_data = kmem_alloc(fu_data_size(user_max), KM_SLEEP);
745	fu_data->fud_user_max = user_max;
746	fu_data->fud_user_count = 0;
747
748	/* invoke the callback to collect device usage information */
749	dofkusers_arg.fvp = fvp;
750	dofkusers_arg.flags = flags;
751	dofkusers_arg.error = error;
752	dofkusers_arg.fu_data = fu_data;
753	ldi_usage_walker(&dofkusers_arg, dofkusers_walker);
754
755	/* check for errors */
756	if (*error != 0)
757		return (fu_data);
758
759	/* if there aren't any file consumers then return */
760	if (fu_data->fud_user_count == 0)
761		return (fu_data);
762
763	/*
764	 * since we ignore the spec_type of the target we're trying to
765	 * access it's possible that we could have duplicates entries in
766	 * the list of consumers.
767	 *
768	 * we don't want to check for duplicate in the callback because
769	 * we're holding locks in the ldi when the callback is invoked.
770	 *
771	 * so here we need to go through the array of file consumers
772	 * and remove duplicate entries.
773	 */
774
775	/* first sort the array of file consumers */
776	qsort((caddr_t)fu_data->fud_user, fu_data->fud_user_count,
777	    sizeof (f_user_t), f_user_cmp);
778
779	/* then remove any duplicate entires */
780	i = 1;
781	while (i < fu_data->fud_user_count) {
782
783		if (f_user_cmp(&fu_data->fud_user[i],
784		    &fu_data->fud_user[i - 1]) != 0) {
785			/*
786			 * the current element is unique, move onto
787			 * the next one
788			 */
789			i++;
790			continue;
791		}
792
793		/*
794		 * this entry is a duplicate so if it's not the last
795		 * entry in the array then remove it.
796		 */
797		fu_data->fud_user_count--;
798		if (i == fu_data->fud_user_count)
799			break;
800
801		bcopy(&fu_data->fud_user[i + 1], &fu_data->fud_user[i],
802		    sizeof (f_user_t) * (fu_data->fud_user_count - i));
803	}
804
805	return (fu_data);
806}
807
808/*
809 * Determine the ways in which processes and the kernel are using a named
810 * file or mounted file system (path).  Normally return 0.  In case of an
811 * error appropriate errno will be returned.
812 *
813 * Upon success, uts_fusers will also copyout the file usage information
814 * in the form of an array of f_user_t's that are contained within an
815 * fu_data_t pointed to by userbp.
816 */
817static int
818uts_fusers(char *path, int flags, intptr_t userbp)
819{
820	fu_data_t	*fu_data = NULL, *fuk_data = NULL;
821	fu_data_t	fu_header;
822	vnode_t		*fvp = NULL;
823	size_t		bcount;
824	int		error = 0;
825	int		total_max, total_out;
826	int		contained = (flags & F_CONTAINED);
827	int		dip_usage = (flags & F_DEVINFO);
828	int		fvp_isdev;
829
830
831	/* figure out how man f_user_t's we can safetly copy out */
832	if (copyin((const void *)userbp, &total_max, sizeof (total_max)))
833		return (EFAULT);
834
835	/*
836	 * check if we only want a count of how many kernel device
837	 * consumers exist
838	 */
839	if (flags & F_KINFO_COUNT) {
840		fu_header.fud_user_max = total_max;
841		fu_header.fud_user_count = ldi_usage_count();
842		bcount = fu_data_size(0);
843		if (copyout(&fu_header, (void *)userbp, bcount))
844			return (EFAULT);
845		return (0);
846	}
847
848	/* get the vnode for the file we want to look up usage for */
849	error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &fvp);
850	if (error != 0)
851		return (error);
852	ASSERT(fvp);
853	fvp_isdev = vn_matchops(fvp, spec_getvnodeops());
854
855	/*
856	 * if we want to report usage for all files contained within a
857	 * file system then the target file better correspond to the
858	 * root node of a mounted file system, or the root of a zone.
859	 */
860	if (contained && !(fvp->v_flag & VROOT) &&
861	    fvp != curproc->p_zone->zone_rootvp) {
862		error = EINVAL;
863		goto out;
864	}
865
866	/*
867	 * if we want to report usage for all files contained within a
868	 * file system then the target file better not be a device.
869	 */
870	if (contained && fvp_isdev) {
871		error = EINVAL;
872		goto out;
873	}
874
875	/*
876	 * if we want to report usage for a device instance then the
877	 * target file better corrospond to a device
878	 */
879	if (dip_usage && !fvp_isdev) {
880		error = EINVAL;
881		goto out;
882	}
883
884	/*
885	 * if the target vnode isn't a device and it has a reference count
886	 * of one then no one else is going to have it open so we don't
887	 * have any work to do.
888	 */
889	if (!fvp_isdev && (fvp->v_count == 1)) {
890		goto out;
891	}
892
893	/* look up usage information for this vnode */
894	fu_data = dofusers(fvp, flags);
895	fuk_data = dofkusers(fvp, flags, &error);
896	if (error != 0)
897		goto out;
898
899	/* get a count of the number of f_user_t's we need to copy out */
900	total_out = 0;
901	if (fu_data)
902		total_out += fu_data->fud_user_count;
903	if (fuk_data)
904		total_out += fuk_data->fud_user_count;
905
906	/* check if there is enough space to copyout all results */
907	if (total_out > total_max) {
908		error = E2BIG;
909		goto out;
910	}
911
912	/* copyout file usage info counts */
913	fu_header.fud_user_max = total_max;
914	fu_header.fud_user_count = total_out;
915	bcount = fu_data_size(0);
916	if (copyout(&fu_header, (void *)userbp, bcount)) {
917		error = EFAULT;
918		goto out;
919	}
920
921	/* copyout userland process file usage info */
922	if ((fu_data != NULL) && (fu_data->fud_user_count > 0)) {
923		userbp += bcount;
924		bcount = fu_data->fud_user_count * sizeof (f_user_t);
925		if (copyout(fu_data->fud_user, (void *)userbp, bcount)) {
926			error = EFAULT;
927			goto out;
928		}
929	}
930
931	/* copyout kernel file usage info */
932	if ((fuk_data != NULL) && (fuk_data->fud_user_count > 0)) {
933		userbp += bcount;
934		bcount = fuk_data->fud_user_count * sizeof (f_user_t);
935		if (copyout(fuk_data->fud_user, (void *)userbp, bcount)) {
936			error = EFAULT;
937			goto out;
938		}
939	}
940
941out:
942	/* release the vnode that we were looking up usage for */
943	VN_RELE(fvp);
944
945	/* release any allocated memory */
946	if (fu_data)
947		kmem_free(fu_data, fu_data_size(fu_data->fud_user_max));
948	if (fuk_data)
949		kmem_free(fuk_data, fu_data_size(fuk_data->fud_user_max));
950
951	return (error);
952}
953