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