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