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