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 */ 72 static int uts_fusers(char *, int, intptr_t); 73 static int _statvfs64_by_dev(dev_t, struct statvfs64 *); 74 75 #if defined(_ILP32) || defined(_SYSCALL32_IMPL) 76 77 static int utssys_uname32(caddr_t, rval_t *); 78 static int utssys_ustat32(dev_t, struct ustat32 *); 79 80 int64_t 81 utssys32(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 111 static int 112 utssys_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 147 static int 148 utssys_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 197 static int uts_ustat64(dev_t, struct ustat *); 198 199 int64_t 200 utssys64(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 222 static int 223 uts_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 */ 263 static 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 */ 293 static int 294 proc_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 311 static fu_data_t * 312 dofusers(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 624 typedef struct dofkusers_arg { 625 vnode_t *fvp; 626 int flags; 627 int *error; 628 fu_data_t *fu_data; 629 } dofkusers_arg_t; 630 631 static int 632 dofkusers_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 703 int 704 f_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 728 static fu_data_t * 729 dofkusers(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 */ 817 static int 818 uts_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 941 out: 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