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, 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);
 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 }