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(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(&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(struct smbfattr *fa, struct vattr *vap)
516 {
517 	xvattr_t *xvap = (xvattr_t *)vap;	/* *vap may be xvattr_t */
518 	xoptattr_t *xoap = NULL;
519 
520 	if ((xoap = xva_getxoptattr(xvap)) == NULL)
521 		return;
522 
523 	if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
524 		xoap->xoa_createtime = fa->fa_createtime;
525 		XVA_SET_RTN(xvap, XAT_CREATETIME);
526 	}
527 
528 	if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
529 		xoap->xoa_archive =
530 		    ((fa->fa_attr & SMB_FA_ARCHIVE) != 0);
531 		XVA_SET_RTN(xvap, XAT_ARCHIVE);
532 	}
533 
534 	if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
535 		xoap->xoa_system =
536 		    ((fa->fa_attr & SMB_FA_SYSTEM) != 0);
537 		XVA_SET_RTN(xvap, XAT_SYSTEM);
538 	}
539 
540 	if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
541 		xoap->xoa_readonly =
542 		    ((fa->fa_attr & SMB_FA_RDONLY) != 0);
543 		XVA_SET_RTN(xvap, XAT_READONLY);
544 	}
545 
546 	if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
547 		xoap->xoa_hidden =
548 		    ((fa->fa_attr & SMB_FA_HIDDEN) != 0);
549 		XVA_SET_RTN(xvap, XAT_HIDDEN);
550 	}
551 }
552 
553 /*
554  * SMB Client initialization and cleanup.
555  * Much of it is per-zone now.
556  */
557 
558 
559 /* ARGSUSED */
560 static void *
561 smbfs_zone_init(zoneid_t zoneid)
562 {
563 	smi_globals_t *smg;
564 
565 	smg = kmem_alloc(sizeof (*smg), KM_SLEEP);
566 	mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL);
567 	list_create(&smg->smg_list, sizeof (smbmntinfo_t),
568 	    offsetof(smbmntinfo_t, smi_zone_node));
569 	smg->smg_destructor_called = B_FALSE;
570 	return (smg);
571 }
572 
573 /*
574  * Callback routine to tell all SMBFS mounts in the zone to stop creating new
575  * threads.  Existing threads should exit.
576  */
577 /* ARGSUSED */
578 static void
579 smbfs_zone_shutdown(zoneid_t zoneid, void *data)
580 {
581 	smi_globals_t *smg = data;
582 	smbmntinfo_t *smi;
583 
584 	ASSERT(smg != NULL);
585 again:
586 	mutex_enter(&smg->smg_lock);
587 	for (smi = list_head(&smg->smg_list); smi != NULL;
588 	    smi = list_next(&smg->smg_list, smi)) {
589 
590 		/*
591 		 * If we've done the shutdown work for this FS, skip.
592 		 * Once we go off the end of the list, we're done.
593 		 */
594 		if (smi->smi_flags & SMI_DEAD)
595 			continue;
596 
597 		/*
598 		 * We will do work, so not done.  Get a hold on the FS.
599 		 */
600 		VFS_HOLD(smi->smi_vfsp);
601 
602 		mutex_enter(&smi->smi_lock);
603 		smi->smi_flags |= SMI_DEAD;
604 		mutex_exit(&smi->smi_lock);
605 
606 		/*
607 		 * Drop lock and release FS, which may change list, then repeat.
608 		 * We're done when every mi has been done or the list is empty.
609 		 */
610 		mutex_exit(&smg->smg_lock);
611 		VFS_RELE(smi->smi_vfsp);
612 		goto again;
613 	}
614 	mutex_exit(&smg->smg_lock);
615 }
616 
617 static void
618 smbfs_zone_free_globals(smi_globals_t *smg)
619 {
620 	list_destroy(&smg->smg_list);	/* makes sure the list is empty */
621 	mutex_destroy(&smg->smg_lock);
622 	kmem_free(smg, sizeof (*smg));
623 
624 }
625 
626 /* ARGSUSED */
627 static void
628 smbfs_zone_destroy(zoneid_t zoneid, void *data)
629 {
630 	smi_globals_t *smg = data;
631 
632 	ASSERT(smg != NULL);
633 	mutex_enter(&smg->smg_lock);
634 	if (list_head(&smg->smg_list) != NULL) {
635 		/* Still waiting for VFS_FREEVFS() */
636 		smg->smg_destructor_called = B_TRUE;
637 		mutex_exit(&smg->smg_lock);
638 		return;
639 	}
640 	smbfs_zone_free_globals(smg);
641 }
642 
643 /*
644  * Add an SMBFS mount to the per-zone list of SMBFS mounts.
645  */
646 void
647 smbfs_zonelist_add(smbmntinfo_t *smi)
648 {
649 	smi_globals_t *smg;
650 
651 	smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone);
652 	mutex_enter(&smg->smg_lock);
653 	list_insert_head(&smg->smg_list, smi);
654 	mutex_exit(&smg->smg_lock);
655 }
656 
657 /*
658  * Remove an SMBFS mount from the per-zone list of SMBFS mounts.
659  */
660 void
661 smbfs_zonelist_remove(smbmntinfo_t *smi)
662 {
663 	smi_globals_t *smg;
664 
665 	smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone);
666 	mutex_enter(&smg->smg_lock);
667 	list_remove(&smg->smg_list, smi);
668 	/*
669 	 * We can be called asynchronously by VFS_FREEVFS() after the zone
670 	 * shutdown/destroy callbacks have executed; if so, clean up the zone's
671 	 * smi_globals.
672 	 */
673 	if (list_head(&smg->smg_list) == NULL &&
674 	    smg->smg_destructor_called == B_TRUE) {
675 		smbfs_zone_free_globals(smg);
676 		return;
677 	}
678 	mutex_exit(&smg->smg_lock);
679 }
680 
681 #ifdef	lint
682 #define	NEED_SMBFS_CALLBACKS	1
683 #endif
684 
685 #ifdef NEED_SMBFS_CALLBACKS
686 /*
687  * Call-back hooks for netsmb, in case we want them.
688  * Apple's VFS wants them.  We may not need them.
689  */
690 /*ARGSUSED*/
691 static void smbfs_dead(smb_share_t *ssp)
692 {
693 	/*
694 	 * Walk the mount list, finding all mounts
695 	 * using this share...
696 	 */
697 }
698 
699 /*ARGSUSED*/
700 static void smbfs_cb_nop(smb_share_t *ss)
701 {
702 	/* no-op */
703 }
704 
705 smb_fscb_t smbfs_cb = {
706 	.fscb_disconn	= smbfs_dead,
707 	.fscb_connect	= smbfs_cb_nop,
708 	.fscb_down	= smbfs_cb_nop,
709 	.fscb_up	= smbfs_cb_nop };
710 
711 #endif /* NEED_SMBFS_CALLBACKS */
712 
713 /*
714  * SMBFS Client initialization routine.  This routine should only be called
715  * once.  It performs the following tasks:
716  *      - Initalize all global locks
717  *      - Call sub-initialization routines (localize access to variables)
718  */
719 int
720 smbfs_clntinit(void)
721 {
722 
723 	zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown,
724 	    smbfs_zone_destroy);
725 #ifdef NEED_SMBFS_CALLBACKS
726 	(void) smb_fscb_set(&smbfs_cb);
727 #endif /* NEED_SMBFS_CALLBACKS */
728 	return (0);
729 }
730 
731 /*
732  * This routine is called when the modunload is called. This will cleanup
733  * the previously allocated/initialized nodes.
734  */
735 void
736 smbfs_clntfini(void)
737 {
738 #ifdef NEED_SMBFS_CALLBACKS
739 	(void) smb_fscb_set(NULL);
740 #endif /* NEED_SMBFS_CALLBACKS */
741 	(void) zone_key_delete(smi_list_key);
742 }
743