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