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