1/*
2 * Copyright (c) 2000-2001 Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: smb_conn.c,v 1.27.166.1 2005/05/27 02:35:29 lindak Exp $
33 */
34/*
35 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
36 * Use is subject to license terms.
37 */
38
39/*
40 * Connection engine.
41 */
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/kmem.h>
46#include <sys/proc.h>
47#include <sys/lock.h>
48#include <sys/vnode.h>
49#include <sys/stream.h>
50#include <sys/stropts.h>
51#include <sys/socketvar.h>
52#include <sys/cred.h>
53#include <netinet/in.h>
54#include <inet/ip.h>
55#include <inet/ip6.h>
56#include <sys/cmn_err.h>
57#include <sys/thread.h>
58#include <sys/atomic.h>
59#include <sys/u8_textprep.h>
60
61#include <netsmb/smb_osdep.h>
62
63#include <netsmb/smb.h>
64#include <netsmb/smb_conn.h>
65#include <netsmb/smb_subr.h>
66#include <netsmb/smb_tran.h>
67#include <netsmb/smb_pass.h>
68
69static struct smb_connobj smb_vclist;
70
71void smb_co_init(struct smb_connobj *cp, int level, char *objname);
72void smb_co_done(struct smb_connobj *cp);
73void smb_co_hold(struct smb_connobj *cp);
74void smb_co_rele(struct smb_connobj *cp);
75void smb_co_kill(struct smb_connobj *cp);
76
77static void smb_vc_free(struct smb_connobj *cp);
78static void smb_vc_gone(struct smb_connobj *cp);
79
80static void smb_share_free(struct smb_connobj *cp);
81static void smb_share_gone(struct smb_connobj *cp);
82
83int
84smb_sm_init(void)
85{
86	smb_co_init(&smb_vclist, SMBL_SM, "smbsm");
87	return (0);
88}
89
90int
91smb_sm_idle(void)
92{
93	int error = 0;
94	SMB_CO_LOCK(&smb_vclist);
95	if (smb_vclist.co_usecount > 1) {
96		SMBSDEBUG("%d connections still active\n",
97		    smb_vclist.co_usecount - 1);
98		error = EBUSY;
99	}
100	SMB_CO_UNLOCK(&smb_vclist);
101	return (error);
102}
103
104void
105smb_sm_done(void)
106{
107	/*
108	 * XXX Q4BP why are we not iterating on smb_vclist here?
109	 * Because the caller has just called smb_sm_idle() to
110	 * make sure we have no VCs before calling this.
111	 */
112	smb_co_done(&smb_vclist);
113}
114
115
116
117/*
118 * Common code for connection object
119 */
120/*ARGSUSED*/
121void
122smb_co_init(struct smb_connobj *cp, int level, char *objname)
123{
124
125	mutex_init(&cp->co_lock, objname,  MUTEX_DRIVER, NULL);
126
127	cp->co_level = level;
128	cp->co_usecount = 1;
129	SLIST_INIT(&cp->co_children);
130}
131
132/*
133 * Called just before free of an object
134 * of which smb_connobj is a part, i.e.
135 * _vc_free, _share_free, also sm_done.
136 */
137void
138smb_co_done(struct smb_connobj *cp)
139{
140	ASSERT(SLIST_EMPTY(&cp->co_children));
141	mutex_destroy(&cp->co_lock);
142}
143
144static void
145smb_co_addchild(
146	struct smb_connobj *parent,
147	struct smb_connobj *child)
148{
149
150	/*
151	 * Set the child's pointer to the parent.
152	 * No references yet, so no need to lock.
153	 */
154	ASSERT(child->co_usecount == 1);
155	child->co_parent = parent;
156
157	/*
158	 * Add the child to the parent's list of
159	 * children, and in-line smb_co_hold
160	 */
161	ASSERT(MUTEX_HELD(&parent->co_lock));
162	parent->co_usecount++;
163	SLIST_INSERT_HEAD(&parent->co_children, child, co_next);
164}
165
166void
167smb_co_hold(struct smb_connobj *cp)
168{
169	SMB_CO_LOCK(cp);
170	cp->co_usecount++;
171	SMB_CO_UNLOCK(cp);
172}
173
174/*
175 * Called via smb_vc_rele, smb_share_rele
176 */
177void
178smb_co_rele(struct smb_connobj *co)
179{
180	struct smb_connobj *parent;
181	int old_flags;
182
183	SMB_CO_LOCK(co);
184	if (co->co_usecount > 1) {
185		co->co_usecount--;
186		SMB_CO_UNLOCK(co);
187		return;
188	}
189	ASSERT(co->co_usecount == 1);
190	co->co_usecount = 0;
191
192	/*
193	 * This list of children should be empty now.
194	 * Check this while we're still linked, so
195	 * we have a better chance of debugging.
196	 */
197	ASSERT(SLIST_EMPTY(&co->co_children));
198
199	/*
200	 * OK, this element is going away.
201	 *
202	 * We need to drop the lock on this CO so we can take the
203	 * parent CO lock. The _GONE flag prevents this CO from
204	 * getting new references before we can unlink it from the
205	 * parent list.
206	 *
207	 * The _GONE flag is also used to ensure that the co_gone
208	 * function is called only once.  Note that smb_co_kill may
209	 * do this before we get here.  If we find that the _GONE
210	 * flag was not already set, then call the co_gone hook
211	 * (smb_share_gone, smb_vc_gone) which will disconnect
212	 * the share or the VC, respectively.
213	 *
214	 * Note the old: smb_co_gone(co, scred);
215	 * is now in-line here.
216	 */
217	old_flags = co->co_flags;
218	co->co_flags |= SMBO_GONE;
219	SMB_CO_UNLOCK(co);
220
221	if ((old_flags & SMBO_GONE) == 0 && co->co_gone)
222		co->co_gone(co);
223
224	/*
225	 * If we have a parent (only smb_vclist does not)
226	 * then unlink from parent's list of children.
227	 * We have the only reference to the child.
228	 */
229	parent = co->co_parent;
230	if (parent) {
231		SMB_CO_LOCK(parent);
232		ASSERT(SLIST_FIRST(&parent->co_children));
233		if (SLIST_FIRST(&parent->co_children)) {
234			SLIST_REMOVE(&parent->co_children, co,
235			    smb_connobj, co_next);
236		}
237		SMB_CO_UNLOCK(parent);
238	}
239
240	/*
241	 * Now it's safe to free the CO
242	 */
243	if (co->co_free) {
244		co->co_free(co);
245	}
246
247	/*
248	 * Finally, if the CO had a parent, decrement
249	 * the parent's hold count for the lost child.
250	 */
251	if (parent) {
252		/*
253		 * Recursive call here (easier for debugging).
254		 * Can only go two levels.
255		 */
256		smb_co_rele(parent);
257	}
258}
259
260/*
261 * Do just the first part of what co_gone does,
262 * i.e. tree disconnect, or disconnect a VC.
263 * This is used to forcibly close things.
264 */
265void
266smb_co_kill(struct smb_connobj *co)
267{
268	int old_flags;
269
270	SMB_CO_LOCK(co);
271	old_flags = co->co_flags;
272	co->co_flags |= SMBO_GONE;
273	SMB_CO_UNLOCK(co);
274
275	/*
276	 * Do the same "call only once" logic here as in
277	 * smb_co_rele, though it's probably not possible
278	 * for this to be called after smb_co_rele.
279	 */
280	if ((old_flags & SMBO_GONE) == 0 && co->co_gone)
281		co->co_gone(co);
282
283	/* XXX: Walk list of children and kill those too? */
284}
285
286
287/*
288 * Session objects, which are referred to as "VC" for
289 * "virtual cirtuit". This has nothing to do with the
290 * CIFS notion of a "virtual cirtuit".  See smb_conn.h
291 */
292
293void
294smb_vc_hold(struct smb_vc *vcp)
295{
296	smb_co_hold(VCTOCP(vcp));
297}
298
299void
300smb_vc_rele(struct smb_vc *vcp)
301{
302	smb_co_rele(VCTOCP(vcp));
303}
304
305void
306smb_vc_kill(struct smb_vc *vcp)
307{
308	smb_co_kill(VCTOCP(vcp));
309}
310
311/*
312 * Normally called via smb_vc_rele()
313 * after co_usecount drops to zero.
314 * Also called via: smb_vc_kill()
315 *
316 * Shutdown the VC to this server,
317 * invalidate shares linked with it.
318 */
319/*ARGSUSED*/
320static void
321smb_vc_gone(struct smb_connobj *cp)
322{
323	struct smb_vc *vcp = CPTOVC(cp);
324
325	/*
326	 * Was smb_vc_disconnect(vcp);
327	 */
328	smb_iod_disconnect(vcp);
329}
330
331/*
332 * The VC has no more references.  Free it.
333 * No locks needed here.
334 */
335static void
336smb_vc_free(struct smb_connobj *cp)
337{
338	struct smb_vc *vcp = CPTOVC(cp);
339
340	/*
341	 * The _gone call should have emptied the request list,
342	 * but let's make sure, as requests may have references
343	 * to this VC without taking a hold.  (The hold is the
344	 * responsibility of threads placing requests.)
345	 */
346	ASSERT(vcp->iod_rqlist.tqh_first == NULL);
347
348	if (vcp->vc_tdata)
349		SMB_TRAN_DONE(vcp);
350
351/*
352 * We are not using the iconv routines here. So commenting them for now.
353 * REVISIT.
354 */
355#ifdef NOTYETDEFINED
356	if (vcp->vc_tolower)
357		iconv_close(vcp->vc_tolower);
358	if (vcp->vc_toupper)
359		iconv_close(vcp->vc_toupper);
360	if (vcp->vc_tolocal)
361		iconv_close(vcp->vc_tolocal);
362	if (vcp->vc_toserver)
363		iconv_close(vcp->vc_toserver);
364#endif
365
366	if (vcp->vc_mackey != NULL)
367		kmem_free(vcp->vc_mackey, vcp->vc_mackeylen);
368
369	cv_destroy(&vcp->iod_idle);
370	rw_destroy(&vcp->iod_rqlock);
371	sema_destroy(&vcp->vc_sendlock);
372	cv_destroy(&vcp->vc_statechg);
373	smb_co_done(VCTOCP(vcp));
374	kmem_free(vcp, sizeof (*vcp));
375}
376
377/*ARGSUSED*/
378int
379smb_vc_create(smbioc_ossn_t *ossn, smb_cred_t *scred, smb_vc_t **vcpp)
380{
381	static char objtype[] = "smb_vc";
382	cred_t *cr = scred->scr_cred;
383	struct smb_vc *vcp;
384	int error = 0;
385
386	ASSERT(MUTEX_HELD(&smb_vclist.co_lock));
387
388	vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP);
389
390	smb_co_init(VCTOCP(vcp), SMBL_VC, objtype);
391	vcp->vc_co.co_free = smb_vc_free;
392	vcp->vc_co.co_gone = smb_vc_gone;
393
394	cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL);
395	sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL);
396	rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL);
397	cv_init(&vcp->iod_idle, objtype, CV_DRIVER, NULL);
398
399	/* Expanded TAILQ_HEAD_INITIALIZER */
400	vcp->iod_rqlist.tqh_last = &vcp->iod_rqlist.tqh_first;
401
402	vcp->vc_state = SMBIOD_ST_IDLE;
403
404	/*
405	 * These identify the connection.
406	 */
407	vcp->vc_zoneid = getzoneid();
408	bcopy(ossn, &vcp->vc_ssn, sizeof (*ossn));
409
410	/* This fills in vcp->vc_tdata */
411	vcp->vc_tdesc = &smb_tran_nbtcp_desc;
412	if ((error = SMB_TRAN_CREATE(vcp, cr)) != 0)
413		goto errout;
414
415	/* Success! */
416	smb_co_addchild(&smb_vclist, VCTOCP(vcp));
417	*vcpp = vcp;
418	return (0);
419
420errout:
421	/*
422	 * This will destroy the new vc.
423	 * See: smb_vc_free
424	 */
425	smb_vc_rele(vcp);
426	return (error);
427}
428
429/*
430 * Find or create a VC identified by the info in ossn
431 * and return it with a "hold", but not locked.
432 */
433/*ARGSUSED*/
434int
435smb_vc_findcreate(smbioc_ossn_t *ossn, smb_cred_t *scred, smb_vc_t **vcpp)
436{
437	struct smb_connobj *co;
438	struct smb_vc *vcp;
439	smbioc_ssn_ident_t *vc_id;
440	int error;
441	zoneid_t zoneid = getzoneid();
442
443	*vcpp = vcp = NULL;
444
445	SMB_CO_LOCK(&smb_vclist);
446
447	/* var, head, next_field */
448	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
449		vcp = CPTOVC(co);
450
451		/*
452		 * Some things we can check without
453		 * holding the lock (those that are
454		 * set at creation and never change).
455		 */
456
457		/* VCs in other zones are invisibile. */
458		if (vcp->vc_zoneid != zoneid)
459			continue;
460
461		/* Also segregate by Unix owner. */
462		if (vcp->vc_owner != ossn->ssn_owner)
463			continue;
464
465		/*
466		 * Compare identifying info:
467		 * server address, user, domain
468		 * names are case-insensitive
469		 */
470		vc_id = &vcp->vc_ssn.ssn_id;
471		if (bcmp(&vc_id->id_srvaddr,
472		    &ossn->ssn_id.id_srvaddr,
473		    sizeof (vc_id->id_srvaddr)))
474			continue;
475		if (u8_strcmp(vc_id->id_user, ossn->ssn_id.id_user, 0,
476		    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error))
477			continue;
478		if (u8_strcmp(vc_id->id_domain, ossn->ssn_id.id_domain, 0,
479		    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error))
480			continue;
481
482		/*
483		 * We have a match, but still have to check
484		 * the _GONE flag, and do that with a lock.
485		 * No new references when _GONE is set.
486		 *
487		 * Also clear SMBVOPT_CREATE which the caller
488		 * may check to find out if we did create.
489		 */
490		SMB_VC_LOCK(vcp);
491		if ((vcp->vc_flags & SMBV_GONE) == 0) {
492			ossn->ssn_vopt &= ~SMBVOPT_CREATE;
493			/*
494			 * Return it held, unlocked.
495			 * In-line smb_vc_hold here.
496			 */
497			co->co_usecount++;
498			SMB_VC_UNLOCK(vcp);
499			*vcpp = vcp;
500			error = 0;
501			goto out;
502		}
503		SMB_VC_UNLOCK(vcp);
504		/* keep looking. */
505	}
506	vcp = NULL;
507
508	/* Note: smb_vclist is still locked. */
509
510	if (ossn->ssn_vopt & SMBVOPT_CREATE) {
511		/*
512		 * Create a new VC.  It starts out with
513		 * hold count = 1, so don't incr. here.
514		 */
515		error = smb_vc_create(ossn, scred, &vcp);
516		if (error == 0)
517			*vcpp = vcp;
518	} else
519		error = ENOENT;
520
521out:
522	SMB_CO_UNLOCK(&smb_vclist);
523	return (error);
524}
525
526
527/*
528 * Helper functions that operate on VCs
529 */
530
531/*
532 * Get a pointer to the IP address suitable for passing to Trusted
533 * Extensions find_tpc() routine.  Used by smbfs_mount_label_policy().
534 * Compare this code to nfs_mount_label_policy() if problems arise.
535 */
536void *
537smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers)
538{
539	smbioc_ssn_ident_t *id = &vcp->vc_ssn.ssn_id;
540	void *ret;
541
542	switch (id->id_srvaddr.sa.sa_family) {
543	case AF_INET:
544		*ipvers = IPV4_VERSION;
545		ret = &id->id_srvaddr.sin.sin_addr;
546		break;
547
548	case AF_INET6:
549		*ipvers = IPV6_VERSION;
550		ret = &id->id_srvaddr.sin6.sin6_addr;
551		break;
552	default:
553		SMBSDEBUG("invalid address family %d\n",
554		    id->id_srvaddr.sa.sa_family);
555		*ipvers = 0;
556		ret = NULL;
557		break;
558	}
559	return (ret);
560}
561
562void
563smb_vc_walkshares(struct smb_vc *vcp,
564	walk_share_func_t func)
565{
566	smb_connobj_t *co;
567	smb_share_t *ssp;
568
569	/*
570	 * Walk the share list calling func(ssp, arg)
571	 */
572	SMB_VC_LOCK(vcp);
573	SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
574		ssp = CPTOSS(co);
575		SMB_SS_LOCK(ssp);
576		func(ssp);
577		SMB_SS_UNLOCK(ssp);
578	}
579	SMB_VC_UNLOCK(vcp);
580}
581
582
583/*
584 * Share implementation
585 */
586
587void
588smb_share_hold(struct smb_share *ssp)
589{
590	smb_co_hold(SSTOCP(ssp));
591}
592
593void
594smb_share_rele(struct smb_share *ssp)
595{
596	smb_co_rele(SSTOCP(ssp));
597}
598
599void
600smb_share_kill(struct smb_share *ssp)
601{
602	smb_co_kill(SSTOCP(ssp));
603}
604
605/*
606 * Normally called via smb_share_rele()
607 * after co_usecount drops to zero.
608 * Also called via: smb_share_kill()
609 */
610static void
611smb_share_gone(struct smb_connobj *cp)
612{
613	struct smb_cred scred;
614	struct smb_share *ssp = CPTOSS(cp);
615
616	smb_credinit(&scred, NULL);
617	smb_iod_shutdown_share(ssp);
618	(void) smb_smb_treedisconnect(ssp, &scred);
619	smb_credrele(&scred);
620}
621
622/*
623 * Normally called via smb_share_rele()
624 * after co_usecount drops to zero.
625 */
626static void
627smb_share_free(struct smb_connobj *cp)
628{
629	struct smb_share *ssp = CPTOSS(cp);
630
631	cv_destroy(&ssp->ss_conn_done);
632	smb_co_done(SSTOCP(ssp));
633	kmem_free(ssp, sizeof (*ssp));
634}
635
636/*
637 * Allocate share structure and attach it to the given VC
638 * Connection expected to be locked on entry. Share will be returned
639 * in locked state.
640 */
641/*ARGSUSED*/
642int
643smb_share_create(smbioc_tcon_t *tcon, struct smb_vc *vcp,
644	struct smb_share **sspp, struct smb_cred *scred)
645{
646	static char objtype[] = "smb_ss";
647	struct smb_share *ssp;
648
649	ASSERT(MUTEX_HELD(&vcp->vc_lock));
650
651	ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP);
652	smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype);
653	ssp->ss_co.co_free = smb_share_free;
654	ssp->ss_co.co_gone = smb_share_gone;
655
656	cv_init(&ssp->ss_conn_done, objtype, CV_DRIVER, NULL);
657	ssp->ss_tid = SMB_TID_UNKNOWN;
658
659	bcopy(&tcon->tc_sh, &ssp->ss_ioc,
660	    sizeof (smbioc_oshare_t));
661
662	smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
663	*sspp = ssp;
664
665	return (0);
666}
667
668/*
669 * Find or create a share under the given VC
670 * and return it with a "hold", but not locked.
671 */
672
673int
674smb_share_findcreate(smbioc_tcon_t *tcon, struct smb_vc *vcp,
675	struct smb_share **sspp, struct smb_cred *scred)
676{
677	struct smb_connobj *co;
678	struct smb_share *ssp = NULL;
679	int error = 0;
680
681	*sspp = NULL;
682
683	SMB_VC_LOCK(vcp);
684
685	/* var, head, next_field */
686	SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
687		ssp = CPTOSS(co);
688
689		/* Share name */
690		if (u8_strcmp(ssp->ss_name, tcon->tc_sh.sh_name, 0,
691		    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error))
692			continue;
693
694		/*
695		 * We have a match, but still have to check
696		 * the _GONE flag, and do that with a lock.
697		 * No new references when _GONE is set.
698		 *
699		 * Also clear SMBSOPT_CREATE which the caller
700		 * may check to find out if we did create.
701		 */
702		SMB_SS_LOCK(ssp);
703		if ((ssp->ss_flags & SMBS_GONE) == 0) {
704			tcon->tc_opt &= ~SMBSOPT_CREATE;
705			/*
706			 * Return it held, unlocked.
707			 * In-line smb_share_hold here.
708			 */
709			co->co_usecount++;
710			SMB_SS_UNLOCK(ssp);
711			*sspp = ssp;
712			error = 0;
713			goto out;
714		}
715		SMB_SS_UNLOCK(ssp);
716		/* keep looking. */
717	}
718	ssp = NULL;
719
720	/* Note: vcp (list of shares) is still locked. */
721
722	if (tcon->tc_opt & SMBSOPT_CREATE) {
723		/*
724		 * Create a new share.  It starts out with
725		 * hold count = 1, so don't incr. here.
726		 */
727		error = smb_share_create(tcon, vcp, &ssp, scred);
728		if (error == 0)
729			*sspp = ssp;
730	} else
731		error = ENOENT;
732
733out:
734	SMB_VC_UNLOCK(vcp);
735	return (error);
736}
737
738
739/*
740 * Helper functions that operate on shares
741 */
742
743/*
744 * Mark this share as invalid, so consumers will know
745 * their file handles have become invalid.
746 *
747 * Most share consumers store a copy of ss_vcgenid when
748 * opening a file handle and compare that with what's in
749 * the share before using a file handle.  If the genid
750 * doesn't match, the file handle has become "stale"
751 * due to disconnect.  Therefore, zap ss_vcgenid here.
752 */
753void
754smb_share_invalidate(struct smb_share *ssp)
755{
756
757	ASSERT(MUTEX_HELD(&ssp->ss_lock));
758
759	ssp->ss_flags &= ~SMBS_CONNECTED;
760	ssp->ss_tid = SMB_TID_UNKNOWN;
761	ssp->ss_vcgenid = 0;
762}
763
764/*
765 * Connect (or reconnect) a share object.
766 *
767 * Called by smb_usr_get_tree() for new connections,
768 * and called by smb_rq_enqueue() for reconnect.
769 */
770int
771smb_share_tcon(smb_share_t *ssp, smb_cred_t *scred)
772{
773	clock_t tmo;
774	int error;
775
776	SMB_SS_LOCK(ssp);
777
778	if (ssp->ss_flags & SMBS_CONNECTED) {
779		SMBIODEBUG("alread connected?");
780		error = 0;
781		goto out;
782	}
783
784	/*
785	 * Wait for completion of any state changes
786	 * that might be underway.
787	 */
788	while (ssp->ss_flags & SMBS_RECONNECTING) {
789		ssp->ss_conn_waiters++;
790		tmo = cv_wait_sig(&ssp->ss_conn_done, &ssp->ss_lock);
791		ssp->ss_conn_waiters--;
792		if (tmo == 0) {
793			/* Interrupt! */
794			error = EINTR;
795			goto out;
796		}
797	}
798
799	/* Did someone else do it for us? */
800	if (ssp->ss_flags & SMBS_CONNECTED) {
801		error = 0;
802		goto out;
803	}
804
805	/*
806	 * OK, we'll do the work.
807	 */
808	ssp->ss_flags |= SMBS_RECONNECTING;
809
810	/*
811	 * Drop the lock while doing the TCON.
812	 * On success, sets ss_tid, ss_vcgenid,
813	 * and ss_flags |= SMBS_CONNECTED;
814	 */
815	SMB_SS_UNLOCK(ssp);
816	error = smb_smb_treeconnect(ssp, scred);
817	SMB_SS_LOCK(ssp);
818
819	ssp->ss_flags &= ~SMBS_RECONNECTING;
820
821	/* They can all go ahead! */
822	if (ssp->ss_conn_waiters)
823		cv_broadcast(&ssp->ss_conn_done);
824
825out:
826	SMB_SS_UNLOCK(ssp);
827
828	return (error);
829}
830
831/*
832 * Solaris zones support
833 */
834/*ARGSUSED*/
835void
836lingering_vc(struct smb_vc *vc)
837{
838	/* good place for a breakpoint */
839	DEBUG_ENTER("lingering VC");
840}
841
842/*
843 * On zone shutdown, kill any IOD threads still running in this zone.
844 */
845/* ARGSUSED */
846void
847nsmb_zone_shutdown(zoneid_t zoneid, void *data)
848{
849	struct smb_connobj *co;
850	struct smb_vc *vcp;
851
852	SMB_CO_LOCK(&smb_vclist);
853	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
854		vcp = CPTOVC(co);
855
856		if (vcp->vc_zoneid != zoneid)
857			continue;
858
859		/*
860		 * This will close the connection, and
861		 * cause the IOD thread to terminate.
862		 */
863		smb_vc_kill(vcp);
864	}
865	SMB_CO_UNLOCK(&smb_vclist);
866}
867
868/*
869 * On zone destroy, kill any IOD threads and free all resources they used.
870 */
871/* ARGSUSED */
872void
873nsmb_zone_destroy(zoneid_t zoneid, void *data)
874{
875	struct smb_connobj *co;
876	struct smb_vc *vcp;
877
878	/*
879	 * We will repeat what should have already happened
880	 * in zone_shutdown to make things go away.
881	 *
882	 * There should have been an smb_vc_rele call
883	 * by now for all VCs in the zone.  If not,
884	 * there's probably more we needed to do in
885	 * the shutdown call.
886	 */
887
888	SMB_CO_LOCK(&smb_vclist);
889
890	if (smb_vclist.co_usecount > 1) {
891		SMBERROR("%d connections still active\n",
892		    smb_vclist.co_usecount - 1);
893	}
894
895	/* var, head, next_field */
896	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
897		vcp = CPTOVC(co);
898
899		if (vcp->vc_zoneid != zoneid)
900			continue;
901
902		/* Debugging */
903		lingering_vc(vcp);
904	}
905
906	SMB_CO_UNLOCK(&smb_vclist);
907}
908