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