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 static int smbfs_getattr_cache(vnode_t *, smbfattr_t *);
  72 static void smbfattr_to_vattr(vnode_t *, smbfattr_t *, vattr_t *);
  73 static void smbfattr_to_xvattr(vnode_t *, smbfattr_t *, vattr_t *);
  74 
  75 /*
  76  * The following code provide zone support in order to perform an action
  77  * for each smbfs mount in a zone.  This is also where we would add
  78  * per-zone globals and kernel threads for the smbfs module (since
  79  * they must be terminated by the shutdown callback).
  80  */
  81 
  82 struct smi_globals {
  83         kmutex_t        smg_lock;  /* lock protecting smg_list */
  84         list_t          smg_list;  /* list of SMBFS mounts in zone */
  85         boolean_t       smg_destructor_called;
  86 };
  87 typedef struct smi_globals smi_globals_t;
  88 
  89 static zone_key_t smi_list_key;
  90 
  91 /*
  92  * Attributes caching:
  93  *
  94  * Attributes are cached in the smbnode in struct vattr form.
  95  * There is a time associated with the cached attributes (r_attrtime)
  96  * which tells whether the attributes are valid. The time is initialized
  97  * to the difference between current time and the modify time of the vnode
  98  * when new attributes are cached. This allows the attributes for
  99  * files that have changed recently to be timed out sooner than for files
 100  * that have not changed for a long time. There are minimum and maximum
 101  * timeout values that can be set per mount point.
 102  */
 103 
 104 /*
 105  * Validate caches by checking cached attributes. If they have timed out
 106  * get the attributes from the server and compare mtimes. If mtimes are
 107  * different purge all caches for this vnode.
 108  */
 109 int
 110 smbfs_validate_caches(
 111         struct vnode *vp,
 112         cred_t *cr)
 113 {
 114         struct vattr va;
 115 
 116         va.va_mask = AT_SIZE;
 117         return (smbfsgetattr(vp, &va, cr));
 118 }
 119 
 120 /*
 121  * Purge all of the various data caches.
 122  */
 123 /*ARGSUSED*/
 124 void
 125 smbfs_purge_caches(struct vnode *vp)
 126 {
 127 #if 0   /* not yet: mmap support */
 128         /*
 129          * NFS: Purge the DNLC for this vp,
 130          * Clear any readdir state bits,
 131          * the readlink response cache, ...
 132          */
 133         smbnode_t *np = VTOSMB(vp);
 134 
 135         /*
 136          * Flush the page cache.
 137          */
 138         if (vn_has_cached_data(vp)) {
 139                 (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL);
 140         }
 141 #endif  /* not yet */
 142 }
 143 
 144 /*
 145  * Check the attribute cache to see if the new attributes match
 146  * those cached.  If they do, the various `data' caches are
 147  * considered to be good.  Otherwise, purge the cached data.
 148  */
 149 void
 150 smbfs_cache_check(
 151         struct vnode *vp,
 152         struct smbfattr *fap)
 153 {
 154         smbnode_t *np;
 155         int purge_data = 0;
 156         int purge_acl = 0;
 157 
 158         np = VTOSMB(vp);
 159         mutex_enter(&np->r_statelock);
 160 
 161         /*
 162          * Compare with NFS macro: CACHE_VALID
 163          * If the mtime or size has changed,
 164          * purge cached data.
 165          */
 166         if (np->r_attr.fa_mtime.tv_sec != fap->fa_mtime.tv_sec ||
 167             np->r_attr.fa_mtime.tv_nsec != fap->fa_mtime.tv_nsec)
 168                 purge_data = 1;
 169         if (np->r_attr.fa_size != fap->fa_size)
 170                 purge_data = 1;
 171 
 172         if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec ||
 173             np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec)
 174                 purge_acl = 1;
 175 
 176         if (purge_acl) {
 177                 /* just invalidate r_secattr (XXX: OK?) */
 178                 np->r_sectime = gethrtime();
 179         }
 180 
 181         mutex_exit(&np->r_statelock);
 182 
 183         if (purge_data)
 184                 smbfs_purge_caches(vp);
 185 }
 186 
 187 /*
 188  * Set attributes cache for given vnode using vnode attributes.
 189  * From NFS: nfs_attrcache_va
 190  */
 191 #if 0   /* not yet (not sure if we need this) */
 192 void
 193 smbfs_attrcache_va(vnode_t *vp, struct vattr *vap)
 194 {
 195         smbfattr_t fa;
 196         smbnode_t *np;
 197 
 198         vattr_to_fattr(vp, vap, &fa);
 199         smbfs_attrcache_fa(vp, &fa);
 200 }
 201 #endif  /* not yet */
 202 
 203 /*
 204  * Set attributes cache for given vnode using SMB fattr
 205  * and update the attribute cache timeout.
 206  *
 207  * From NFS: nfs_attrcache, nfs_attrcache_va
 208  */
 209 void
 210 smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap)
 211 {
 212         smbnode_t *np;
 213         smbmntinfo_t *smi;
 214         hrtime_t delta, now;
 215         u_offset_t newsize;
 216         vtype_t  vtype, oldvt;
 217         mode_t mode;
 218 
 219         np = VTOSMB(vp);
 220         smi = VTOSMI(vp);
 221 
 222         /*
 223          * We allow v_type to change, so set that here
 224          * (and the mode, which depends on the type).
 225          */
 226         if (fap->fa_attr & SMB_FA_DIR) {
 227                 vtype = VDIR;
 228                 mode = smi->smi_dmode;
 229         } else {
 230                 vtype = VREG;
 231                 mode = smi->smi_fmode;
 232         }
 233 
 234         mutex_enter(&np->r_statelock);
 235         now = gethrtime();
 236 
 237         /*
 238          * Delta is the number of nanoseconds that we will
 239          * cache the attributes of the file.  It is based on
 240          * the number of nanoseconds since the last time that
 241          * we detected a change.  The assumption is that files
 242          * that changed recently are likely to change again.
 243          * There is a minimum and a maximum for regular files
 244          * and for directories which is enforced though.
 245          *
 246          * Using the time since last change was detected
 247          * eliminates direct comparison or calculation
 248          * using mixed client and server times.  SMBFS
 249          * does not make any assumptions regarding the
 250          * client and server clocks being synchronized.
 251          */
 252         if (fap->fa_mtime.tv_sec  != np->r_attr.fa_mtime.tv_sec ||
 253             fap->fa_mtime.tv_nsec != np->r_attr.fa_mtime.tv_nsec ||
 254             fap->fa_size       != np->r_attr.fa_size)
 255                 np->r_mtime = now;
 256 
 257         if ((smi->smi_flags & SMI_NOAC) || (vp->v_flag & VNOCACHE))
 258                 delta = 0;
 259         else {
 260                 delta = now - np->r_mtime;
 261                 if (vtype == VDIR) {
 262                         if (delta < smi->smi_acdirmin)
 263                                 delta = smi->smi_acdirmin;
 264                         else if (delta > smi->smi_acdirmax)
 265                                 delta = smi->smi_acdirmax;
 266                 } else {
 267                         if (delta < smi->smi_acregmin)
 268                                 delta = smi->smi_acregmin;
 269                         else if (delta > smi->smi_acregmax)
 270                                 delta = smi->smi_acregmax;
 271                 }
 272         }
 273 
 274         np->r_attrtime = now + delta;
 275         np->r_attr = *fap;
 276         np->n_mode = mode;
 277         oldvt = vp->v_type;
 278         vp->v_type = vtype;
 279 
 280         /*
 281          * Shall we update r_size? (local notion of size)
 282          *
 283          * The real criteria for updating r_size should be:
 284          * if the file has grown on the server, or if
 285          * the client has not modified the file.
 286          *
 287          * Also deal with the fact that SMB presents
 288          * directories as having size=0.  Doing that
 289          * here and leaving fa_size as returned OtW
 290          * avoids fixing the size lots of places.
 291          */
 292         newsize = fap->fa_size;
 293         if (vtype == VDIR && newsize < DEV_BSIZE)
 294                 newsize = DEV_BSIZE;
 295 
 296         if (np->r_size != newsize) {
 297 #if 0   /* not yet: mmap support */
 298                 if (!vn_has_cached_data(vp) || ...)
 299                         /* XXX: See NFS page cache code. */
 300 #endif  /* not yet */
 301                 /* OK to set the size. */
 302                 np->r_size = newsize;
 303         }
 304 
 305         /* NFS: np->r_flags &= ~RWRITEATTR; */
 306         np->n_flag &= ~NATTRCHANGED;
 307 
 308         mutex_exit(&np->r_statelock);
 309 
 310         if (oldvt != vtype) {
 311                 SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype);
 312         }
 313 }
 314 
 315 /*
 316  * Fill in attribute from the cache.
 317  *
 318  * If valid, copy to *fap and return zero,
 319  * otherwise return an error.
 320  *
 321  * From NFS: nfs_getattr_cache()
 322  */
 323 int
 324 smbfs_getattr_cache(vnode_t *vp, struct smbfattr *fap)
 325 {
 326         smbnode_t *np;
 327         int error;
 328 
 329         np = VTOSMB(vp);
 330 
 331         mutex_enter(&np->r_statelock);
 332         if (gethrtime() >= np->r_attrtime) {
 333                 /* cache expired */
 334                 error = ENOENT;
 335         } else {
 336                 /* cache is valid */
 337                 *fap = np->r_attr;
 338                 error = 0;
 339         }
 340         mutex_exit(&np->r_statelock);
 341 
 342         return (error);
 343 }
 344 
 345 /*
 346  * Get attributes over-the-wire and update attributes cache
 347  * if no error occurred in the over-the-wire operation.
 348  * Return 0 if successful, otherwise error.
 349  * From NFS: nfs_getattr_otw
 350  */
 351 int
 352 smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr)
 353 {
 354         struct smbnode *np;
 355         struct smb_cred scred;
 356         int error;
 357 
 358         np = VTOSMB(vp);
 359 
 360         /*
 361          * NFS uses the ACL rpc here (if smi_flags & SMI_ACL)
 362          * With SMB, getting the ACL is a significantly more
 363          * expensive operation, so we do that only when asked
 364          * for the uid/gid.  See smbfsgetattr().
 365          */
 366 
 367         /* Shared lock for (possible) n_fid use. */
 368         if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
 369                 return (EINTR);
 370         smb_credinit(&scred, cr);
 371 
 372         bzero(fap, sizeof (*fap));
 373         error = smbfs_smb_getfattr(np, fap, &scred);
 374 
 375         smb_credrele(&scred);
 376         smbfs_rw_exit(&np->r_lkserlock);
 377 
 378         if (error) {
 379                 /* NFS had: PURGE_STALE_FH(error, vp, cr) */
 380                 smbfs_attrcache_remove(np);
 381                 if (error == ENOENT || error == ENOTDIR) {
 382                         /*
 383                          * Getattr failed because the object was
 384                          * removed or renamed by another client.
 385                          * Remove any cached attributes under it.
 386                          */
 387                         smbfs_attrcache_prune(np);
 388                 }
 389                 return (error);
 390         }
 391 
 392         /*
 393          * NFS: smbfs_cache_fattr(vap, fa, vap, t, cr);
 394          * which did: fattr_to_vattr, nfs_attr_cache.
 395          * We cache the fattr form, so just do the
 396          * cache check and store the attributes.
 397          */
 398         smbfs_cache_check(vp, fap);
 399         smbfs_attrcache_fa(vp, fap);
 400 
 401         return (0);
 402 }
 403 
 404 /*
 405  * Return either cached or remote attributes. If get remote attr
 406  * use them to check and invalidate caches, then cache the new attributes.
 407  *
 408  * From NFS: nfsgetattr()
 409  */
 410 int
 411 smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr)
 412 {
 413         struct smbfattr fa;
 414         smbmntinfo_t *smi;
 415         uint_t mask;
 416         int error;
 417 
 418         smi = VTOSMI(vp);
 419 
 420         ASSERT(curproc->p_zone == smi->smi_zone_ref.zref_zone);
 421 
 422         /*
 423          * If asked for UID or GID, update n_uid, n_gid.
 424          */
 425         mask = AT_ALL;
 426         if (vap->va_mask & (AT_UID | AT_GID)) {
 427                 if (smi->smi_flags & SMI_ACL)
 428                         (void) smbfs_acl_getids(vp, cr);
 429                 /* else leave as set in make_smbnode */
 430         } else {
 431                 mask &= ~(AT_UID | AT_GID);
 432         }
 433 
 434         /*
 435          * If we've got cached attributes, just use them;
 436          * otherwise go to the server to get attributes,
 437          * which will update the cache in the process.
 438          */
 439         error = smbfs_getattr_cache(vp, &fa);
 440         if (error)
 441                 error = smbfs_getattr_otw(vp, &fa, cr);
 442         if (error)
 443                 return (error);
 444         vap->va_mask |= mask;
 445 
 446         /*
 447          * Re. client's view of the file size, see:
 448          * smbfs_attrcache_fa, smbfs_getattr_otw
 449          */
 450         smbfattr_to_vattr(vp, &fa, vap);
 451         if (vap->va_mask & AT_XVATTR)
 452                 smbfattr_to_xvattr(vp, &fa, vap);
 453                 
 454         return (0);
 455 }
 456 
 457 
 458 /*
 459  * Convert SMB over the wire attributes to vnode form.
 460  * Returns 0 for success, error if failed (overflow, etc).
 461  * From NFS: nattr_to_vattr()
 462  */
 463 void
 464 smbfattr_to_vattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap)
 465 {
 466         struct smbnode *np = VTOSMB(vp);
 467 
 468         /*
 469          * Take type, mode, uid, gid from the smbfs node,
 470          * which has have been updated by _getattr_otw.
 471          */
 472         vap->va_type = vp->v_type;
 473         vap->va_mode = np->n_mode;
 474 
 475         vap->va_uid = np->n_uid;
 476         vap->va_gid = np->n_gid;
 477 
 478         vap->va_fsid = vp->v_vfsp->vfs_dev;
 479         vap->va_nodeid = np->n_ino;
 480         vap->va_nlink = 1;
 481 
 482         /*
 483          * Difference from NFS here:  We cache attributes as
 484          * reported by the server, so r_attr.fa_size is the
 485          * server's idea of the file size.  This is called
 486          * for getattr, so we want to return the client's
 487          * idea of the file size.  NFS deals with that in
 488          * nfsgetattr(), the equivalent of our caller.
 489          */
 490         vap->va_size = np->r_size;
 491 
 492         /*
 493          * Times.  Note, already converted from NT to
 494          * Unix form (in the unmarshalling code).
 495          */
 496         vap->va_atime = fa->fa_atime;
 497         vap->va_mtime = fa->fa_mtime;
 498         vap->va_ctime = fa->fa_ctime;
 499 
 500         /*
 501          * rdev, blksize, seq are made up.
 502          * va_nblocks is 512 byte blocks.
 503          */
 504         vap->va_rdev = vp->v_rdev;
 505         vap->va_blksize = MAXBSIZE;
 506         vap->va_nblocks = (fsblkcnt64_t)btod(np->r_attr.fa_allocsz);
 507         vap->va_seq = 0;
 508 }
 509 
 510 /*
 511  * smbfattr_to_xvattr: like smbfattr_to_vattr but for
 512  * Extensible system attributes (PSARC 2007/315)
 513  */
 514 static void
 515 smbfattr_to_xvattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap)
 516 {
 517         struct smbnode *np = VTOSMB(vp);
 518         xvattr_t *xvap = (xvattr_t *)vap;       /* *vap may be xvattr_t */
 519         xoptattr_t *xoap = NULL;
 520 
 521         if ((xoap = xva_getxoptattr(xvap)) == NULL)
 522                 return;
 523 
 524         if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
 525                 xoap->xoa_createtime = fa->fa_createtime;
 526                 XVA_SET_RTN(xvap, XAT_CREATETIME);
 527         }
 528 
 529         if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
 530                 xoap->xoa_archive =
 531                     ((fa->fa_attr & SMB_FA_ARCHIVE) != 0);
 532                 XVA_SET_RTN(xvap, XAT_ARCHIVE);
 533         }
 534 
 535         if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
 536                 xoap->xoa_system =
 537                     ((fa->fa_attr & SMB_FA_SYSTEM) != 0);
 538                 XVA_SET_RTN(xvap, XAT_SYSTEM);
 539         }
 540 
 541         if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
 542                 xoap->xoa_readonly =
 543                     ((fa->fa_attr & SMB_FA_RDONLY) != 0);
 544                 XVA_SET_RTN(xvap, XAT_READONLY);
 545         }
 546 
 547         if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
 548                 xoap->xoa_hidden =
 549                     ((fa->fa_attr & SMB_FA_HIDDEN) != 0);
 550                 XVA_SET_RTN(xvap, XAT_HIDDEN);
 551         }
 552 }
 553 
 554 /*
 555  * SMB Client initialization and cleanup.
 556  * Much of it is per-zone now.
 557  */
 558 
 559 
 560 /* ARGSUSED */
 561 static void *
 562 smbfs_zone_init(zoneid_t zoneid)
 563 {
 564         smi_globals_t *smg;
 565 
 566         smg = kmem_alloc(sizeof (*smg), KM_SLEEP);
 567         mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL);
 568         list_create(&smg->smg_list, sizeof (smbmntinfo_t),
 569             offsetof(smbmntinfo_t, smi_zone_node));
 570         smg->smg_destructor_called = B_FALSE;
 571         return (smg);
 572 }
 573 
 574 /*
 575  * Callback routine to tell all SMBFS mounts in the zone to stop creating new
 576  * threads.  Existing threads should exit.
 577  */
 578 /* ARGSUSED */
 579 static void
 580 smbfs_zone_shutdown(zoneid_t zoneid, void *data)
 581 {
 582         smi_globals_t *smg = data;
 583         smbmntinfo_t *smi;
 584 
 585         ASSERT(smg != NULL);
 586 again:
 587         mutex_enter(&smg->smg_lock);
 588         for (smi = list_head(&smg->smg_list); smi != NULL;
 589             smi = list_next(&smg->smg_list, smi)) {
 590 
 591                 /*
 592                  * If we've done the shutdown work for this FS, skip.
 593                  * Once we go off the end of the list, we're done.
 594                  */
 595                 if (smi->smi_flags & SMI_DEAD)
 596                         continue;
 597 
 598                 /*
 599                  * We will do work, so not done.  Get a hold on the FS.
 600                  */
 601                 VFS_HOLD(smi->smi_vfsp);
 602 
 603                 mutex_enter(&smi->smi_lock);
 604                 smi->smi_flags |= SMI_DEAD;
 605                 mutex_exit(&smi->smi_lock);
 606 
 607                 /*
 608                  * Drop lock and release FS, which may change list, then repeat.
 609                  * We're done when every mi has been done or the list is empty.
 610                  */
 611                 mutex_exit(&smg->smg_lock);
 612                 VFS_RELE(smi->smi_vfsp);
 613                 goto again;
 614         }
 615         mutex_exit(&smg->smg_lock);
 616 }
 617 
 618 static void
 619 smbfs_zone_free_globals(smi_globals_t *smg)
 620 {
 621         list_destroy(&smg->smg_list);    /* makes sure the list is empty */
 622         mutex_destroy(&smg->smg_lock);
 623         kmem_free(smg, sizeof (*smg));
 624 
 625 }
 626 
 627 /* ARGSUSED */
 628 static void
 629 smbfs_zone_destroy(zoneid_t zoneid, void *data)
 630 {
 631         smi_globals_t *smg = data;
 632 
 633         ASSERT(smg != NULL);
 634         mutex_enter(&smg->smg_lock);
 635         if (list_head(&smg->smg_list) != NULL) {
 636                 /* Still waiting for VFS_FREEVFS() */
 637                 smg->smg_destructor_called = B_TRUE;
 638                 mutex_exit(&smg->smg_lock);
 639                 return;
 640         }
 641         smbfs_zone_free_globals(smg);
 642 }
 643 
 644 /*
 645  * Add an SMBFS mount to the per-zone list of SMBFS mounts.
 646  */
 647 void
 648 smbfs_zonelist_add(smbmntinfo_t *smi)
 649 {
 650         smi_globals_t *smg;
 651 
 652         smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone);
 653         mutex_enter(&smg->smg_lock);
 654         list_insert_head(&smg->smg_list, smi);
 655         mutex_exit(&smg->smg_lock);
 656 }
 657 
 658 /*
 659  * Remove an SMBFS mount from the per-zone list of SMBFS mounts.
 660  */
 661 void
 662 smbfs_zonelist_remove(smbmntinfo_t *smi)
 663 {
 664         smi_globals_t *smg;
 665 
 666         smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone);
 667         mutex_enter(&smg->smg_lock);
 668         list_remove(&smg->smg_list, smi);
 669         /*
 670          * We can be called asynchronously by VFS_FREEVFS() after the zone
 671          * shutdown/destroy callbacks have executed; if so, clean up the zone's
 672          * smi_globals.
 673          */
 674         if (list_head(&smg->smg_list) == NULL &&
 675             smg->smg_destructor_called == B_TRUE) {
 676                 smbfs_zone_free_globals(smg);
 677                 return;
 678         }
 679         mutex_exit(&smg->smg_lock);
 680 }
 681 
 682 #ifdef  lint
 683 #define NEED_SMBFS_CALLBACKS    1
 684 #endif
 685 
 686 #ifdef NEED_SMBFS_CALLBACKS
 687 /*
 688  * Call-back hooks for netsmb, in case we want them.
 689  * Apple's VFS wants them.  We may not need them.
 690  */
 691 /*ARGSUSED*/
 692 static void smbfs_dead(smb_share_t *ssp)
 693 {
 694         /*
 695          * Walk the mount list, finding all mounts
 696          * using this share...
 697          */
 698 }
 699 
 700 /*ARGSUSED*/
 701 static void smbfs_cb_nop(smb_share_t *ss)
 702 {
 703         /* no-op */
 704 }
 705 
 706 smb_fscb_t smbfs_cb = {
 707         .fscb_disconn   = smbfs_dead,
 708         .fscb_connect   = smbfs_cb_nop,
 709         .fscb_down      = smbfs_cb_nop,
 710         .fscb_up        = smbfs_cb_nop };
 711 
 712 #endif /* NEED_SMBFS_CALLBACKS */
 713 
 714 /*
 715  * SMBFS Client initialization routine.  This routine should only be called
 716  * once.  It performs the following tasks:
 717  *      - Initalize all global locks
 718  *      - Call sub-initialization routines (localize access to variables)
 719  */
 720 int
 721 smbfs_clntinit(void)
 722 {
 723 
 724         zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown,
 725             smbfs_zone_destroy);
 726 #ifdef NEED_SMBFS_CALLBACKS
 727         (void) smb_fscb_set(&smbfs_cb);
 728 #endif /* NEED_SMBFS_CALLBACKS */
 729         return (0);
 730 }
 731 
 732 /*
 733  * This routine is called when the modunload is called. This will cleanup
 734  * the previously allocated/initialized nodes.
 735  */
 736 void
 737 smbfs_clntfini(void)
 738 {
 739 #ifdef NEED_SMBFS_CALLBACKS
 740         (void) smb_fscb_set(NULL);
 741 #endif /* NEED_SMBFS_CALLBACKS */
 742         (void) zone_key_delete(smi_list_key);
 743 }