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