xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c (revision 30165b7f6753bc3d48c52319bed7ec7b3ea36b3c)
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 }
855