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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  *
  25  *      Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
  26  *      All rights reserved.
  27  */
  28 
  29 #include <sys/param.h>
  30 #include <sys/systm.h>
  31 #include <sys/thread.h>
  32 #include <sys/t_lock.h>
  33 #include <sys/time.h>
  34 #include <sys/vnode.h>
  35 #include <sys/vfs.h>
  36 #include <sys/errno.h>
  37 #include <sys/buf.h>
  38 #include <sys/stat.h>
  39 #include <sys/cred.h>
  40 #include <sys/kmem.h>
  41 #include <sys/debug.h>
  42 #include <sys/vmsystm.h>
  43 #include <sys/flock.h>
  44 #include <sys/share.h>
  45 #include <sys/cmn_err.h>
  46 #include <sys/tiuser.h>
  47 #include <sys/sysmacros.h>
  48 #include <sys/callb.h>
  49 #include <sys/acl.h>
  50 #include <sys/kstat.h>
  51 #include <sys/signal.h>
  52 #include <sys/list.h>
  53 #include <sys/zone.h>
  54 
  55 #include <netsmb/smb.h>
  56 #include <netsmb/smb_conn.h>
  57 #include <netsmb/smb_subr.h>
  58 
  59 #include <smbfs/smbfs.h>
  60 #include <smbfs/smbfs_node.h>
  61 #include <smbfs/smbfs_subr.h>
  62 
  63 #include <vm/hat.h>
  64 #include <vm/as.h>
  65 #include <vm/page.h>
  66 #include <vm/pvn.h>
  67 #include <vm/seg.h>
  68 #include <vm/seg_map.h>
  69 #include <vm/seg_vn.h>
  70 
  71 #define ATTRCACHE_VALID(vp)     (gethrtime() < VTOSMB(vp)->r_attrtime)
  72 
  73 static int smbfs_getattr_cache(vnode_t *, smbfattr_t *);
  74 static void smbfattr_to_vattr(vnode_t *, smbfattr_t *, vattr_t *);
  75 static void smbfattr_to_xvattr(smbfattr_t *, vattr_t *);
  76 static int smbfs_getattr_otw(vnode_t *, struct smbfattr *, cred_t *);
  77 
  78 
  79 /*
  80  * The following code provide zone support in order to perform an action
  81  * for each smbfs mount in a zone.  This is also where we would add
  82  * per-zone globals and kernel threads for the smbfs module (since
  83  * they must be terminated by the shutdown callback).
  84  */
  85 
  86 struct smi_globals {
  87         kmutex_t        smg_lock;  /* lock protecting smg_list */
  88         list_t          smg_list;  /* list of SMBFS mounts in zone */
  89         boolean_t       smg_destructor_called;
  90 };
  91 typedef struct smi_globals smi_globals_t;
  92 
  93 static zone_key_t smi_list_key;
  94 
  95 /*
  96  * Attributes caching:
  97  *
  98  * Attributes are cached in the smbnode in struct vattr form.
  99  * There is a time associated with the cached attributes (r_attrtime)
 100  * which tells whether the attributes are valid. The time is initialized
 101  * to the difference between current time and the modify time of the vnode
 102  * when new attributes are cached. This allows the attributes for
 103  * files that have changed recently to be timed out sooner than for files
 104  * that have not changed for a long time. There are minimum and maximum
 105  * timeout values that can be set per mount point.
 106  */
 107 
 108 /*
 109  * Helper for _validate_caches
 110  */
 111 int
 112 smbfs_waitfor_purge_complete(vnode_t *vp)
 113 {
 114         smbnode_t *np;
 115         k_sigset_t smask;
 116 
 117         np = VTOSMB(vp);
 118         if (np->r_serial != NULL && np->r_serial != curthread) {
 119                 mutex_enter(&np->r_statelock);
 120                 sigintr(&smask, VTOSMI(vp)->smi_flags & SMI_INT);
 121                 while (np->r_serial != NULL) {
 122                         if (!cv_wait_sig(&np->r_cv, &np->r_statelock)) {
 123                                 sigunintr(&smask);
 124                                 mutex_exit(&np->r_statelock);
 125                                 return (EINTR);
 126                         }
 127                 }
 128                 sigunintr(&smask);
 129                 mutex_exit(&np->r_statelock);
 130         }
 131         return (0);
 132 }
 133 
 134 /*
 135  * Validate caches by checking cached attributes. If the cached
 136  * attributes have timed out, then get new attributes from the server.
 137  * As a side affect, this will do cache invalidation if the attributes
 138  * have changed.
 139  *
 140  * If the attributes have not timed out and if there is a cache
 141  * invalidation being done by some other thread, then wait until that
 142  * thread has completed the cache invalidation.
 143  */
 144 int
 145 smbfs_validate_caches(
 146         struct vnode *vp,
 147         cred_t *cr)
 148 {
 149         struct smbfattr fa;
 150         int error;
 151 
 152         if (ATTRCACHE_VALID(vp)) {
 153                 error = smbfs_waitfor_purge_complete(vp);
 154                 if (error)
 155                         return (error);
 156                 return (0);
 157         }
 158 
 159         return (smbfs_getattr_otw(vp, &fa, cr));
 160 }
 161 
 162 /*
 163  * Purge all of the various data caches.
 164  *
 165  * Here NFS also had a flags arg to control what gets flushed.
 166  * We only have the page cache, so no flags arg.
 167  */
 168 /* ARGSUSED */
 169 void
 170 smbfs_purge_caches(struct vnode *vp, cred_t *cr)
 171 {
 172 
 173         /*
 174          * Here NFS has: Purge the DNLC for this vp,
 175          * Clear any readdir state bits,
 176          * the readlink response cache, ...
 177          */
 178 
 179         /*
 180          * Flush the page cache.
 181          */
 182         if (vn_has_cached_data(vp)) {
 183                 (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL);
 184         }
 185 
 186         /*
 187          * Here NFS has: Flush the readdir response cache.
 188          * No readdir cache in smbfs.
 189          */
 190 }
 191 
 192 /*
 193  * Here NFS has:
 194  * nfs_purge_rddir_cache()
 195  * nfs3_cache_post_op_attr()
 196  * nfs3_cache_post_op_vattr()
 197  * nfs3_cache_wcc_data()
 198  */
 199 
 200 /*
 201  * Check the attribute cache to see if the new attributes match
 202  * those cached.  If they do, the various `data' caches are
 203  * considered to be good.  Otherwise, purge the cached data.
 204  */
 205 static void
 206 smbfs_cache_check(
 207         struct vnode *vp,
 208         struct smbfattr *fap,
 209         cred_t *cr)
 210 {
 211         smbnode_t *np;
 212         int purge_data = 0;
 213         int purge_acl = 0;
 214 
 215         np = VTOSMB(vp);
 216         mutex_enter(&np->r_statelock);
 217 
 218         /*
 219          * Compare with NFS macro: CACHE_VALID
 220          * If the mtime or size has changed,
 221          * purge cached data.
 222          */
 223         if (np->r_attr.fa_mtime.tv_sec != fap->fa_mtime.tv_sec ||
 224             np->r_attr.fa_mtime.tv_nsec != fap->fa_mtime.tv_nsec)
 225                 purge_data = 1;
 226         if (np->r_attr.fa_size != fap->fa_size)
 227                 purge_data = 1;
 228 
 229         if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec ||
 230             np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec)
 231                 purge_acl = 1;
 232 
 233         if (purge_acl) {
 234                 np->r_sectime = gethrtime();
 235         }
 236 
 237         mutex_exit(&np->r_statelock);
 238 
 239         if (purge_data)
 240                 smbfs_purge_caches(vp, cr);
 241 }
 242 
 243 /*
 244  * Set attributes cache for given vnode using SMB fattr
 245  * and update the attribute cache timeout.
 246  *
 247  * Based on NFS: nfs_attrcache, nfs_attrcache_va
 248  */
 249 void
 250 smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap)
 251 {
 252         smbnode_t *np;
 253         smbmntinfo_t *smi;
 254         hrtime_t delta, now;
 255         u_offset_t newsize;
 256         vtype_t  vtype, oldvt;
 257         mode_t mode;
 258 
 259         np = VTOSMB(vp);
 260         smi = VTOSMI(vp);
 261 
 262         /*
 263          * We allow v_type to change, so set that here
 264          * (and the mode, which depends on the type).
 265          */
 266         if (fap->fa_attr & SMB_FA_DIR) {
 267                 vtype = VDIR;
 268                 mode = smi->smi_dmode;
 269         } else {
 270                 vtype = VREG;
 271                 mode = smi->smi_fmode;
 272         }
 273 
 274         mutex_enter(&np->r_statelock);
 275         now = gethrtime();
 276 
 277         /*
 278          * Delta is the number of nanoseconds that we will
 279          * cache the attributes of the file.  It is based on
 280          * the number of nanoseconds since the last time that
 281          * we detected a change.  The assumption is that files
 282          * that changed recently are likely to change again.
 283          * There is a minimum and a maximum for regular files
 284          * and for directories which is enforced though.
 285          *
 286          * Using the time since last change was detected
 287          * eliminates direct comparison or calculation
 288          * using mixed client and server times.  SMBFS
 289          * does not make any assumptions regarding the
 290          * client and server clocks being synchronized.
 291          */
 292         if (fap->fa_mtime.tv_sec  != np->r_attr.fa_mtime.tv_sec ||
 293             fap->fa_mtime.tv_nsec != np->r_attr.fa_mtime.tv_nsec ||
 294             fap->fa_size       != np->r_attr.fa_size)
 295                 np->r_mtime = now;
 296 
 297         if ((smi->smi_flags & SMI_NOAC) || (vp->v_flag & VNOCACHE))
 298                 delta = 0;
 299         else {
 300                 delta = now - np->r_mtime;
 301                 if (vtype == VDIR) {
 302                         if (delta < smi->smi_acdirmin)
 303                                 delta = smi->smi_acdirmin;
 304                         else if (delta > smi->smi_acdirmax)
 305                                 delta = smi->smi_acdirmax;
 306                 } else {
 307                         if (delta < smi->smi_acregmin)
 308                                 delta = smi->smi_acregmin;
 309                         else if (delta > smi->smi_acregmax)
 310                                 delta = smi->smi_acregmax;
 311                 }
 312         }
 313 
 314         np->r_attrtime = now + delta;
 315         np->r_attr = *fap;
 316         np->n_mode = mode;
 317         oldvt = vp->v_type;
 318         vp->v_type = vtype;
 319 
 320         /*
 321          * Shall we update r_size? (local notion of size)
 322          *
 323          * The real criteria for updating r_size should be:
 324          * if the file has grown on the server, or if
 325          * the client has not modified the file.
 326          *
 327          * Also deal with the fact that SMB presents
 328          * directories as having size=0.  Doing that
 329          * here and leaving fa_size as returned OtW
 330          * avoids fixing the size lots of places.
 331          */
 332         newsize = fap->fa_size;
 333         if (vtype == VDIR && newsize < DEV_BSIZE)
 334                 newsize = DEV_BSIZE;
 335 
 336         if (np->r_size != newsize &&
 337             (!vn_has_cached_data(vp) ||
 338             (!(np->r_flags & RDIRTY) && np->r_count == 0))) {
 339                 /* OK to set the size. */
 340                 np->r_size = newsize;
 341         }
 342 
 343         /*
 344          * Here NFS has:
 345          * nfs_setswaplike(vp, va);
 346          * np->r_flags &= ~RWRITEATTR;
 347          * (not needed here)
 348          */
 349 
 350         np->n_flag &= ~NATTRCHANGED;
 351         mutex_exit(&np->r_statelock);
 352 
 353         if (oldvt != vtype) {
 354                 SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype);
 355         }
 356 }
 357 
 358 /*
 359  * Fill in attribute from the cache.
 360  *
 361  * If valid, copy to *fap and return zero,
 362  * otherwise return an error.
 363  *
 364  * From NFS: nfs_getattr_cache()
 365  */
 366 int
 367 smbfs_getattr_cache(vnode_t *vp, struct smbfattr *fap)
 368 {
 369         smbnode_t *np;
 370         int error;
 371 
 372         np = VTOSMB(vp);
 373 
 374         mutex_enter(&np->r_statelock);
 375         if (gethrtime() >= np->r_attrtime) {
 376                 /* cache expired */
 377                 error = ENOENT;
 378         } else {
 379                 /* cache is valid */
 380                 *fap = np->r_attr;
 381                 error = 0;
 382         }
 383         mutex_exit(&np->r_statelock);
 384 
 385         return (error);
 386 }
 387 
 388 /*
 389  * Get attributes over-the-wire and update attributes cache
 390  * if no error occurred in the over-the-wire operation.
 391  * Return 0 if successful, otherwise error.
 392  * From NFS: nfs_getattr_otw
 393  */
 394 static int
 395 smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr)
 396 {
 397         struct smbnode *np;
 398         struct smb_cred scred;
 399         int error;
 400 
 401         np = VTOSMB(vp);
 402 
 403         /*
 404          * Here NFS uses the ACL RPC (if smi_flags & SMI_ACL)
 405          * With SMB, getting the ACL is a significantly more
 406          * expensive operation, so we do that only when asked
 407          * for the uid/gid.  See smbfsgetattr().
 408          */
 409 
 410         /* Shared lock for (possible) n_fid use. */
 411         if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
 412                 return (EINTR);
 413         smb_credinit(&scred, cr);
 414 
 415         bzero(fap, sizeof (*fap));
 416         error = smbfs_smb_getfattr(np, fap, &scred);
 417 
 418         smb_credrele(&scred);
 419         smbfs_rw_exit(&np->r_lkserlock);
 420 
 421         if (error) {
 422                 /* Here NFS has: PURGE_STALE_FH(error, vp, cr) */
 423                 smbfs_attrcache_remove(np);
 424                 if (error == ENOENT || error == ENOTDIR) {
 425                         /*
 426                          * Getattr failed because the object was
 427                          * removed or renamed by another client.
 428                          * Remove any cached attributes under it.
 429                          */
 430                         smbfs_attrcache_prune(np);
 431                 }
 432                 return (error);
 433         }
 434 
 435         /*
 436          * Here NFS has: nfs_cache_fattr(vap, fa, vap, t, cr);
 437          * which did: fattr_to_vattr, nfs_attr_cache.
 438          * We cache the fattr form, so just do the
 439          * cache check and store the attributes.
 440          */
 441         smbfs_cache_check(vp, fap, cr);
 442         smbfs_attrcache_fa(vp, fap);
 443 
 444         return (0);
 445 }
 446 
 447 /*
 448  * Return either cached or remote attributes. If we get remote attrs,
 449  * use them to check and invalidate caches, then cache the new attributes.
 450  *
 451  * From NFS: nfsgetattr()
 452  */
 453 int
 454 smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr)
 455 {
 456         struct smbfattr fa;
 457         smbmntinfo_t *smi;
 458         uint_t mask;
 459         int error;
 460 
 461         smi = VTOSMI(vp);
 462 
 463         ASSERT(curproc->p_zone == smi->smi_zone_ref.zref_zone);
 464 
 465         /*
 466          * If asked for UID or GID, update n_uid, n_gid.
 467          */
 468         mask = AT_ALL;
 469         if (vap->va_mask & (AT_UID | AT_GID)) {
 470                 if (smi->smi_flags & SMI_ACL)
 471                         (void) smbfs_acl_getids(vp, cr);
 472                 /* else leave as set in make_smbnode */
 473         } else {
 474                 mask &= ~(AT_UID | AT_GID);
 475         }
 476 
 477         /*
 478          * If we've got cached attributes, just use them;
 479          * otherwise go to the server to get attributes,
 480          * which will update the cache in the process.
 481          */
 482         error = smbfs_getattr_cache(vp, &fa);
 483         if (error)
 484                 error = smbfs_getattr_otw(vp, &fa, cr);
 485         if (error)
 486                 return (error);
 487         vap->va_mask |= mask;
 488 
 489         /*
 490          * Re. client's view of the file size, see:
 491          * smbfs_attrcache_fa, smbfs_getattr_otw
 492          */
 493         smbfattr_to_vattr(vp, &fa, vap);
 494         if (vap->va_mask & AT_XVATTR)
 495                 smbfattr_to_xvattr(&fa, vap);
 496 
 497         return (0);
 498 }
 499 
 500 
 501 /*
 502  * Convert SMB over the wire attributes to vnode form.
 503  * Returns 0 for success, error if failed (overflow, etc).
 504  * From NFS: nattr_to_vattr()
 505  */
 506 void
 507 smbfattr_to_vattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap)
 508 {
 509         struct smbnode *np = VTOSMB(vp);
 510 
 511         /*
 512          * Take type, mode, uid, gid from the smbfs node,
 513          * which has have been updated by _getattr_otw.
 514          */
 515         vap->va_type = vp->v_type;
 516         vap->va_mode = np->n_mode;
 517 
 518         vap->va_uid = np->n_uid;
 519         vap->va_gid = np->n_gid;
 520 
 521         vap->va_fsid = vp->v_vfsp->vfs_dev;
 522         vap->va_nodeid = np->n_ino;
 523         vap->va_nlink = 1;
 524 
 525         /*
 526          * Difference from NFS here:  We cache attributes as
 527          * reported by the server, so r_attr.fa_size is the
 528          * server's idea of the file size.  This is called
 529          * for getattr, so we want to return the client's
 530          * idea of the file size.  NFS deals with that in
 531          * nfsgetattr(), the equivalent of our caller.
 532          */
 533         vap->va_size = np->r_size;
 534 
 535         /*
 536          * Times.  Note, already converted from NT to
 537          * Unix form (in the unmarshalling code).
 538          */
 539         vap->va_atime = fa->fa_atime;
 540         vap->va_mtime = fa->fa_mtime;
 541         vap->va_ctime = fa->fa_ctime;
 542 
 543         /*
 544          * rdev, blksize, seq are made up.
 545          * va_nblocks is 512 byte blocks.
 546          */
 547         vap->va_rdev = vp->v_rdev;
 548         vap->va_blksize = MAXBSIZE;
 549         vap->va_nblocks = (fsblkcnt64_t)btod(np->r_attr.fa_allocsz);
 550         vap->va_seq = 0;
 551 }
 552 
 553 /*
 554  * smbfattr_to_xvattr: like smbfattr_to_vattr but for
 555  * Extensible system attributes (PSARC 2007/315)
 556  */
 557 static void
 558 smbfattr_to_xvattr(struct smbfattr *fa, struct vattr *vap)
 559 {
 560         xvattr_t *xvap = (xvattr_t *)vap;       /* *vap may be xvattr_t */
 561         xoptattr_t *xoap = NULL;
 562 
 563         if ((xoap = xva_getxoptattr(xvap)) == NULL)
 564                 return;
 565 
 566         if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
 567                 xoap->xoa_createtime = fa->fa_createtime;
 568                 XVA_SET_RTN(xvap, XAT_CREATETIME);
 569         }
 570 
 571         if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
 572                 xoap->xoa_archive =
 573                     ((fa->fa_attr & SMB_FA_ARCHIVE) != 0);
 574                 XVA_SET_RTN(xvap, XAT_ARCHIVE);
 575         }
 576 
 577         if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
 578                 xoap->xoa_system =
 579                     ((fa->fa_attr & SMB_FA_SYSTEM) != 0);
 580                 XVA_SET_RTN(xvap, XAT_SYSTEM);
 581         }
 582 
 583         if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
 584                 xoap->xoa_readonly =
 585                     ((fa->fa_attr & SMB_FA_RDONLY) != 0);
 586                 XVA_SET_RTN(xvap, XAT_READONLY);
 587         }
 588 
 589         if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
 590                 xoap->xoa_hidden =
 591                     ((fa->fa_attr & SMB_FA_HIDDEN) != 0);
 592                 XVA_SET_RTN(xvap, XAT_HIDDEN);
 593         }
 594 }
 595 
 596 /*
 597  * Here NFS has:
 598  *      nfs_async_... stuff
 599  * which we're not using (no async I/O), and:
 600  *      writerp(),
 601  *      nfs_putpages()
 602  *      nfs_invalidate_pages()
 603  * which we have in smbfs_vnops.c, and
 604  *      nfs_printfhandle()
 605  *      nfs_write_error()
 606  * not needed here.
 607  */
 608 
 609 /*
 610  * Helper function for smbfs_sync
 611  *
 612  * Walk the per-zone list of smbfs mounts, calling smbfs_rflush
 613  * on each one.  This is a little tricky because we need to exit
 614  * the list mutex before each _rflush call and then try to resume
 615  * where we were in the list after re-entering the mutex.
 616  */
 617 void
 618 smbfs_flushall(cred_t *cr)
 619 {
 620         smi_globals_t *smg;
 621         smbmntinfo_t *tmp_smi, *cur_smi, *next_smi;
 622 
 623         smg = zone_getspecific(smi_list_key, crgetzone(cr));
 624         ASSERT(smg != NULL);
 625 
 626         mutex_enter(&smg->smg_lock);
 627         cur_smi = list_head(&smg->smg_list);
 628         if (cur_smi == NULL) {
 629                 mutex_exit(&smg->smg_lock);
 630                 return;
 631         }
 632         VFS_HOLD(cur_smi->smi_vfsp);
 633         mutex_exit(&smg->smg_lock);
 634 
 635 flush:
 636         smbfs_rflush(cur_smi->smi_vfsp, cr);
 637 
 638         mutex_enter(&smg->smg_lock);
 639         /*
 640          * Resume after cur_smi if that's still on the list,
 641          * otherwise restart at the head.
 642          */
 643         for (tmp_smi = list_head(&smg->smg_list);
 644             tmp_smi != NULL;
 645             tmp_smi = list_next(&smg->smg_list, tmp_smi))
 646                 if (tmp_smi == cur_smi)
 647                         break;
 648         if (tmp_smi != NULL)
 649                 next_smi = list_next(&smg->smg_list, tmp_smi);
 650         else
 651                 next_smi = list_head(&smg->smg_list);
 652 
 653         if (next_smi != NULL)
 654                 VFS_HOLD(next_smi->smi_vfsp);
 655         VFS_RELE(cur_smi->smi_vfsp);
 656 
 657         mutex_exit(&smg->smg_lock);
 658 
 659         if (next_smi != NULL) {
 660                 cur_smi = next_smi;
 661                 goto flush;
 662         }
 663 }
 664 
 665 /*
 666  * SMB Client initialization and cleanup.
 667  * Much of it is per-zone now.
 668  */
 669 
 670 
 671 /* ARGSUSED */
 672 static void *
 673 smbfs_zone_init(zoneid_t zoneid)
 674 {
 675         smi_globals_t *smg;
 676 
 677         smg = kmem_alloc(sizeof (*smg), KM_SLEEP);
 678         mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL);
 679         list_create(&smg->smg_list, sizeof (smbmntinfo_t),
 680             offsetof(smbmntinfo_t, smi_zone_node));
 681         smg->smg_destructor_called = B_FALSE;
 682         return (smg);
 683 }
 684 
 685 /*
 686  * Callback routine to tell all SMBFS mounts in the zone to stop creating new
 687  * threads.  Existing threads should exit.
 688  */
 689 /* ARGSUSED */
 690 static void
 691 smbfs_zone_shutdown(zoneid_t zoneid, void *data)
 692 {
 693         smi_globals_t *smg = data;
 694         smbmntinfo_t *smi;
 695 
 696         ASSERT(smg != NULL);
 697 again:
 698         mutex_enter(&smg->smg_lock);
 699         for (smi = list_head(&smg->smg_list); smi != NULL;
 700             smi = list_next(&smg->smg_list, smi)) {
 701 
 702                 /*
 703                  * If we've done the shutdown work for this FS, skip.
 704                  * Once we go off the end of the list, we're done.
 705                  */
 706                 if (smi->smi_flags & SMI_DEAD)
 707                         continue;
 708 
 709                 /*
 710                  * We will do work, so not done.  Get a hold on the FS.
 711                  */
 712                 VFS_HOLD(smi->smi_vfsp);
 713 
 714                 mutex_enter(&smi->smi_lock);
 715                 smi->smi_flags |= SMI_DEAD;
 716                 mutex_exit(&smi->smi_lock);
 717 
 718                 /*
 719                  * Drop lock and release FS, which may change list, then repeat.
 720                  * We're done when every mi has been done or the list is empty.
 721                  */
 722                 mutex_exit(&smg->smg_lock);
 723                 VFS_RELE(smi->smi_vfsp);
 724                 goto again;
 725         }
 726         mutex_exit(&smg->smg_lock);
 727 }
 728 
 729 static void
 730 smbfs_zone_free_globals(smi_globals_t *smg)
 731 {
 732         list_destroy(&smg->smg_list);    /* makes sure the list is empty */
 733         mutex_destroy(&smg->smg_lock);
 734         kmem_free(smg, sizeof (*smg));
 735 
 736 }
 737 
 738 /* ARGSUSED */
 739 static void
 740 smbfs_zone_destroy(zoneid_t zoneid, void *data)
 741 {
 742         smi_globals_t *smg = data;
 743 
 744         ASSERT(smg != NULL);
 745         mutex_enter(&smg->smg_lock);
 746         if (list_head(&smg->smg_list) != NULL) {
 747                 /* Still waiting for VFS_FREEVFS() */
 748                 smg->smg_destructor_called = B_TRUE;
 749                 mutex_exit(&smg->smg_lock);
 750                 return;
 751         }
 752         smbfs_zone_free_globals(smg);
 753 }
 754 
 755 /*
 756  * Add an SMBFS mount to the per-zone list of SMBFS mounts.
 757  */
 758 void
 759 smbfs_zonelist_add(smbmntinfo_t *smi)
 760 {
 761         smi_globals_t *smg;
 762 
 763         smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone);
 764         mutex_enter(&smg->smg_lock);
 765         list_insert_head(&smg->smg_list, smi);
 766         mutex_exit(&smg->smg_lock);
 767 }
 768 
 769 /*
 770  * Remove an SMBFS mount from the per-zone list of SMBFS mounts.
 771  */
 772 void
 773 smbfs_zonelist_remove(smbmntinfo_t *smi)
 774 {
 775         smi_globals_t *smg;
 776 
 777         smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone);
 778         mutex_enter(&smg->smg_lock);
 779         list_remove(&smg->smg_list, smi);
 780         /*
 781          * We can be called asynchronously by VFS_FREEVFS() after the zone
 782          * shutdown/destroy callbacks have executed; if so, clean up the zone's
 783          * smi_globals.
 784          */
 785         if (list_head(&smg->smg_list) == NULL &&
 786             smg->smg_destructor_called == B_TRUE) {
 787                 smbfs_zone_free_globals(smg);
 788                 return;
 789         }
 790         mutex_exit(&smg->smg_lock);
 791 }
 792 
 793 #ifdef  lint
 794 #define NEED_SMBFS_CALLBACKS    1
 795 #endif
 796 
 797 #ifdef NEED_SMBFS_CALLBACKS
 798 /*
 799  * Call-back hooks for netsmb, in case we want them.
 800  * Apple's VFS wants them.  We may not need them.
 801  */
 802 /*ARGSUSED*/
 803 static void smbfs_dead(smb_share_t *ssp)
 804 {
 805         /*
 806          * Walk the mount list, finding all mounts
 807          * using this share...
 808          */
 809 }
 810 
 811 /*ARGSUSED*/
 812 static void smbfs_cb_nop(smb_share_t *ss)
 813 {
 814         /* no-op */
 815 }
 816 
 817 smb_fscb_t smbfs_cb = {
 818         .fscb_disconn   = smbfs_dead,
 819         .fscb_connect   = smbfs_cb_nop,
 820         .fscb_down      = smbfs_cb_nop,
 821         .fscb_up        = smbfs_cb_nop };
 822 
 823 #endif /* NEED_SMBFS_CALLBACKS */
 824 
 825 /*
 826  * SMBFS Client initialization routine.  This routine should only be called
 827  * once.  It performs the following tasks:
 828  *      - Initalize all global locks
 829  *      - Call sub-initialization routines (localize access to variables)
 830  */
 831 int
 832 smbfs_clntinit(void)
 833 {
 834 
 835         zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown,
 836             smbfs_zone_destroy);
 837 #ifdef NEED_SMBFS_CALLBACKS
 838         (void) smb_fscb_set(&smbfs_cb);
 839 #endif /* NEED_SMBFS_CALLBACKS */
 840         return (0);
 841 }
 842 
 843 /*
 844  * This routine is called when the modunload is called. This will cleanup
 845  * the previously allocated/initialized nodes.
 846  */
 847 void
 848 smbfs_clntfini(void)
 849 {
 850 #ifdef NEED_SMBFS_CALLBACKS
 851         (void) smb_fscb_set(NULL);
 852 #endif /* NEED_SMBFS_CALLBACKS */
 853         (void) zone_key_delete(smi_list_key);
 854 }