1*da6c28aaSamw /*
2*da6c28aaSamw  * CDDL HEADER START
3*da6c28aaSamw  *
4*da6c28aaSamw  * The contents of this file are subject to the terms of the
5*da6c28aaSamw  * Common Development and Distribution License (the "License").
6*da6c28aaSamw  * You may not use this file except in compliance with the License.
7*da6c28aaSamw  *
8*da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10*da6c28aaSamw  * See the License for the specific language governing permissions
11*da6c28aaSamw  * and limitations under the License.
12*da6c28aaSamw  *
13*da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14*da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16*da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17*da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18*da6c28aaSamw  *
19*da6c28aaSamw  * CDDL HEADER END
20*da6c28aaSamw  */
21*da6c28aaSamw /*
22*da6c28aaSamw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*da6c28aaSamw  * Use is subject to license terms.
24*da6c28aaSamw  */
25*da6c28aaSamw 
26*da6c28aaSamw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*da6c28aaSamw 
28*da6c28aaSamw /*
29*da6c28aaSamw  * General Structures Layout
30*da6c28aaSamw  * -------------------------
31*da6c28aaSamw  *
32*da6c28aaSamw  * This is a simplified diagram showing the relationship between most of the
33*da6c28aaSamw  * main structures.
34*da6c28aaSamw  *
35*da6c28aaSamw  * +-------------------+
36*da6c28aaSamw  * |     SMB_INFO      |
37*da6c28aaSamw  * +-------------------+
38*da6c28aaSamw  *          |
39*da6c28aaSamw  *          |
40*da6c28aaSamw  *          v
41*da6c28aaSamw  * +-------------------+       +-------------------+      +-------------------+
42*da6c28aaSamw  * |     SESSION       |<----->|     SESSION       |......|      SESSION      |
43*da6c28aaSamw  * +-------------------+       +-------------------+      +-------------------+
44*da6c28aaSamw  *          |
45*da6c28aaSamw  *          |
46*da6c28aaSamw  *          v
47*da6c28aaSamw  * +-------------------+       +-------------------+      +-------------------+
48*da6c28aaSamw  * |       USER        |<----->|       USER        |......|       USER        |
49*da6c28aaSamw  * +-------------------+       +-------------------+      +-------------------+
50*da6c28aaSamw  *          |
51*da6c28aaSamw  *          |
52*da6c28aaSamw  *          v
53*da6c28aaSamw  * +-------------------+       +-------------------+      +-------------------+
54*da6c28aaSamw  * |       TREE        |<----->|       TREE        |......|       TREE        |
55*da6c28aaSamw  * +-------------------+       +-------------------+      +-------------------+
56*da6c28aaSamw  *      |         |
57*da6c28aaSamw  *      |         |
58*da6c28aaSamw  *      |         v
59*da6c28aaSamw  *      |     +-------+       +-------+      +-------+
60*da6c28aaSamw  *      |     | OFILE |<----->| OFILE |......| OFILE |
61*da6c28aaSamw  *      |     +-------+       +-------+      +-------+
62*da6c28aaSamw  *      |
63*da6c28aaSamw  *      |
64*da6c28aaSamw  *      v
65*da6c28aaSamw  *  +-------+       +------+      +------+
66*da6c28aaSamw  *  | ODIR  |<----->| ODIR |......| ODIR |
67*da6c28aaSamw  *  +-------+       +------+      +------+
68*da6c28aaSamw  *
69*da6c28aaSamw  *
70*da6c28aaSamw  * Tree State Machine
71*da6c28aaSamw  * ------------------
72*da6c28aaSamw  *
73*da6c28aaSamw  *    +-----------------------------+	 T0
74*da6c28aaSamw  *    |  SMB_TREE_STATE_CONNECTED   |<----------- Creation/Allocation
75*da6c28aaSamw  *    +-----------------------------+
76*da6c28aaSamw  *		    |
77*da6c28aaSamw  *		    | T1
78*da6c28aaSamw  *		    |
79*da6c28aaSamw  *		    v
80*da6c28aaSamw  *    +------------------------------+
81*da6c28aaSamw  *    | SMB_TREE_STATE_DISCONNECTING |
82*da6c28aaSamw  *    +------------------------------+
83*da6c28aaSamw  *		    |
84*da6c28aaSamw  *		    | T2
85*da6c28aaSamw  *		    |
86*da6c28aaSamw  *		    v
87*da6c28aaSamw  *    +-----------------------------+    T3
88*da6c28aaSamw  *    | SMB_TREE_STATE_DISCONNECTED |----------> Deletion/Free
89*da6c28aaSamw  *    +-----------------------------+
90*da6c28aaSamw  *
91*da6c28aaSamw  * SMB_TREE_STATE_CONNECTED
92*da6c28aaSamw  *
93*da6c28aaSamw  *    While in this state:
94*da6c28aaSamw  *      - The tree is queued in the list of trees of its user.
95*da6c28aaSamw  *      - References will be given out if the tree is looked up.
96*da6c28aaSamw  *      - Files under that tree can be accessed.
97*da6c28aaSamw  *
98*da6c28aaSamw  * SMB_TREE_STATE_DISCONNECTING
99*da6c28aaSamw  *
100*da6c28aaSamw  *    While in this state:
101*da6c28aaSamw  *      - The tree is queued in the list of trees of its user.
102*da6c28aaSamw  *      - References will not be given out if the tree is looked up.
103*da6c28aaSamw  *      - The files and directories open under the tree are being closed.
104*da6c28aaSamw  *      - The resources associated with the tree remain.
105*da6c28aaSamw  *
106*da6c28aaSamw  * SMB_TREE_STATE_DISCONNECTED
107*da6c28aaSamw  *
108*da6c28aaSamw  *    While in this state:
109*da6c28aaSamw  *      - The tree is queued in the list of trees of its user.
110*da6c28aaSamw  *      - References will not be given out if the tree is looked up.
111*da6c28aaSamw  *      - The tree has no more files and directories opened.
112*da6c28aaSamw  *      - The resources associated with the tree remain.
113*da6c28aaSamw  *
114*da6c28aaSamw  * Transition T0
115*da6c28aaSamw  *
116*da6c28aaSamw  *    This transition occurs in smb_tree_connect(). A new tree is created and
117*da6c28aaSamw  *    added to the list of trees of a user.
118*da6c28aaSamw  *
119*da6c28aaSamw  * Transition T1
120*da6c28aaSamw  *
121*da6c28aaSamw  *    This transition occurs in smb_tree_disconnect().
122*da6c28aaSamw  *
123*da6c28aaSamw  * Transition T2
124*da6c28aaSamw  *
125*da6c28aaSamw  *    This transition occurs in smb_tree_release(). The resources associated
126*da6c28aaSamw  *    with the tree are freed as well as the tree structure. For the transition
127*da6c28aaSamw  *    to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and
128*da6c28aaSamw  *    the reference count be zero.
129*da6c28aaSamw  *
130*da6c28aaSamw  * Comments
131*da6c28aaSamw  * --------
132*da6c28aaSamw  *
133*da6c28aaSamw  *    The state machine of the tree structures is controlled by 3 elements:
134*da6c28aaSamw  *      - The list of trees of the user it belongs to.
135*da6c28aaSamw  *      - The mutex embedded in the structure itself.
136*da6c28aaSamw  *      - The reference count.
137*da6c28aaSamw  *
138*da6c28aaSamw  *    There's a mutex embedded in the tree structure used to protect its fields
139*da6c28aaSamw  *    and there's a lock embedded in the list of trees of a user. To
140*da6c28aaSamw  *    increment or to decrement the reference count the mutex must be entered.
141*da6c28aaSamw  *    To insert the tree into the list of trees of the user and to remove
142*da6c28aaSamw  *    the tree from it, the lock must be entered in RW_WRITER mode.
143*da6c28aaSamw  *
144*da6c28aaSamw  *    Rules of access to a tree structure:
145*da6c28aaSamw  *
146*da6c28aaSamw  *    1) In order to avoid deadlocks, when both (mutex and lock of the user
147*da6c28aaSamw  *       list) have to be entered, the lock must be entered first.
148*da6c28aaSamw  *
149*da6c28aaSamw  *    2) All actions applied to a tree require a reference count.
150*da6c28aaSamw  *
151*da6c28aaSamw  *    3) There are 2 ways of getting a reference count. One is when the tree
152*da6c28aaSamw  *       is connected. The other when the user is looked up. This translates
153*da6c28aaSamw  *       into 2 functions: smb_tree_connect() and smb_tree_lookup_by_tid().
154*da6c28aaSamw  *
155*da6c28aaSamw  *    It should be noted that the reference count of a tree registers the
156*da6c28aaSamw  *    number of references to the tree in other structures (such as an smb
157*da6c28aaSamw  *    request). The reference count is not incremented in these 2 instances:
158*da6c28aaSamw  *
159*da6c28aaSamw  *    1) The tree is connected. An tree is anchored by his state. If there's
160*da6c28aaSamw  *       no activity involving a tree currently connected, the reference
161*da6c28aaSamw  *       count of that tree is zero.
162*da6c28aaSamw  *
163*da6c28aaSamw  *    2) The tree is queued in the list of trees of the user. The fact of
164*da6c28aaSamw  *       being queued in that list is NOT registered by incrementing the
165*da6c28aaSamw  *       reference count.
166*da6c28aaSamw  */
167*da6c28aaSamw #include <smbsrv/smb_incl.h>
168*da6c28aaSamw #include <smbsrv/smb_fsops.h>
169*da6c28aaSamw 
170*da6c28aaSamw /* Static functions defined further down this file. */
171*da6c28aaSamw static void smb_tree_delete(smb_tree_t *);
172*da6c28aaSamw static smb_tree_t *smb_tree_lookup_head(smb_llist_t *);
173*da6c28aaSamw static smb_tree_t *smb_tree_lookup_next(smb_llist_t *, smb_tree_t *);
174*da6c28aaSamw 
175*da6c28aaSamw /*
176*da6c28aaSamw  * smb_tree_connect
177*da6c28aaSamw  */
178*da6c28aaSamw smb_tree_t *
179*da6c28aaSamw smb_tree_connect(
180*da6c28aaSamw     smb_user_t		*user,
181*da6c28aaSamw     uint16_t		access_flags,
182*da6c28aaSamw     char		*sharename,
183*da6c28aaSamw     char		*resource,
184*da6c28aaSamw     int32_t		stype,
185*da6c28aaSamw     smb_node_t		*snode,
186*da6c28aaSamw     fsvol_attr_t	*vol_attr)
187*da6c28aaSamw {
188*da6c28aaSamw 	smb_tree_t	*tree;
189*da6c28aaSamw 	uint16_t	tid;
190*da6c28aaSamw 
191*da6c28aaSamw 	if (smb_idpool_alloc(&user->u_tid_pool, &tid)) {
192*da6c28aaSamw 		return (NULL);
193*da6c28aaSamw 	}
194*da6c28aaSamw 
195*da6c28aaSamw 	tree = kmem_cache_alloc(smb_info.si_cache_tree, KM_SLEEP);
196*da6c28aaSamw 	bzero(tree, sizeof (smb_tree_t));
197*da6c28aaSamw 
198*da6c28aaSamw 	if (smb_idpool_constructor(&tree->t_fid_pool)) {
199*da6c28aaSamw 		smb_idpool_free(&user->u_tid_pool, tid);
200*da6c28aaSamw 		kmem_cache_free(smb_info.si_cache_tree, tree);
201*da6c28aaSamw 		return (NULL);
202*da6c28aaSamw 	}
203*da6c28aaSamw 
204*da6c28aaSamw 	if (smb_idpool_constructor(&tree->t_sid_pool)) {
205*da6c28aaSamw 		smb_idpool_destructor(&tree->t_fid_pool);
206*da6c28aaSamw 		smb_idpool_free(&user->u_tid_pool, tid);
207*da6c28aaSamw 		kmem_cache_free(smb_info.si_cache_tree, tree);
208*da6c28aaSamw 		return (NULL);
209*da6c28aaSamw 	}
210*da6c28aaSamw 
211*da6c28aaSamw 	smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t),
212*da6c28aaSamw 	    offsetof(smb_ofile_t, f_lnd));
213*da6c28aaSamw 
214*da6c28aaSamw 	smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t),
215*da6c28aaSamw 	    offsetof(smb_odir_t, d_lnd));
216*da6c28aaSamw 
217*da6c28aaSamw 	(void) strlcpy(tree->t_sharename, sharename,
218*da6c28aaSamw 	    sizeof (tree->t_sharename));
219*da6c28aaSamw 	(void) strlcpy(tree->t_resource, resource, sizeof (tree->t_resource));
220*da6c28aaSamw 
221*da6c28aaSamw 	mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL);
222*da6c28aaSamw 
223*da6c28aaSamw 	tree->t_user = user;
224*da6c28aaSamw 	tree->t_session = user->u_session;
225*da6c28aaSamw 	tree->t_refcnt = 1;
226*da6c28aaSamw 	tree->t_tid = tid;
227*da6c28aaSamw 	tree->t_access = access_flags;
228*da6c28aaSamw 	tree->t_res_type = stype;
229*da6c28aaSamw 	tree->t_snode = snode;
230*da6c28aaSamw 	tree->t_state = SMB_TREE_STATE_CONNECTED;
231*da6c28aaSamw 	tree->t_magic = SMB_TREE_MAGIC;
232*da6c28aaSamw 
233*da6c28aaSamw 	switch (stype & STYPE_MASK) {
234*da6c28aaSamw 	case STYPE_DISKTREE:
235*da6c28aaSamw 		tree->t_fsd = snode->tree_fsd;
236*da6c28aaSamw 
237*da6c28aaSamw 		(void) strlcpy(tree->t_typename, vol_attr->fs_typename,
238*da6c28aaSamw 		    SMB_TREE_TYPENAME_SZ);
239*da6c28aaSamw 		(void) utf8_strupr((char *)tree->t_typename);
240*da6c28aaSamw 
241*da6c28aaSamw 		if (vol_attr->flags & FSOLF_READONLY)
242*da6c28aaSamw 			tree->t_access = SMB_TREE_READ_ONLY;
243*da6c28aaSamw 
244*da6c28aaSamw 		tree->t_acltype = smb_fsop_acltype(snode);
245*da6c28aaSamw 
246*da6c28aaSamw 		if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_ACLONCREATE)) {
247*da6c28aaSamw 			tree->t_flags |= SMB_TREE_FLAG_ACLONCREATE;
248*da6c28aaSamw 		}
249*da6c28aaSamw 
250*da6c28aaSamw 		if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_ACEMASKONACCESS)) {
251*da6c28aaSamw 			tree->t_flags |= SMB_TREE_FLAG_ACEMASKONACCESS;
252*da6c28aaSamw 		}
253*da6c28aaSamw 
254*da6c28aaSamw 		if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_CASEINSENSITIVE)) {
255*da6c28aaSamw 			tree->t_flags |= SMB_TREE_FLAG_IGNORE_CASE;
256*da6c28aaSamw 		}
257*da6c28aaSamw 		break;
258*da6c28aaSamw 
259*da6c28aaSamw 	case STYPE_IPC:
260*da6c28aaSamw 	default:
261*da6c28aaSamw 		tree->t_typename[0] = '\0';
262*da6c28aaSamw 		break;
263*da6c28aaSamw 	}
264*da6c28aaSamw 
265*da6c28aaSamw 	smb_llist_enter(&user->u_tree_list, RW_WRITER);
266*da6c28aaSamw 	smb_llist_insert_head(&user->u_tree_list, tree);
267*da6c28aaSamw 	smb_llist_exit(&user->u_tree_list);
268*da6c28aaSamw 	atomic_inc_32(&user->u_session->s_tree_cnt);
269*da6c28aaSamw 	atomic_inc_32(&smb_info.open_trees);
270*da6c28aaSamw 
271*da6c28aaSamw 	return (tree);
272*da6c28aaSamw }
273*da6c28aaSamw 
274*da6c28aaSamw /*
275*da6c28aaSamw  * smb_tree_disconnect
276*da6c28aaSamw  *
277*da6c28aaSamw  *
278*da6c28aaSamw  */
279*da6c28aaSamw void
280*da6c28aaSamw smb_tree_disconnect(
281*da6c28aaSamw     smb_tree_t	*tree)
282*da6c28aaSamw {
283*da6c28aaSamw 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
284*da6c28aaSamw 
285*da6c28aaSamw 	mutex_enter(&tree->t_mutex);
286*da6c28aaSamw 	ASSERT(tree->t_refcnt);
287*da6c28aaSamw 	switch (tree->t_state) {
288*da6c28aaSamw 	case SMB_TREE_STATE_CONNECTED: {
289*da6c28aaSamw 		/*
290*da6c28aaSamw 		 * The tree is moved into a state indicating that the disconnect
291*da6c28aaSamw 		 * process has started.
292*da6c28aaSamw 		 */
293*da6c28aaSamw 		tree->t_state = SMB_TREE_STATE_DISCONNECTING;
294*da6c28aaSamw 		mutex_exit(&tree->t_mutex);
295*da6c28aaSamw 		atomic_dec_32(&smb_info.open_trees);
296*da6c28aaSamw 		/*
297*da6c28aaSamw 		 * The files opened under this tree are closed.
298*da6c28aaSamw 		 */
299*da6c28aaSamw 		smb_ofile_close_all(tree);
300*da6c28aaSamw 		/*
301*da6c28aaSamw 		 * The directories opened under this tree are closed.
302*da6c28aaSamw 		 */
303*da6c28aaSamw 		smb_odir_close_all(tree);
304*da6c28aaSamw 		mutex_enter(&tree->t_mutex);
305*da6c28aaSamw 		tree->t_state = SMB_TREE_STATE_DISCONNECTED;
306*da6c28aaSamw 		/*FALLTHRU*/
307*da6c28aaSamw 	}
308*da6c28aaSamw 	case SMB_TREE_STATE_DISCONNECTED:
309*da6c28aaSamw 	case SMB_TREE_STATE_DISCONNECTING:
310*da6c28aaSamw 		break;
311*da6c28aaSamw 
312*da6c28aaSamw 	default:
313*da6c28aaSamw 		ASSERT(0);
314*da6c28aaSamw 		break;
315*da6c28aaSamw 	}
316*da6c28aaSamw 	mutex_exit(&tree->t_mutex);
317*da6c28aaSamw }
318*da6c28aaSamw 
319*da6c28aaSamw /*
320*da6c28aaSamw  * smb_tree_disconnect_all
321*da6c28aaSamw  *
322*da6c28aaSamw  *
323*da6c28aaSamw  */
324*da6c28aaSamw void
325*da6c28aaSamw smb_tree_disconnect_all(
326*da6c28aaSamw     smb_user_t		*user)
327*da6c28aaSamw {
328*da6c28aaSamw 	smb_tree_t	*tree;
329*da6c28aaSamw 
330*da6c28aaSamw 	ASSERT(user);
331*da6c28aaSamw 	ASSERT(user->u_magic == SMB_USER_MAGIC);
332*da6c28aaSamw 
333*da6c28aaSamw 	tree = smb_tree_lookup_head(&user->u_tree_list);
334*da6c28aaSamw 	while (tree) {
335*da6c28aaSamw 		ASSERT(tree->t_user == user);
336*da6c28aaSamw 		smb_tree_disconnect(tree);
337*da6c28aaSamw 		smb_tree_release(tree);
338*da6c28aaSamw 		tree = smb_tree_lookup_head(&user->u_tree_list);
339*da6c28aaSamw 	}
340*da6c28aaSamw }
341*da6c28aaSamw 
342*da6c28aaSamw /*
343*da6c28aaSamw  * smb_tree_close_all_by_pid
344*da6c28aaSamw  *
345*da6c28aaSamw  *
346*da6c28aaSamw  */
347*da6c28aaSamw void
348*da6c28aaSamw smb_tree_close_all_by_pid(
349*da6c28aaSamw     smb_user_t		*user,
350*da6c28aaSamw     uint16_t		pid)
351*da6c28aaSamw {
352*da6c28aaSamw 	smb_tree_t	*tree;
353*da6c28aaSamw 
354*da6c28aaSamw 	ASSERT(user);
355*da6c28aaSamw 	ASSERT(user->u_magic == SMB_USER_MAGIC);
356*da6c28aaSamw 
357*da6c28aaSamw 	tree = smb_tree_lookup_head(&user->u_tree_list);
358*da6c28aaSamw 	while (tree) {
359*da6c28aaSamw 		smb_tree_t	*next;
360*da6c28aaSamw 		ASSERT(tree->t_user == user);
361*da6c28aaSamw 		smb_ofile_close_all_by_pid(tree, pid);
362*da6c28aaSamw 		smb_odir_close_all_by_pid(tree, pid);
363*da6c28aaSamw 		next = smb_tree_lookup_next(&user->u_tree_list, tree);
364*da6c28aaSamw 		smb_tree_release(tree);
365*da6c28aaSamw 		tree = next;
366*da6c28aaSamw 	}
367*da6c28aaSamw }
368*da6c28aaSamw 
369*da6c28aaSamw /*
370*da6c28aaSamw  * smb_tree_release
371*da6c28aaSamw  *
372*da6c28aaSamw  *
373*da6c28aaSamw  */
374*da6c28aaSamw void
375*da6c28aaSamw smb_tree_release(
376*da6c28aaSamw     smb_tree_t		*tree)
377*da6c28aaSamw {
378*da6c28aaSamw 	ASSERT(tree);
379*da6c28aaSamw 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
380*da6c28aaSamw 
381*da6c28aaSamw 	mutex_enter(&tree->t_mutex);
382*da6c28aaSamw 	ASSERT(tree->t_refcnt);
383*da6c28aaSamw 	tree->t_refcnt--;
384*da6c28aaSamw 	switch (tree->t_state) {
385*da6c28aaSamw 	case SMB_TREE_STATE_DISCONNECTED:
386*da6c28aaSamw 		if (tree->t_refcnt == 0) {
387*da6c28aaSamw 			mutex_exit(&tree->t_mutex);
388*da6c28aaSamw 			smb_tree_delete(tree);
389*da6c28aaSamw 			return;
390*da6c28aaSamw 		}
391*da6c28aaSamw 		break;
392*da6c28aaSamw 
393*da6c28aaSamw 	case SMB_TREE_STATE_CONNECTED:
394*da6c28aaSamw 	case SMB_TREE_STATE_DISCONNECTING:
395*da6c28aaSamw 		break;
396*da6c28aaSamw 
397*da6c28aaSamw 	default:
398*da6c28aaSamw 		ASSERT(0);
399*da6c28aaSamw 		break;
400*da6c28aaSamw 	}
401*da6c28aaSamw 	mutex_exit(&tree->t_mutex);
402*da6c28aaSamw }
403*da6c28aaSamw 
404*da6c28aaSamw /*
405*da6c28aaSamw  * Find the appropriate tree for this request. The request credentials
406*da6c28aaSamw  * set here override those set during uid lookup. In domain mode, the
407*da6c28aaSamw  * user and tree credentials should be the same. In share mode, the
408*da6c28aaSamw  * tree credentials (defined in the share definition) should override
409*da6c28aaSamw  * the user credentials.
410*da6c28aaSamw  */
411*da6c28aaSamw smb_tree_t *
412*da6c28aaSamw smb_tree_lookup_by_tid(
413*da6c28aaSamw     smb_user_t		*user,
414*da6c28aaSamw     uint16_t		tid)
415*da6c28aaSamw {
416*da6c28aaSamw 	smb_tree_t	*tree;
417*da6c28aaSamw 
418*da6c28aaSamw 	ASSERT(user);
419*da6c28aaSamw 	ASSERT(user->u_magic == SMB_USER_MAGIC);
420*da6c28aaSamw 
421*da6c28aaSamw 	smb_llist_enter(&user->u_tree_list, RW_READER);
422*da6c28aaSamw 	tree = smb_llist_head(&user->u_tree_list);
423*da6c28aaSamw 	while (tree) {
424*da6c28aaSamw 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
425*da6c28aaSamw 		ASSERT(tree->t_user == user);
426*da6c28aaSamw 		if (tree->t_tid == tid) {
427*da6c28aaSamw 			mutex_enter(&tree->t_mutex);
428*da6c28aaSamw 			switch (tree->t_state) {
429*da6c28aaSamw 			case SMB_TREE_STATE_CONNECTED:
430*da6c28aaSamw 				/* The tree exists and is still connected. */
431*da6c28aaSamw 				tree->t_refcnt++;
432*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
433*da6c28aaSamw 				smb_llist_exit(&user->u_tree_list);
434*da6c28aaSamw 				return (tree);
435*da6c28aaSamw 			case SMB_TREE_STATE_DISCONNECTING:
436*da6c28aaSamw 			case SMB_TREE_STATE_DISCONNECTED:
437*da6c28aaSamw 				/*
438*da6c28aaSamw 				 * The tree exists but is diconnected or is in
439*da6c28aaSamw 				 * the process of being destroyed.
440*da6c28aaSamw 				 */
441*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
442*da6c28aaSamw 				smb_llist_exit(&user->u_tree_list);
443*da6c28aaSamw 				return (NULL);
444*da6c28aaSamw 			default:
445*da6c28aaSamw 				ASSERT(0);
446*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
447*da6c28aaSamw 				smb_llist_exit(&user->u_tree_list);
448*da6c28aaSamw 				return (NULL);
449*da6c28aaSamw 			}
450*da6c28aaSamw 		}
451*da6c28aaSamw 		tree = smb_llist_next(&user->u_tree_list, tree);
452*da6c28aaSamw 	}
453*da6c28aaSamw 	smb_llist_exit(&user->u_tree_list);
454*da6c28aaSamw 	return (NULL);
455*da6c28aaSamw }
456*da6c28aaSamw 
457*da6c28aaSamw /*
458*da6c28aaSamw  * smb_tree_lookup_first_by_name
459*da6c28aaSamw  *
460*da6c28aaSamw  * This function returns the first tree in the connected state that matches the
461*da6c28aaSamw  * sharename passed in. If the tree provided is NULL the search starts from
462*da6c28aaSamw  * the beginning of the list of trees of the user. It a tree is provided the
463*da6c28aaSamw  * search starts just after that tree.
464*da6c28aaSamw  */
465*da6c28aaSamw smb_tree_t *
466*da6c28aaSamw smb_tree_lookup_by_name(
467*da6c28aaSamw     smb_user_t		*user,
468*da6c28aaSamw     char		*sharename,
469*da6c28aaSamw     smb_tree_t		*tree)
470*da6c28aaSamw {
471*da6c28aaSamw 	ASSERT(user);
472*da6c28aaSamw 	ASSERT(user->u_magic == SMB_USER_MAGIC);
473*da6c28aaSamw 	ASSERT(sharename);
474*da6c28aaSamw 
475*da6c28aaSamw 	smb_llist_enter(&user->u_tree_list, RW_READER);
476*da6c28aaSamw 
477*da6c28aaSamw 	if (tree) {
478*da6c28aaSamw 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
479*da6c28aaSamw 		ASSERT(tree->t_user == user);
480*da6c28aaSamw 		tree = smb_llist_next(&user->u_tree_list, tree);
481*da6c28aaSamw 	} else {
482*da6c28aaSamw 		tree = smb_llist_head(&user->u_tree_list);
483*da6c28aaSamw 	}
484*da6c28aaSamw 
485*da6c28aaSamw 	while (tree) {
486*da6c28aaSamw 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
487*da6c28aaSamw 		ASSERT(tree->t_user == user);
488*da6c28aaSamw 		if (strcmp(tree->t_sharename, sharename) == 0) {
489*da6c28aaSamw 			mutex_enter(&tree->t_mutex);
490*da6c28aaSamw 			switch (tree->t_state) {
491*da6c28aaSamw 			case SMB_TREE_STATE_CONNECTED:
492*da6c28aaSamw 				/* The tree exists and is still connected. */
493*da6c28aaSamw 				tree->t_refcnt++;
494*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
495*da6c28aaSamw 				smb_llist_exit(&user->u_tree_list);
496*da6c28aaSamw 				return (tree);
497*da6c28aaSamw 			case SMB_TREE_STATE_DISCONNECTING:
498*da6c28aaSamw 			case SMB_TREE_STATE_DISCONNECTED:
499*da6c28aaSamw 				/*
500*da6c28aaSamw 				 * The tree exists but is diconnected or is in
501*da6c28aaSamw 				 * the process of being destroyed.
502*da6c28aaSamw 				 */
503*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
504*da6c28aaSamw 				break;
505*da6c28aaSamw 			default:
506*da6c28aaSamw 				ASSERT(0);
507*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
508*da6c28aaSamw 				break;
509*da6c28aaSamw 			}
510*da6c28aaSamw 		}
511*da6c28aaSamw 		tree = smb_llist_next(&user->u_tree_list, tree);
512*da6c28aaSamw 	}
513*da6c28aaSamw 	smb_llist_exit(&user->u_tree_list);
514*da6c28aaSamw 	return (NULL);
515*da6c28aaSamw }
516*da6c28aaSamw 
517*da6c28aaSamw /*
518*da6c28aaSamw  * smb_tree_lookup_first_by_fsd
519*da6c28aaSamw  *
520*da6c28aaSamw  * This function returns the first tree in the connected state that matches the
521*da6c28aaSamw  * fsd passed in. If the tree provided is NULL the search starts from
522*da6c28aaSamw  * the beginning of the list of trees of the user. It a tree is provided the
523*da6c28aaSamw  * search starts just after that tree.
524*da6c28aaSamw  */
525*da6c28aaSamw smb_tree_t *
526*da6c28aaSamw smb_tree_lookup_by_fsd(
527*da6c28aaSamw     smb_user_t		*user,
528*da6c28aaSamw     fs_desc_t		*fsd,
529*da6c28aaSamw     smb_tree_t		*tree)
530*da6c28aaSamw {
531*da6c28aaSamw 	ASSERT(user);
532*da6c28aaSamw 	ASSERT(user->u_magic == SMB_USER_MAGIC);
533*da6c28aaSamw 	ASSERT(fsd);
534*da6c28aaSamw 
535*da6c28aaSamw 	smb_llist_enter(&user->u_tree_list, RW_READER);
536*da6c28aaSamw 
537*da6c28aaSamw 	if (tree) {
538*da6c28aaSamw 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
539*da6c28aaSamw 		ASSERT(tree->t_user == user);
540*da6c28aaSamw 		tree = smb_llist_next(&user->u_tree_list, tree);
541*da6c28aaSamw 	} else {
542*da6c28aaSamw 		tree = smb_llist_head(&user->u_tree_list);
543*da6c28aaSamw 	}
544*da6c28aaSamw 
545*da6c28aaSamw 	while (tree) {
546*da6c28aaSamw 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
547*da6c28aaSamw 		ASSERT(tree->t_user == user);
548*da6c28aaSamw 		if (fsd_cmp(&tree->t_fsd, fsd) == 0) {
549*da6c28aaSamw 			mutex_enter(&tree->t_mutex);
550*da6c28aaSamw 			switch (tree->t_state) {
551*da6c28aaSamw 			case SMB_TREE_STATE_CONNECTED:
552*da6c28aaSamw 				/* The tree exists and is still connected. */
553*da6c28aaSamw 				tree->t_refcnt++;
554*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
555*da6c28aaSamw 				smb_llist_exit(&user->u_tree_list);
556*da6c28aaSamw 				return (tree);
557*da6c28aaSamw 			case SMB_TREE_STATE_DISCONNECTING:
558*da6c28aaSamw 			case SMB_TREE_STATE_DISCONNECTED:
559*da6c28aaSamw 				/*
560*da6c28aaSamw 				 * The tree exists but is diconnected or is in
561*da6c28aaSamw 				 * the process of being destroyed.
562*da6c28aaSamw 				 */
563*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
564*da6c28aaSamw 				break;
565*da6c28aaSamw 			default:
566*da6c28aaSamw 				ASSERT(0);
567*da6c28aaSamw 				mutex_exit(&tree->t_mutex);
568*da6c28aaSamw 				break;
569*da6c28aaSamw 			}
570*da6c28aaSamw 		}
571*da6c28aaSamw 		tree = smb_llist_next(&user->u_tree_list, tree);
572*da6c28aaSamw 	}
573*da6c28aaSamw 	smb_llist_exit(&user->u_tree_list);
574*da6c28aaSamw 	return (NULL);
575*da6c28aaSamw }
576*da6c28aaSamw 
577*da6c28aaSamw /* *************************** Static Functions ***************************** */
578*da6c28aaSamw 
579*da6c28aaSamw /*
580*da6c28aaSamw  * smb_tree_delete
581*da6c28aaSamw  *
582*da6c28aaSamw  * This function releases all the resources associated with a tree. It also
583*da6c28aaSamw  * removes the tree the caller passes from the list of trees of the user.
584*da6c28aaSamw  *
585*da6c28aaSamw  * The tree to destroy must be in the "destroying state" and the reference count
586*da6c28aaSamw  * must be zero. This function assumes it's single threaded i.e. only one
587*da6c28aaSamw  * thread will attempt to destroy a specific tree (this condition should be met
588*da6c28aaSamw  * if the tree is is the "destroying state" and has a reference count of zero).
589*da6c28aaSamw  *
590*da6c28aaSamw  * Entry:
591*da6c28aaSamw  *	tree	Tree to destroy
592*da6c28aaSamw  *
593*da6c28aaSamw  * Exit:
594*da6c28aaSamw  *	Nothing
595*da6c28aaSamw  *
596*da6c28aaSamw  * Return:
597*da6c28aaSamw  *	Nothing
598*da6c28aaSamw  */
599*da6c28aaSamw static void
600*da6c28aaSamw smb_tree_delete(smb_tree_t *tree)
601*da6c28aaSamw {
602*da6c28aaSamw 	ASSERT(tree);
603*da6c28aaSamw 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
604*da6c28aaSamw 	ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
605*da6c28aaSamw 	ASSERT(tree->t_refcnt == 0);
606*da6c28aaSamw 
607*da6c28aaSamw 	/*
608*da6c28aaSamw 	 * Let's remove the tree from the list of trees of the
609*da6c28aaSamw 	 * user. This has to be done before any resources
610*da6c28aaSamw 	 * associated with the tree are released.
611*da6c28aaSamw 	 */
612*da6c28aaSamw 	smb_llist_enter(&tree->t_user->u_tree_list, RW_WRITER);
613*da6c28aaSamw 	smb_llist_remove(&tree->t_user->u_tree_list, tree);
614*da6c28aaSamw 	smb_llist_exit(&tree->t_user->u_tree_list);
615*da6c28aaSamw 
616*da6c28aaSamw 	tree->t_magic = (uint32_t)~SMB_TREE_MAGIC;
617*da6c28aaSamw 	smb_idpool_free(&tree->t_user->u_tid_pool, tree->t_tid);
618*da6c28aaSamw 	atomic_dec_32(&tree->t_session->s_tree_cnt);
619*da6c28aaSamw 
620*da6c28aaSamw 	if (tree->t_snode) {
621*da6c28aaSamw 		smb_node_release(tree->t_snode);
622*da6c28aaSamw 	}
623*da6c28aaSamw 	mutex_destroy(&tree->t_mutex);
624*da6c28aaSamw 	/*
625*da6c28aaSamw 	 * The list of open files and open directories should be empty.
626*da6c28aaSamw 	 */
627*da6c28aaSamw 	smb_llist_destructor(&tree->t_ofile_list);
628*da6c28aaSamw 	smb_llist_destructor(&tree->t_odir_list);
629*da6c28aaSamw 	smb_idpool_destructor(&tree->t_fid_pool);
630*da6c28aaSamw 	smb_idpool_destructor(&tree->t_sid_pool);
631*da6c28aaSamw 	kmem_cache_free(smb_info.si_cache_tree, tree);
632*da6c28aaSamw }
633*da6c28aaSamw 
634*da6c28aaSamw /*
635*da6c28aaSamw  * smb_tree_lookup_head
636*da6c28aaSamw  *
637*da6c28aaSamw  * This function returns the first tree in the list that is in the
638*da6c28aaSamw  * SMB_TREE_STATE_CONNECTED. A reference is taken on the tree and
639*da6c28aaSamw  * smb_tree_release() will have to be called for the tree returned.
640*da6c28aaSamw  *
641*da6c28aaSamw  * Entry:
642*da6c28aaSamw  *	lst	List of trees (usually the list of trees of a user)
643*da6c28aaSamw  *
644*da6c28aaSamw  * Exit:
645*da6c28aaSamw  *	Nothing
646*da6c28aaSamw  *
647*da6c28aaSamw  * Return:
648*da6c28aaSamw  *	NULL	No tree in the SMB_TREE_STATE_CONNECTED state was found.
649*da6c28aaSamw  *	!NULL	First tree in the list in the SMB_TREE_STATE_CONNECTED state.
650*da6c28aaSamw  */
651*da6c28aaSamw static smb_tree_t *
652*da6c28aaSamw smb_tree_lookup_head(
653*da6c28aaSamw     smb_llist_t		*lst)
654*da6c28aaSamw {
655*da6c28aaSamw 	smb_tree_t	*tree;
656*da6c28aaSamw 
657*da6c28aaSamw 	smb_llist_enter(lst, RW_READER);
658*da6c28aaSamw 	tree = smb_llist_head(lst);
659*da6c28aaSamw 	while (tree) {
660*da6c28aaSamw 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
661*da6c28aaSamw 		mutex_enter(&tree->t_mutex);
662*da6c28aaSamw 		if (tree->t_state == SMB_TREE_STATE_CONNECTED) {
663*da6c28aaSamw 			tree->t_refcnt++;
664*da6c28aaSamw 			mutex_exit(&tree->t_mutex);
665*da6c28aaSamw 			break;
666*da6c28aaSamw 		} else if ((tree->t_state == SMB_TREE_STATE_DISCONNECTING) ||
667*da6c28aaSamw 		    (tree->t_state == SMB_TREE_STATE_DISCONNECTED)) {
668*da6c28aaSamw 			mutex_exit(&tree->t_mutex);
669*da6c28aaSamw 			tree = smb_llist_next(lst, tree);
670*da6c28aaSamw 		} else {
671*da6c28aaSamw 			ASSERT(0);
672*da6c28aaSamw 			mutex_exit(&tree->t_mutex);
673*da6c28aaSamw 			tree = smb_llist_next(lst, tree);
674*da6c28aaSamw 		}
675*da6c28aaSamw 	}
676*da6c28aaSamw 	smb_llist_exit(lst);
677*da6c28aaSamw 
678*da6c28aaSamw 	return (tree);
679*da6c28aaSamw }
680*da6c28aaSamw 
681*da6c28aaSamw /*
682*da6c28aaSamw  * smb_tree_lookup_next
683*da6c28aaSamw  *
684*da6c28aaSamw  * This function returns the next tree in the list that is in the
685*da6c28aaSamw  * SMB_TREE_STATE_CONNECTED. A reference is taken on the tree and
686*da6c28aaSamw  * smb_tree_release() will have to be called for the tree returned.
687*da6c28aaSamw  *
688*da6c28aaSamw  * Entry:
689*da6c28aaSamw  *	lst	List of trees (usually the list of trees of a user).
690*da6c28aaSamw  *	tree	Starting tree.
691*da6c28aaSamw  *
692*da6c28aaSamw  * Exit:
693*da6c28aaSamw  *	Nothing
694*da6c28aaSamw  *
695*da6c28aaSamw  * Return:
696*da6c28aaSamw  *	NULL	No tree in the SMB_TREE_STATE_CONNECTED state was found.
697*da6c28aaSamw  *	!NULL	Next tree in the list in the SMB_TREE_STATE_CONNECTED state.
698*da6c28aaSamw  */
699*da6c28aaSamw static smb_tree_t *
700*da6c28aaSamw smb_tree_lookup_next(
701*da6c28aaSamw     smb_llist_t		*lst,
702*da6c28aaSamw     smb_tree_t		*tree)
703*da6c28aaSamw {
704*da6c28aaSamw 	smb_tree_t	*next;
705*da6c28aaSamw 
706*da6c28aaSamw 	ASSERT(lst);
707*da6c28aaSamw 	ASSERT(tree);
708*da6c28aaSamw 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
709*da6c28aaSamw 	ASSERT(tree->t_refcnt);
710*da6c28aaSamw 
711*da6c28aaSamw 	smb_llist_enter(lst, RW_READER);
712*da6c28aaSamw 	next = smb_llist_next(lst, tree);
713*da6c28aaSamw 	while (next) {
714*da6c28aaSamw 		ASSERT(next->t_magic == SMB_TREE_MAGIC);
715*da6c28aaSamw 		mutex_enter(&next->t_mutex);
716*da6c28aaSamw 		if (next->t_state == SMB_TREE_STATE_CONNECTED) {
717*da6c28aaSamw 			next->t_refcnt++;
718*da6c28aaSamw 			mutex_exit(&next->t_mutex);
719*da6c28aaSamw 			break;
720*da6c28aaSamw 		} else if ((next->t_state == SMB_TREE_STATE_DISCONNECTING) ||
721*da6c28aaSamw 		    (next->t_state == SMB_TREE_STATE_DISCONNECTED)) {
722*da6c28aaSamw 			mutex_exit(&next->t_mutex);
723*da6c28aaSamw 			next = smb_llist_next(lst, next);
724*da6c28aaSamw 		} else {
725*da6c28aaSamw 			ASSERT(0);
726*da6c28aaSamw 			mutex_exit(&next->t_mutex);
727*da6c28aaSamw 			next = smb_llist_next(lst, next);
728*da6c28aaSamw 		}
729*da6c28aaSamw 	}
730*da6c28aaSamw 	smb_llist_exit(lst);
731*da6c28aaSamw 
732*da6c28aaSamw 	return (next);
733*da6c28aaSamw }
734