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