xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_tree.c (revision a194faf8907a6722dcf10ad16c6ca72c9b7bd0ba)
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 2007 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(smb_info.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(smb_info.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(smb_info.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_refcnt = 1;
227 	tree->t_tid = tid;
228 	tree->t_access = access_flags;
229 	tree->t_res_type = stype;
230 	tree->t_snode = snode;
231 	tree->t_state = SMB_TREE_STATE_CONNECTED;
232 	tree->t_magic = SMB_TREE_MAGIC;
233 
234 	switch (stype & STYPE_MASK) {
235 	case STYPE_DISKTREE:
236 		tree->t_fsd = snode->tree_fsd;
237 
238 		(void) strlcpy(tree->t_typename, vol_attr->fs_typename,
239 		    SMB_TREE_TYPENAME_SZ);
240 		(void) utf8_strupr((char *)tree->t_typename);
241 
242 		if (vol_attr->flags & FSOLF_READONLY)
243 			tree->t_access = SMB_TREE_READ_ONLY;
244 
245 		tree->t_acltype = smb_fsop_acltype(snode);
246 
247 		if (strncasecmp(tree->t_typename, NFS, sizeof (NFS)) == 0)
248 			tree->t_flags |= SMB_TREE_FLAG_NFS_MOUNTED;
249 
250 		if (strncasecmp(tree->t_typename, "UFS", sizeof ("UFS")) == 0)
251 			tree->t_flags |= SMB_TREE_FLAG_UFS;
252 
253 		if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_ACLONCREATE))
254 			tree->t_flags |= SMB_TREE_FLAG_ACLONCREATE;
255 
256 		if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_ACEMASKONACCESS))
257 			tree->t_flags |= SMB_TREE_FLAG_ACEMASKONACCESS;
258 
259 		if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_CASEINSENSITIVE))
260 			tree->t_flags |= SMB_TREE_FLAG_IGNORE_CASE;
261 
262 		break;
263 
264 	case STYPE_IPC:
265 	default:
266 		tree->t_typename[0] = '\0';
267 		break;
268 	}
269 
270 	smb_llist_enter(&user->u_tree_list, RW_WRITER);
271 	smb_llist_insert_head(&user->u_tree_list, tree);
272 	smb_llist_exit(&user->u_tree_list);
273 	atomic_inc_32(&user->u_session->s_tree_cnt);
274 	atomic_inc_32(&smb_info.open_trees);
275 
276 	return (tree);
277 }
278 
279 /*
280  * smb_tree_disconnect
281  *
282  *
283  */
284 void
285 smb_tree_disconnect(
286     smb_tree_t	*tree)
287 {
288 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
289 
290 	mutex_enter(&tree->t_mutex);
291 	ASSERT(tree->t_refcnt);
292 	switch (tree->t_state) {
293 	case SMB_TREE_STATE_CONNECTED: {
294 		/*
295 		 * The tree is moved into a state indicating that the disconnect
296 		 * process has started.
297 		 */
298 		tree->t_state = SMB_TREE_STATE_DISCONNECTING;
299 		mutex_exit(&tree->t_mutex);
300 		atomic_dec_32(&smb_info.open_trees);
301 		/*
302 		 * The files opened under this tree are closed.
303 		 */
304 		smb_ofile_close_all(tree);
305 		/*
306 		 * The directories opened under this tree are closed.
307 		 */
308 		smb_odir_close_all(tree);
309 		mutex_enter(&tree->t_mutex);
310 		tree->t_state = SMB_TREE_STATE_DISCONNECTED;
311 		/*FALLTHRU*/
312 	}
313 	case SMB_TREE_STATE_DISCONNECTED:
314 	case SMB_TREE_STATE_DISCONNECTING:
315 		break;
316 
317 	default:
318 		ASSERT(0);
319 		break;
320 	}
321 	mutex_exit(&tree->t_mutex);
322 }
323 
324 /*
325  * smb_tree_disconnect_all
326  *
327  *
328  */
329 void
330 smb_tree_disconnect_all(
331     smb_user_t		*user)
332 {
333 	smb_tree_t	*tree;
334 
335 	ASSERT(user);
336 	ASSERT(user->u_magic == SMB_USER_MAGIC);
337 
338 	tree = smb_tree_lookup_head(&user->u_tree_list);
339 	while (tree) {
340 		ASSERT(tree->t_user == user);
341 		smb_tree_disconnect(tree);
342 		smb_tree_release(tree);
343 		tree = smb_tree_lookup_head(&user->u_tree_list);
344 	}
345 }
346 
347 /*
348  * smb_tree_close_all_by_pid
349  *
350  *
351  */
352 void
353 smb_tree_close_all_by_pid(
354     smb_user_t		*user,
355     uint16_t		pid)
356 {
357 	smb_tree_t	*tree;
358 
359 	ASSERT(user);
360 	ASSERT(user->u_magic == SMB_USER_MAGIC);
361 
362 	tree = smb_tree_lookup_head(&user->u_tree_list);
363 	while (tree) {
364 		smb_tree_t	*next;
365 		ASSERT(tree->t_user == user);
366 		smb_ofile_close_all_by_pid(tree, pid);
367 		smb_odir_close_all_by_pid(tree, pid);
368 		next = smb_tree_lookup_next(&user->u_tree_list, tree);
369 		smb_tree_release(tree);
370 		tree = next;
371 	}
372 }
373 
374 /*
375  * smb_tree_release
376  *
377  *
378  */
379 void
380 smb_tree_release(
381     smb_tree_t		*tree)
382 {
383 	ASSERT(tree);
384 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
385 
386 	mutex_enter(&tree->t_mutex);
387 	ASSERT(tree->t_refcnt);
388 	tree->t_refcnt--;
389 	switch (tree->t_state) {
390 	case SMB_TREE_STATE_DISCONNECTED:
391 		if (tree->t_refcnt == 0) {
392 			mutex_exit(&tree->t_mutex);
393 			smb_tree_delete(tree);
394 			return;
395 		}
396 		break;
397 
398 	case SMB_TREE_STATE_CONNECTED:
399 	case SMB_TREE_STATE_DISCONNECTING:
400 		break;
401 
402 	default:
403 		ASSERT(0);
404 		break;
405 	}
406 	mutex_exit(&tree->t_mutex);
407 }
408 
409 /*
410  * Find the appropriate tree for this request. The request credentials
411  * set here override those set during uid lookup. In domain mode, the
412  * user and tree credentials should be the same. In share mode, the
413  * tree credentials (defined in the share definition) should override
414  * the user credentials.
415  */
416 smb_tree_t *
417 smb_tree_lookup_by_tid(
418     smb_user_t		*user,
419     uint16_t		tid)
420 {
421 	smb_tree_t	*tree;
422 
423 	ASSERT(user);
424 	ASSERT(user->u_magic == SMB_USER_MAGIC);
425 
426 	smb_llist_enter(&user->u_tree_list, RW_READER);
427 	tree = smb_llist_head(&user->u_tree_list);
428 	while (tree) {
429 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
430 		ASSERT(tree->t_user == user);
431 		if (tree->t_tid == tid) {
432 			mutex_enter(&tree->t_mutex);
433 			switch (tree->t_state) {
434 			case SMB_TREE_STATE_CONNECTED:
435 				/* The tree exists and is still connected. */
436 				tree->t_refcnt++;
437 				mutex_exit(&tree->t_mutex);
438 				smb_llist_exit(&user->u_tree_list);
439 				return (tree);
440 			case SMB_TREE_STATE_DISCONNECTING:
441 			case SMB_TREE_STATE_DISCONNECTED:
442 				/*
443 				 * The tree exists but is diconnected or is in
444 				 * the process of being destroyed.
445 				 */
446 				mutex_exit(&tree->t_mutex);
447 				smb_llist_exit(&user->u_tree_list);
448 				return (NULL);
449 			default:
450 				ASSERT(0);
451 				mutex_exit(&tree->t_mutex);
452 				smb_llist_exit(&user->u_tree_list);
453 				return (NULL);
454 			}
455 		}
456 		tree = smb_llist_next(&user->u_tree_list, tree);
457 	}
458 	smb_llist_exit(&user->u_tree_list);
459 	return (NULL);
460 }
461 
462 /*
463  * smb_tree_lookup_first_by_name
464  *
465  * This function returns the first tree in the connected state that matches the
466  * sharename passed in. If the tree provided is NULL the search starts from
467  * the beginning of the list of trees of the user. It a tree is provided the
468  * search starts just after that tree.
469  */
470 smb_tree_t *
471 smb_tree_lookup_by_name(
472     smb_user_t		*user,
473     char		*sharename,
474     smb_tree_t		*tree)
475 {
476 	ASSERT(user);
477 	ASSERT(user->u_magic == SMB_USER_MAGIC);
478 	ASSERT(sharename);
479 
480 	smb_llist_enter(&user->u_tree_list, RW_READER);
481 
482 	if (tree) {
483 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
484 		ASSERT(tree->t_user == user);
485 		tree = smb_llist_next(&user->u_tree_list, tree);
486 	} else {
487 		tree = smb_llist_head(&user->u_tree_list);
488 	}
489 
490 	while (tree) {
491 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
492 		ASSERT(tree->t_user == user);
493 		if (strcmp(tree->t_sharename, sharename) == 0) {
494 			mutex_enter(&tree->t_mutex);
495 			switch (tree->t_state) {
496 			case SMB_TREE_STATE_CONNECTED:
497 				/* The tree exists and is still connected. */
498 				tree->t_refcnt++;
499 				mutex_exit(&tree->t_mutex);
500 				smb_llist_exit(&user->u_tree_list);
501 				return (tree);
502 			case SMB_TREE_STATE_DISCONNECTING:
503 			case SMB_TREE_STATE_DISCONNECTED:
504 				/*
505 				 * The tree exists but is diconnected or is in
506 				 * the process of being destroyed.
507 				 */
508 				mutex_exit(&tree->t_mutex);
509 				break;
510 			default:
511 				ASSERT(0);
512 				mutex_exit(&tree->t_mutex);
513 				break;
514 			}
515 		}
516 		tree = smb_llist_next(&user->u_tree_list, tree);
517 	}
518 	smb_llist_exit(&user->u_tree_list);
519 	return (NULL);
520 }
521 
522 /*
523  * smb_tree_lookup_first_by_fsd
524  *
525  * This function returns the first tree in the connected state that matches the
526  * fsd passed in. If the tree provided is NULL the search starts from
527  * the beginning of the list of trees of the user. It a tree is provided the
528  * search starts just after that tree.
529  */
530 smb_tree_t *
531 smb_tree_lookup_by_fsd(
532     smb_user_t		*user,
533     fs_desc_t		*fsd,
534     smb_tree_t		*tree)
535 {
536 	ASSERT(user);
537 	ASSERT(user->u_magic == SMB_USER_MAGIC);
538 	ASSERT(fsd);
539 
540 	smb_llist_enter(&user->u_tree_list, RW_READER);
541 
542 	if (tree) {
543 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
544 		ASSERT(tree->t_user == user);
545 		tree = smb_llist_next(&user->u_tree_list, tree);
546 	} else {
547 		tree = smb_llist_head(&user->u_tree_list);
548 	}
549 
550 	while (tree) {
551 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
552 		ASSERT(tree->t_user == user);
553 		if (fsd_cmp(&tree->t_fsd, fsd) == 0) {
554 			mutex_enter(&tree->t_mutex);
555 			switch (tree->t_state) {
556 			case SMB_TREE_STATE_CONNECTED:
557 				/* The tree exists and is still connected. */
558 				tree->t_refcnt++;
559 				mutex_exit(&tree->t_mutex);
560 				smb_llist_exit(&user->u_tree_list);
561 				return (tree);
562 			case SMB_TREE_STATE_DISCONNECTING:
563 			case SMB_TREE_STATE_DISCONNECTED:
564 				/*
565 				 * The tree exists but is diconnected or is in
566 				 * the process of being destroyed.
567 				 */
568 				mutex_exit(&tree->t_mutex);
569 				break;
570 			default:
571 				ASSERT(0);
572 				mutex_exit(&tree->t_mutex);
573 				break;
574 			}
575 		}
576 		tree = smb_llist_next(&user->u_tree_list, tree);
577 	}
578 	smb_llist_exit(&user->u_tree_list);
579 	return (NULL);
580 }
581 
582 /* *************************** Static Functions ***************************** */
583 
584 /*
585  * smb_tree_delete
586  *
587  * This function releases all the resources associated with a tree. It also
588  * removes the tree the caller passes from the list of trees of the user.
589  *
590  * The tree to destroy must be in the "destroying state" and the reference count
591  * must be zero. This function assumes it's single threaded i.e. only one
592  * thread will attempt to destroy a specific tree (this condition should be met
593  * if the tree is is the "destroying state" and has a reference count of zero).
594  *
595  * Entry:
596  *	tree	Tree to destroy
597  *
598  * Exit:
599  *	Nothing
600  *
601  * Return:
602  *	Nothing
603  */
604 static void
605 smb_tree_delete(smb_tree_t *tree)
606 {
607 	ASSERT(tree);
608 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
609 	ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
610 	ASSERT(tree->t_refcnt == 0);
611 
612 	/*
613 	 * Let's remove the tree from the list of trees of the
614 	 * user. This has to be done before any resources
615 	 * associated with the tree are released.
616 	 */
617 	smb_llist_enter(&tree->t_user->u_tree_list, RW_WRITER);
618 	smb_llist_remove(&tree->t_user->u_tree_list, tree);
619 	smb_llist_exit(&tree->t_user->u_tree_list);
620 
621 	tree->t_magic = (uint32_t)~SMB_TREE_MAGIC;
622 	smb_idpool_free(&tree->t_user->u_tid_pool, tree->t_tid);
623 	atomic_dec_32(&tree->t_session->s_tree_cnt);
624 
625 	if (tree->t_snode) {
626 		smb_node_release(tree->t_snode);
627 	}
628 	mutex_destroy(&tree->t_mutex);
629 	/*
630 	 * The list of open files and open directories should be empty.
631 	 */
632 	smb_llist_destructor(&tree->t_ofile_list);
633 	smb_llist_destructor(&tree->t_odir_list);
634 	smb_idpool_destructor(&tree->t_fid_pool);
635 	smb_idpool_destructor(&tree->t_sid_pool);
636 	kmem_cache_free(smb_info.si_cache_tree, tree);
637 }
638 
639 /*
640  * smb_tree_lookup_head
641  *
642  * This function returns the first tree in the list that is in the
643  * SMB_TREE_STATE_CONNECTED. A reference is taken on the tree and
644  * smb_tree_release() will have to be called for the tree returned.
645  *
646  * Entry:
647  *	lst	List of trees (usually the list of trees of a user)
648  *
649  * Exit:
650  *	Nothing
651  *
652  * Return:
653  *	NULL	No tree in the SMB_TREE_STATE_CONNECTED state was found.
654  *	!NULL	First tree in the list in the SMB_TREE_STATE_CONNECTED state.
655  */
656 static smb_tree_t *
657 smb_tree_lookup_head(
658     smb_llist_t		*lst)
659 {
660 	smb_tree_t	*tree;
661 
662 	smb_llist_enter(lst, RW_READER);
663 	tree = smb_llist_head(lst);
664 	while (tree) {
665 		ASSERT(tree->t_magic == SMB_TREE_MAGIC);
666 		mutex_enter(&tree->t_mutex);
667 		if (tree->t_state == SMB_TREE_STATE_CONNECTED) {
668 			tree->t_refcnt++;
669 			mutex_exit(&tree->t_mutex);
670 			break;
671 		} else if ((tree->t_state == SMB_TREE_STATE_DISCONNECTING) ||
672 		    (tree->t_state == SMB_TREE_STATE_DISCONNECTED)) {
673 			mutex_exit(&tree->t_mutex);
674 			tree = smb_llist_next(lst, tree);
675 		} else {
676 			ASSERT(0);
677 			mutex_exit(&tree->t_mutex);
678 			tree = smb_llist_next(lst, tree);
679 		}
680 	}
681 	smb_llist_exit(lst);
682 
683 	return (tree);
684 }
685 
686 /*
687  * smb_tree_lookup_next
688  *
689  * This function returns the next tree in the list that is in the
690  * SMB_TREE_STATE_CONNECTED. A reference is taken on the tree and
691  * smb_tree_release() will have to be called for the tree returned.
692  *
693  * Entry:
694  *	lst	List of trees (usually the list of trees of a user).
695  *	tree	Starting tree.
696  *
697  * Exit:
698  *	Nothing
699  *
700  * Return:
701  *	NULL	No tree in the SMB_TREE_STATE_CONNECTED state was found.
702  *	!NULL	Next tree in the list in the SMB_TREE_STATE_CONNECTED state.
703  */
704 static smb_tree_t *
705 smb_tree_lookup_next(
706     smb_llist_t		*lst,
707     smb_tree_t		*tree)
708 {
709 	smb_tree_t	*next;
710 
711 	ASSERT(lst);
712 	ASSERT(tree);
713 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
714 	ASSERT(tree->t_refcnt);
715 
716 	smb_llist_enter(lst, RW_READER);
717 	next = smb_llist_next(lst, tree);
718 	while (next) {
719 		ASSERT(next->t_magic == SMB_TREE_MAGIC);
720 		mutex_enter(&next->t_mutex);
721 		if (next->t_state == SMB_TREE_STATE_CONNECTED) {
722 			next->t_refcnt++;
723 			mutex_exit(&next->t_mutex);
724 			break;
725 		} else if ((next->t_state == SMB_TREE_STATE_DISCONNECTING) ||
726 		    (next->t_state == SMB_TREE_STATE_DISCONNECTED)) {
727 			mutex_exit(&next->t_mutex);
728 			next = smb_llist_next(lst, next);
729 		} else {
730 			ASSERT(0);
731 			mutex_exit(&next->t_mutex);
732 			next = smb_llist_next(lst, next);
733 		}
734 	}
735 	smb_llist_exit(lst);
736 
737 	return (next);
738 }
739