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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * General Structures Layout 28 * ------------------------- 29 * 30 * This is a simplified diagram showing the relationship between most of the 31 * main structures. 32 * 33 * +-------------------+ 34 * | SMB_INFO | 35 * +-------------------+ 36 * | 37 * | 38 * v 39 * +-------------------+ +-------------------+ +-------------------+ 40 * | SESSION |<----->| SESSION |......| SESSION | 41 * +-------------------+ +-------------------+ +-------------------+ 42 * | 43 * | 44 * v 45 * +-------------------+ +-------------------+ +-------------------+ 46 * | USER |<----->| USER |......| USER | 47 * +-------------------+ +-------------------+ +-------------------+ 48 * | 49 * | 50 * v 51 * +-------------------+ +-------------------+ +-------------------+ 52 * | TREE |<----->| TREE |......| TREE | 53 * +-------------------+ +-------------------+ +-------------------+ 54 * | | 55 * | | 56 * | v 57 * | +-------+ +-------+ +-------+ 58 * | | OFILE |<----->| OFILE |......| OFILE | 59 * | +-------+ +-------+ +-------+ 60 * | 61 * | 62 * v 63 * +-------+ +------+ +------+ 64 * | ODIR |<----->| ODIR |......| ODIR | 65 * +-------+ +------+ +------+ 66 * 67 * 68 * Tree State Machine 69 * ------------------ 70 * 71 * +-----------------------------+ T0 72 * | SMB_TREE_STATE_CONNECTED |<----------- Creation/Allocation 73 * +-----------------------------+ 74 * | 75 * | T1 76 * | 77 * v 78 * +------------------------------+ 79 * | SMB_TREE_STATE_DISCONNECTING | 80 * +------------------------------+ 81 * | 82 * | T2 83 * | 84 * v 85 * +-----------------------------+ T3 86 * | SMB_TREE_STATE_DISCONNECTED |----------> Deletion/Free 87 * +-----------------------------+ 88 * 89 * SMB_TREE_STATE_CONNECTED 90 * 91 * While in this state: 92 * - The tree is queued in the list of trees of its user. 93 * - References will be given out if the tree is looked up. 94 * - Files under that tree can be accessed. 95 * 96 * SMB_TREE_STATE_DISCONNECTING 97 * 98 * While in this state: 99 * - The tree is queued in the list of trees of its user. 100 * - References will not be given out if the tree is looked up. 101 * - The files and directories open under the tree are being closed. 102 * - The resources associated with the tree remain. 103 * 104 * SMB_TREE_STATE_DISCONNECTED 105 * 106 * While in this state: 107 * - The tree is queued in the list of trees of its user. 108 * - References will not be given out if the tree is looked up. 109 * - The tree has no more files and directories opened. 110 * - The resources associated with the tree remain. 111 * 112 * Transition T0 113 * 114 * This transition occurs in smb_tree_connect(). A new tree is created and 115 * added to the list of trees of a user. 116 * 117 * Transition T1 118 * 119 * This transition occurs in smb_tree_disconnect(). 120 * 121 * Transition T2 122 * 123 * This transition occurs in smb_tree_release(). The resources associated 124 * with the tree are freed as well as the tree structure. For the transition 125 * to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and 126 * the reference count be zero. 127 * 128 * Comments 129 * -------- 130 * 131 * The state machine of the tree structures is controlled by 3 elements: 132 * - The list of trees of the user it belongs to. 133 * - The mutex embedded in the structure itself. 134 * - The reference count. 135 * 136 * There's a mutex embedded in the tree structure used to protect its fields 137 * and there's a lock embedded in the list of trees of a user. To 138 * increment or to decrement the reference count the mutex must be entered. 139 * To insert the tree into the list of trees of the user and to remove 140 * the tree from it, the lock must be entered in RW_WRITER mode. 141 * 142 * Rules of access to a tree structure: 143 * 144 * 1) In order to avoid deadlocks, when both (mutex and lock of the user 145 * list) have to be entered, the lock must be entered first. 146 * 147 * 2) All actions applied to a tree require a reference count. 148 * 149 * 3) There are 2 ways of getting a reference count: when a tree is 150 * connected and when a tree is looked up. 151 * 152 * It should be noted that the reference count of a tree registers the 153 * number of references to the tree in other structures (such as an smb 154 * request). The reference count is not incremented in these 2 instances: 155 * 156 * 1) The tree is connected. An tree is anchored by his state. If there's 157 * no activity involving a tree currently connected, the reference 158 * count of that tree is zero. 159 * 160 * 2) The tree is queued in the list of trees of the user. The fact of 161 * being queued in that list is NOT registered by incrementing the 162 * reference count. 163 */ 164 #include <sys/types.h> 165 #include <sys/refstr_impl.h> 166 #include <sys/feature_tests.h> 167 #include <sys/sunddi.h> 168 #include <sys/fsid.h> 169 #include <sys/vfs.h> 170 #include <sys/stat.h> 171 #include <sys/varargs.h> 172 #include <sys/cred.h> 173 #include <smbsrv/smb_kproto.h> 174 #include <smbsrv/lmerr.h> 175 #include <smbsrv/smb_fsops.h> 176 #include <smbsrv/smb_share.h> 177 #include <sys/pathname.h> 178 179 int smb_tcon_mute = 0; 180 181 static smb_tree_t *smb_tree_connect_disk(smb_request_t *, const char *); 182 static smb_tree_t *smb_tree_connect_ipc(smb_request_t *, const char *); 183 static smb_tree_t *smb_tree_alloc(smb_user_t *, const smb_share_t *, 184 int32_t, smb_node_t *, uint32_t); 185 static boolean_t smb_tree_is_connected_locked(smb_tree_t *); 186 static boolean_t smb_tree_is_disconnected(smb_tree_t *); 187 static const char *smb_tree_get_sharename(const char *); 188 static int smb_tree_get_stype(const char *, const char *, int32_t *); 189 static int smb_tree_getattr(const smb_share_t *, smb_node_t *, smb_tree_t *); 190 static void smb_tree_get_volname(vfs_t *, smb_tree_t *); 191 static void smb_tree_get_flags(const smb_share_t *, vfs_t *, smb_tree_t *); 192 static void smb_tree_log(smb_request_t *, const char *, const char *, ...); 193 static void smb_tree_close_odirs(smb_tree_t *, uint16_t); 194 static smb_ofile_t *smb_tree_get_ofile(smb_tree_t *, smb_ofile_t *); 195 static smb_odir_t *smb_tree_get_odir(smb_tree_t *, smb_odir_t *); 196 static void smb_tree_set_execsub_info(smb_tree_t *, smb_execsub_info_t *); 197 static int smb_tree_enum_private(smb_tree_t *, smb_svcenum_t *); 198 static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *); 199 static void smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *); 200 static void smb_tree_netinfo_fini(smb_netconnectinfo_t *); 201 202 /* 203 * Extract the share name and share type and connect as appropriate. 204 * Share names are case insensitive so we map the share name to 205 * lower-case as a convenience for internal processing. 206 */ 207 smb_tree_t * 208 smb_tree_connect(smb_request_t *sr) 209 { 210 char *unc_path = sr->arg.tcon.path; 211 char *service = sr->arg.tcon.service; 212 smb_tree_t *tree = NULL; 213 const char *name; 214 int32_t stype; 215 216 (void) smb_strlwr(unc_path); 217 218 if ((name = smb_tree_get_sharename(unc_path)) == NULL) { 219 smbsr_error(sr, 0, ERRSRV, ERRinvnetname); 220 return (NULL); 221 } 222 223 if (smb_tree_get_stype(name, service, &stype) != 0) { 224 smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE, 225 ERRDOS, ERROR_BAD_DEV_TYPE); 226 return (NULL); 227 } 228 229 switch (stype & STYPE_MASK) { 230 case STYPE_DISKTREE: 231 tree = smb_tree_connect_disk(sr, name); 232 break; 233 234 case STYPE_IPC: 235 tree = smb_tree_connect_ipc(sr, name); 236 break; 237 238 default: 239 smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE, 240 ERRDOS, ERROR_BAD_DEV_TYPE); 241 break; 242 } 243 244 return (tree); 245 } 246 247 /* 248 * Disconnect a tree. 249 */ 250 void 251 smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec) 252 { 253 smb_execsub_info_t subs; 254 255 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 256 257 mutex_enter(&tree->t_mutex); 258 ASSERT(tree->t_refcnt); 259 260 if (smb_tree_is_connected_locked(tree)) { 261 /* 262 * Indicate that the disconnect process has started. 263 */ 264 tree->t_state = SMB_TREE_STATE_DISCONNECTING; 265 mutex_exit(&tree->t_mutex); 266 atomic_dec_32(&tree->t_server->sv_open_trees); 267 268 if (do_exec) { 269 /* 270 * The files opened under this tree are closed. 271 */ 272 smb_ofile_close_all(tree); 273 /* 274 * The directories opened under this tree are closed. 275 */ 276 smb_tree_close_odirs(tree, 0); 277 } 278 279 mutex_enter(&tree->t_mutex); 280 tree->t_state = SMB_TREE_STATE_DISCONNECTED; 281 } 282 283 mutex_exit(&tree->t_mutex); 284 285 if (do_exec && tree->t_state == SMB_TREE_STATE_DISCONNECTED && 286 tree->t_shr_flags & SMB_SHRF_UNMAP) { 287 288 (void) smb_tree_set_execsub_info(tree, &subs); 289 290 (void) smb_kshare_exec(tree->t_server->sv_lmshrd, 291 (char *)tree->t_sharename, &subs, SMB_SHR_UNMAP); 292 } 293 } 294 295 /* 296 * Take a reference on a tree. 297 */ 298 boolean_t 299 smb_tree_hold( 300 smb_tree_t *tree) 301 { 302 ASSERT(tree); 303 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 304 305 mutex_enter(&tree->t_mutex); 306 307 if (smb_tree_is_connected_locked(tree)) { 308 tree->t_refcnt++; 309 mutex_exit(&tree->t_mutex); 310 return (B_TRUE); 311 } 312 313 mutex_exit(&tree->t_mutex); 314 return (B_FALSE); 315 } 316 317 /* 318 * Release a reference on a tree. If the tree is disconnected and the 319 * reference count falls to zero, post the object for deletion. 320 * Object deletion is deferred to avoid modifying a list while an 321 * iteration may be in progress. 322 */ 323 void 324 smb_tree_release( 325 smb_tree_t *tree) 326 { 327 SMB_TREE_VALID(tree); 328 329 mutex_enter(&tree->t_mutex); 330 ASSERT(tree->t_refcnt); 331 tree->t_refcnt--; 332 333 if (smb_tree_is_disconnected(tree) && (tree->t_refcnt == 0)) 334 smb_user_post_tree(tree->t_user, tree); 335 336 mutex_exit(&tree->t_mutex); 337 } 338 339 void 340 smb_tree_post_ofile(smb_tree_t *tree, smb_ofile_t *of) 341 { 342 SMB_TREE_VALID(tree); 343 SMB_OFILE_VALID(of); 344 ASSERT(of->f_refcnt == 0); 345 ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED); 346 ASSERT(of->f_tree == tree); 347 348 smb_llist_post(&tree->t_ofile_list, of, smb_ofile_delete); 349 } 350 351 void 352 smb_tree_post_odir(smb_tree_t *tree, smb_odir_t *od) 353 { 354 SMB_TREE_VALID(tree); 355 SMB_ODIR_VALID(od); 356 ASSERT(od->d_refcnt == 0); 357 ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED); 358 ASSERT(od->d_tree == tree); 359 360 smb_llist_post(&tree->t_odir_list, od, smb_odir_delete); 361 } 362 363 /* 364 * Close ofiles and odirs that match pid. 365 */ 366 void 367 smb_tree_close_pid( 368 smb_tree_t *tree, 369 uint16_t pid) 370 { 371 ASSERT(tree); 372 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 373 374 smb_ofile_close_all_by_pid(tree, pid); 375 smb_tree_close_odirs(tree, pid); 376 } 377 378 /* 379 * Check whether or not a tree supports the features identified by flags. 380 */ 381 boolean_t 382 smb_tree_has_feature(smb_tree_t *tree, uint32_t flags) 383 { 384 ASSERT(tree); 385 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 386 387 return ((tree->t_flags & flags) == flags); 388 } 389 390 /* 391 * If the enumeration request is for tree data, handle the request 392 * here. Otherwise, pass it on to the ofiles. 393 * 394 * This function should be called with a hold on the tree. 395 */ 396 int 397 smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum) 398 { 399 smb_ofile_t *of; 400 smb_ofile_t *next; 401 int rc; 402 403 ASSERT(tree); 404 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 405 406 if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE) 407 return (smb_tree_enum_private(tree, svcenum)); 408 409 of = smb_tree_get_ofile(tree, NULL); 410 while (of) { 411 ASSERT(of->f_tree == tree); 412 413 rc = smb_ofile_enum(of, svcenum); 414 if (rc != 0) { 415 smb_ofile_release(of); 416 break; 417 } 418 419 next = smb_tree_get_ofile(tree, of); 420 smb_ofile_release(of); 421 of = next; 422 } 423 424 return (rc); 425 } 426 427 /* 428 * Close a file by its unique id. 429 */ 430 int 431 smb_tree_fclose(smb_tree_t *tree, uint32_t uniqid) 432 { 433 smb_ofile_t *of; 434 435 ASSERT(tree); 436 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 437 438 if ((of = smb_ofile_lookup_by_uniqid(tree, uniqid)) == NULL) 439 return (ENOENT); 440 441 if (smb_ofile_disallow_fclose(of)) { 442 smb_ofile_release(of); 443 return (EACCES); 444 } 445 446 smb_ofile_close(of, 0); 447 smb_ofile_release(of); 448 return (0); 449 } 450 451 /* *************************** Static Functions ***************************** */ 452 453 #define SHARES_DIR ".zfs/shares/" 454 static void 455 smb_tree_acl_access(smb_request_t *sr, const smb_share_t *si, vnode_t *pathvp, 456 uint32_t *access) 457 { 458 smb_user_t *user; 459 cred_t *cred; 460 int rc; 461 vfs_t *vfsp; 462 vnode_t *root = NULL; 463 vnode_t *sharevp = NULL; 464 char *sharepath; 465 struct pathname pnp; 466 size_t size; 467 468 user = sr->uid_user; 469 cred = user->u_cred; 470 *access = ACE_ALL_PERMS; /* default to full "UNIX" access */ 471 472 if (si->shr_flags & SMB_SHRF_AUTOHOME) { 473 /* 474 * An autohome share owner gets full access to the share. 475 * Everyone else is denied access. 476 */ 477 if (smb_strcasecmp(si->shr_name, user->u_name, 0) != 0) 478 *access = 0; 479 return; 480 } 481 482 /* 483 * Using the vnode of the share path, we then find the root 484 * directory of the mounted file system. We will then look to 485 * see if there is a .zfs/shares directory and if there is, 486 * get the access information from the ACL/ACES values and 487 * check against the cred. 488 */ 489 vfsp = pathvp->v_vfsp; 490 if (vfsp != NULL) 491 rc = VFS_ROOT(vfsp, &root); 492 else 493 rc = ENOENT; 494 495 if (rc != 0) 496 return; 497 498 499 /* 500 * Find the share object, if there is one. Need to construct 501 * the path to the .zfs/shares/<sharename> object and look it 502 * up. root is called held but will be released by 503 * lookuppnvp(). 504 */ 505 506 size = sizeof (SHARES_DIR) + strlen(si->shr_name) + 1; 507 sharepath = kmem_alloc(size, KM_SLEEP); 508 (void) sprintf(sharepath, "%s%s", SHARES_DIR, si->shr_name); 509 510 pn_alloc(&pnp); 511 (void) pn_set(&pnp, sharepath); 512 rc = lookuppnvp(&pnp, NULL, NO_FOLLOW, NULL, 513 &sharevp, rootdir, root, kcred); 514 pn_free(&pnp); 515 516 kmem_free(sharepath, size); 517 518 /* 519 * Now get the effective access value based on cred and ACL 520 * values. 521 */ 522 523 if (rc == 0) { 524 smb_vop_eaccess(sharevp, (int *)access, V_ACE_MASK, NULL, cred); 525 VN_RELE(sharevp); 526 } 527 } 528 529 /* 530 * Connect a share for use with files and directories. 531 */ 532 533 static smb_tree_t * 534 smb_tree_connect_disk(smb_request_t *sr, const char *sharename) 535 { 536 smb_user_t *user = sr->uid_user; 537 smb_node_t *dnode = NULL; 538 smb_node_t *snode = NULL; 539 char last_component[MAXNAMELEN]; 540 smb_tree_t *tree; 541 smb_share_t *si; 542 cred_t *u_cred; 543 int rc; 544 uint32_t access = 0; /* read/write is assumed */ 545 uint32_t hostaccess = ACE_ALL_PERMS; 546 uint32_t aclaccess; 547 smb_execsub_info_t subs; 548 549 ASSERT(user); 550 u_cred = user->u_cred; 551 ASSERT(u_cred); 552 553 if (user->u_flags & SMB_USER_FLAG_IPC) { 554 smb_tree_log(sr, sharename, "access denied: IPC only"); 555 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess); 556 return (NULL); 557 } 558 559 si = kmem_zalloc(sizeof (smb_share_t), KM_SLEEP); 560 561 if (smb_kshare_getinfo(sr->sr_server->sv_lmshrd, (char *)sharename, si, 562 &sr->session->ipaddr) != NERR_Success) { 563 smb_tree_log(sr, sharename, "share not found"); 564 smbsr_error(sr, 0, ERRSRV, ERRinvnetname); 565 kmem_free(si, sizeof (smb_share_t)); 566 return (NULL); 567 } 568 569 if (user->u_flags & SMB_USER_FLAG_GUEST) { 570 if ((si->shr_flags & SMB_SHRF_GUEST_OK) == 0) { 571 smb_tree_log(sr, sharename, 572 "access denied: guest disabled"); 573 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, 574 ERRaccess); 575 kmem_free(si, sizeof (smb_share_t)); 576 return (NULL); 577 } 578 } 579 580 /* 581 * Handle the default administration shares: C$, D$ etc. 582 * Only a user with admin rights is allowed to map these 583 * shares. 584 */ 585 if (si->shr_flags & SMB_SHRF_ADMIN) { 586 if (!smb_user_is_admin(user)) { 587 smb_tree_log(sr, sharename, "access denied: not admin"); 588 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 589 ERRSRV, ERRaccess); 590 kmem_free(si, sizeof (smb_share_t)); 591 return (NULL); 592 } 593 } 594 595 /* 596 * Set up the OptionalSupport for this share. 597 */ 598 sr->arg.tcon.optional_support = SMB_SUPPORT_SEARCH_BITS; 599 600 switch (si->shr_flags & SMB_SHRF_CSC_MASK) { 601 case SMB_SHRF_CSC_DISABLED: 602 sr->arg.tcon.optional_support |= SMB_CSC_CACHE_NONE; 603 break; 604 case SMB_SHRF_CSC_AUTO: 605 sr->arg.tcon.optional_support |= SMB_CSC_CACHE_AUTO_REINT; 606 break; 607 case SMB_SHRF_CSC_VDO: 608 sr->arg.tcon.optional_support |= SMB_CSC_CACHE_VDO; 609 break; 610 case SMB_SHRF_CSC_MANUAL: 611 default: 612 /* 613 * Default to SMB_CSC_CACHE_MANUAL_REINT. 614 */ 615 break; 616 } 617 618 /* ABE support */ 619 if (si->shr_flags & SMB_SHRF_ABE) 620 sr->arg.tcon.optional_support |= 621 SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM; 622 623 if (si->shr_flags & SMB_SHRF_DFSROOT) 624 sr->arg.tcon.optional_support |= SMB_SHARE_IS_IN_DFS; 625 626 access = si->shr_access_value & SMB_SHRF_ACC_ALL; 627 628 if (access == SMB_SHRF_ACC_RO) { 629 hostaccess &= ~ACE_ALL_WRITE_PERMS; 630 } else if (access == SMB_SHRF_ACC_NONE) { 631 kmem_free(si, sizeof (smb_share_t)); 632 smb_tree_log(sr, sharename, "access denied: host access"); 633 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess); 634 return (NULL); 635 } 636 637 /* 638 * Check that the shared directory exists. 639 */ 640 rc = smb_pathname_reduce(sr, u_cred, si->shr_path, 0, 0, &dnode, 641 last_component); 642 643 if (rc == 0) { 644 rc = smb_fsop_lookup(sr, u_cred, SMB_FOLLOW_LINKS, 645 sr->sr_server->si_root_smb_node, dnode, last_component, 646 &snode); 647 648 smb_node_release(dnode); 649 } 650 651 if (rc) { 652 if (snode) 653 smb_node_release(snode); 654 655 smb_tree_log(sr, sharename, "bad path: %s", si->shr_path); 656 smbsr_error(sr, 0, ERRSRV, ERRinvnetname); 657 kmem_free(si, sizeof (smb_share_t)); 658 return (NULL); 659 } 660 661 /* 662 * Find share level ACL if it exists in the designated 663 * location. Needs to be done after finding a valid path but 664 * before the tree is allocated. 665 */ 666 smb_tree_acl_access(sr, si, snode->vp, &aclaccess); 667 if ((aclaccess & ACE_ALL_PERMS) == 0) { 668 smb_tree_log(sr, sharename, "access denied: share ACL"); 669 smbsr_error(sr, 0, ERRSRV, ERRaccess); 670 kmem_free(si, sizeof (smb_share_t)); 671 smb_node_release(snode); 672 return (NULL); 673 } 674 675 /* 676 * Set tree ACL access to the minimum ACL permissions based on 677 * hostaccess (those allowed by host based access) and 678 * aclaccess (those from the ACL object for the share). This 679 * is done during the alloc. 680 */ 681 682 (void) strlcpy(si->shr_name, sharename, MAXNAMELEN); 683 tree = smb_tree_alloc(user, si, STYPE_DISKTREE, snode, 684 hostaccess & aclaccess); 685 686 smb_node_release(snode); 687 688 if (tree == NULL) 689 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess); 690 else { 691 692 tree->t_shr_flags = si->shr_flags; 693 694 if (tree->t_shr_flags & SMB_SHRF_MAP) { 695 (void) smb_tree_set_execsub_info(tree, &subs); 696 697 rc = smb_kshare_exec(sr->sr_server->sv_lmshrd, 698 (char *)sharename, &subs, SMB_SHR_MAP); 699 700 if (rc != 0 && tree->t_shr_flags & SMB_SHRF_DISP_TERM) { 701 smb_tree_disconnect(tree, B_FALSE); 702 smb_tree_release(tree); 703 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, 704 ERRaccess); 705 kmem_free(si, sizeof (smb_share_t)); 706 return (NULL); 707 } 708 } 709 } 710 711 kmem_free(si, sizeof (smb_share_t)); 712 713 return (tree); 714 } 715 716 /* 717 * Connect an IPC share for use with named pipes. 718 */ 719 static smb_tree_t * 720 smb_tree_connect_ipc(smb_request_t *sr, const char *name) 721 { 722 smb_user_t *user = sr->uid_user; 723 smb_tree_t *tree; 724 smb_share_t *si; 725 726 ASSERT(user); 727 728 if ((user->u_flags & SMB_USER_FLAG_IPC) && 729 sr->sr_cfg->skc_restrict_anon) { 730 smb_tree_log(sr, name, "access denied: restrict anonymous"); 731 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess); 732 return (NULL); 733 } 734 735 sr->arg.tcon.optional_support = SMB_SUPPORT_SEARCH_BITS; 736 737 si = kmem_zalloc(sizeof (smb_share_t), KM_SLEEP); 738 (void) strlcpy(si->shr_name, name, MAXNAMELEN); 739 (void) strlcpy(si->shr_path, name, MAXPATHLEN); 740 si->shr_type = STYPE_IPC | STYPE_SPECIAL; 741 742 tree = smb_tree_alloc(user, si, STYPE_IPC, NULL, ACE_ALL_PERMS); 743 if (tree == NULL) { 744 smb_tree_log(sr, name, "access denied"); 745 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess); 746 } 747 748 kmem_free(si, sizeof (smb_share_t)); 749 return (tree); 750 } 751 752 /* 753 * Allocate a tree. 754 */ 755 static smb_tree_t * 756 smb_tree_alloc( 757 smb_user_t *user, 758 const smb_share_t *si, 759 int32_t stype, 760 smb_node_t *snode, 761 uint32_t access) 762 { 763 smb_tree_t *tree; 764 uint16_t tid; 765 766 if (smb_idpool_alloc(&user->u_tid_pool, &tid)) 767 return (NULL); 768 769 tree = kmem_cache_alloc(user->u_server->si_cache_tree, KM_SLEEP); 770 bzero(tree, sizeof (smb_tree_t)); 771 772 if (STYPE_ISDSK(stype)) { 773 if (smb_tree_getattr(si, snode, tree) != 0) { 774 smb_idpool_free(&user->u_tid_pool, tid); 775 kmem_cache_free(user->u_server->si_cache_tree, tree); 776 return (NULL); 777 } 778 } 779 780 if (smb_idpool_constructor(&tree->t_fid_pool)) { 781 smb_idpool_free(&user->u_tid_pool, tid); 782 kmem_cache_free(user->u_server->si_cache_tree, tree); 783 return (NULL); 784 } 785 786 if (smb_idpool_constructor(&tree->t_odid_pool)) { 787 smb_idpool_destructor(&tree->t_fid_pool); 788 smb_idpool_free(&user->u_tid_pool, tid); 789 kmem_cache_free(user->u_server->si_cache_tree, tree); 790 return (NULL); 791 } 792 793 smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t), 794 offsetof(smb_ofile_t, f_lnd)); 795 796 smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t), 797 offsetof(smb_odir_t, d_lnd)); 798 799 (void) strlcpy(tree->t_sharename, si->shr_name, 800 sizeof (tree->t_sharename)); 801 (void) strlcpy(tree->t_resource, si->shr_path, 802 sizeof (tree->t_resource)); 803 804 mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL); 805 806 tree->t_user = user; 807 tree->t_session = user->u_session; 808 tree->t_server = user->u_server; 809 tree->t_refcnt = 1; 810 tree->t_tid = tid; 811 tree->t_res_type = stype; 812 tree->t_state = SMB_TREE_STATE_CONNECTED; 813 tree->t_magic = SMB_TREE_MAGIC; 814 tree->t_access = access; 815 tree->t_connect_time = gethrestime_sec(); 816 817 /* if FS is readonly, enforce that here */ 818 if (tree->t_flags & SMB_TREE_READONLY) 819 tree->t_access &= ~ACE_ALL_WRITE_PERMS; 820 821 if (STYPE_ISDSK(stype)) { 822 smb_node_ref(snode); 823 tree->t_snode = snode; 824 tree->t_acltype = smb_fsop_acltype(snode); 825 } 826 827 smb_llist_enter(&user->u_tree_list, RW_WRITER); 828 smb_llist_insert_head(&user->u_tree_list, tree); 829 smb_llist_exit(&user->u_tree_list); 830 atomic_inc_32(&user->u_session->s_tree_cnt); 831 atomic_inc_32(&user->u_server->sv_open_trees); 832 833 return (tree); 834 } 835 836 /* 837 * Deallocate a tree. The open file and open directory lists should be 838 * empty. 839 * 840 * Remove the tree from the user's tree list before freeing resources 841 * associated with the tree. 842 */ 843 void 844 smb_tree_dealloc(void *arg) 845 { 846 smb_user_t *user; 847 smb_tree_t *tree = (smb_tree_t *)arg; 848 849 SMB_TREE_VALID(tree); 850 ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED); 851 ASSERT(tree->t_refcnt == 0); 852 853 user = tree->t_user; 854 smb_llist_enter(&user->u_tree_list, RW_WRITER); 855 smb_llist_remove(&user->u_tree_list, tree); 856 smb_idpool_free(&user->u_tid_pool, tree->t_tid); 857 atomic_dec_32(&tree->t_session->s_tree_cnt); 858 smb_llist_exit(&user->u_tree_list); 859 860 mutex_enter(&tree->t_mutex); 861 mutex_exit(&tree->t_mutex); 862 863 tree->t_magic = (uint32_t)~SMB_TREE_MAGIC; 864 865 if (tree->t_snode) 866 smb_node_release(tree->t_snode); 867 868 mutex_destroy(&tree->t_mutex); 869 smb_llist_destructor(&tree->t_ofile_list); 870 smb_llist_destructor(&tree->t_odir_list); 871 smb_idpool_destructor(&tree->t_fid_pool); 872 smb_idpool_destructor(&tree->t_odid_pool); 873 kmem_cache_free(tree->t_server->si_cache_tree, tree); 874 } 875 876 /* 877 * Determine whether or not a tree is connected. 878 * This function must be called with the tree mutex held. 879 */ 880 static boolean_t 881 smb_tree_is_connected_locked(smb_tree_t *tree) 882 { 883 switch (tree->t_state) { 884 case SMB_TREE_STATE_CONNECTED: 885 return (B_TRUE); 886 887 case SMB_TREE_STATE_DISCONNECTING: 888 case SMB_TREE_STATE_DISCONNECTED: 889 /* 890 * The tree exists but being diconnected or destroyed. 891 */ 892 return (B_FALSE); 893 894 default: 895 ASSERT(0); 896 return (B_FALSE); 897 } 898 } 899 900 /* 901 * Determine whether or not a tree is disconnected. 902 * This function must be called with the tree mutex held. 903 */ 904 static boolean_t 905 smb_tree_is_disconnected(smb_tree_t *tree) 906 { 907 switch (tree->t_state) { 908 case SMB_TREE_STATE_DISCONNECTED: 909 return (B_TRUE); 910 911 case SMB_TREE_STATE_CONNECTED: 912 case SMB_TREE_STATE_DISCONNECTING: 913 return (B_FALSE); 914 915 default: 916 ASSERT(0); 917 return (B_FALSE); 918 } 919 } 920 921 /* 922 * Return a pointer to the share name within a share resource path. 923 * 924 * The share path may be a Uniform Naming Convention (UNC) string 925 * (\\server\share) or simply the share name. We validate the UNC 926 * format but we don't look at the server name. 927 */ 928 static const char * 929 smb_tree_get_sharename(const char *unc_path) 930 { 931 const char *sharename = unc_path; 932 933 if (sharename[0] == '\\') { 934 /* 935 * Looks like a UNC path, validate the format. 936 */ 937 if (sharename[1] != '\\') 938 return (NULL); 939 940 if ((sharename = strchr(sharename+2, '\\')) == NULL) 941 return (NULL); 942 943 ++sharename; 944 } else if (strchr(sharename, '\\') != NULL) { 945 /* 946 * This should be a share name (no embedded \'s). 947 */ 948 return (NULL); 949 } 950 951 return (sharename); 952 } 953 954 /* 955 * Map the service to a resource type. Valid values for service are: 956 * 957 * A: Disk share 958 * LPT1: Printer 959 * IPC Named pipe 960 * COMM Communications device 961 * ????? Any type of device (wildcard) 962 * 963 * We support IPC and disk shares; anything else is currently treated 964 * as an error. IPC$ is reserved as the named pipe share. 965 */ 966 static int 967 smb_tree_get_stype(const char *sharename, const char *service, 968 int32_t *stype_ret) 969 { 970 const char *any = "?????"; 971 972 if ((strcmp(service, any) == 0) || (strcasecmp(service, "IPC") == 0)) { 973 if (strcasecmp(sharename, "IPC$") == 0) { 974 *stype_ret = STYPE_IPC; 975 return (0); 976 } 977 } 978 979 if ((strcmp(service, any) == 0) || (strcasecmp(service, "A:") == 0)) { 980 if (strcasecmp(sharename, "IPC$") == 0) 981 return (-1); 982 983 *stype_ret = STYPE_DISKTREE; 984 return (0); 985 } 986 987 return (-1); 988 } 989 990 /* 991 * Obtain the tree attributes: volume name, typename and flags. 992 */ 993 static int 994 smb_tree_getattr(const smb_share_t *si, smb_node_t *node, smb_tree_t *tree) 995 { 996 vfs_t *vfsp = SMB_NODE_VFS(node); 997 998 ASSERT(vfsp); 999 1000 if (getvfs(&vfsp->vfs_fsid) != vfsp) 1001 return (ESTALE); 1002 1003 smb_tree_get_volname(vfsp, tree); 1004 smb_tree_get_flags(si, vfsp, tree); 1005 1006 VFS_RELE(vfsp); 1007 return (0); 1008 } 1009 1010 /* 1011 * Extract the volume name. 1012 */ 1013 static void 1014 smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree) 1015 { 1016 refstr_t *vfs_mntpoint; 1017 const char *s; 1018 char *name; 1019 1020 vfs_mntpoint = vfs_getmntpoint(vfsp); 1021 1022 s = vfs_mntpoint->rs_string; 1023 s += strspn(s, "/"); 1024 (void) strlcpy(tree->t_volume, s, SMB_VOLNAMELEN); 1025 1026 refstr_rele(vfs_mntpoint); 1027 1028 name = tree->t_volume; 1029 (void) strsep((char **)&name, "/"); 1030 } 1031 1032 /* 1033 * Always set ACL support because the VFS will fake ACLs for file systems 1034 * that don't support them. 1035 * 1036 * Some flags are dependent on the typename, which is also set up here. 1037 * File system types are hardcoded in uts/common/os/vfs_conf.c. 1038 */ 1039 static void 1040 smb_tree_get_flags(const smb_share_t *si, vfs_t *vfsp, smb_tree_t *tree) 1041 { 1042 typedef struct smb_mtype { 1043 char *mt_name; 1044 size_t mt_namelen; 1045 uint32_t mt_flags; 1046 } smb_mtype_t; 1047 1048 static smb_mtype_t smb_mtype[] = { 1049 { "zfs", 3, SMB_TREE_UNICODE_ON_DISK | SMB_TREE_QUOTA }, 1050 { "ufs", 3, SMB_TREE_UNICODE_ON_DISK }, 1051 { "nfs", 3, SMB_TREE_NFS_MOUNTED }, 1052 { "tmpfs", 5, SMB_TREE_NO_EXPORT } 1053 }; 1054 smb_mtype_t *mtype; 1055 char *name; 1056 uint32_t flags = SMB_TREE_SUPPORTS_ACLS; 1057 int i; 1058 1059 if (si->shr_flags & SMB_SHRF_DFSROOT) 1060 flags |= SMB_TREE_DFSROOT; 1061 1062 if (si->shr_flags & SMB_SHRF_CATIA) 1063 flags |= SMB_TREE_CATIA; 1064 1065 if (si->shr_flags & SMB_SHRF_ABE) 1066 flags |= SMB_TREE_ABE; 1067 1068 if (vfsp->vfs_flag & VFS_RDONLY) 1069 flags |= SMB_TREE_READONLY; 1070 1071 if (vfsp->vfs_flag & VFS_XATTR) 1072 flags |= SMB_TREE_STREAMS; 1073 1074 if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) 1075 flags |= SMB_TREE_NO_ATIME; 1076 1077 name = vfssw[vfsp->vfs_fstype].vsw_name; 1078 1079 for (i = 0; i < sizeof (smb_mtype) / sizeof (smb_mtype[0]); ++i) { 1080 mtype = &smb_mtype[i]; 1081 if (strncasecmp(name, mtype->mt_name, mtype->mt_namelen) == 0) 1082 flags |= mtype->mt_flags; 1083 } 1084 1085 (void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN); 1086 (void) smb_strupr((char *)tree->t_typename); 1087 1088 if (vfs_has_feature(vfsp, VFSFT_XVATTR)) 1089 flags |= SMB_TREE_XVATTR; 1090 1091 if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE)) 1092 flags |= SMB_TREE_CASEINSENSITIVE; 1093 1094 if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE)) 1095 flags |= SMB_TREE_NO_CASESENSITIVE; 1096 1097 if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS)) 1098 flags |= SMB_TREE_DIRENTFLAGS; 1099 1100 if (vfs_has_feature(vfsp, VFSFT_ACLONCREATE)) 1101 flags |= SMB_TREE_ACLONCREATE; 1102 1103 if (vfs_has_feature(vfsp, VFSFT_ACEMASKONACCESS)) 1104 flags |= SMB_TREE_ACEMASKONACCESS; 1105 1106 DTRACE_PROBE2(smb__tree__flags, uint32_t, flags, char *, name); 1107 1108 1109 tree->t_flags = flags; 1110 } 1111 1112 /* 1113 * Report share access result to syslog. 1114 */ 1115 static void 1116 smb_tree_log(smb_request_t *sr, const char *sharename, const char *fmt, ...) 1117 { 1118 va_list ap; 1119 char buf[128]; 1120 smb_user_t *user = sr->uid_user; 1121 1122 ASSERT(user); 1123 1124 if (smb_tcon_mute) 1125 return; 1126 1127 if ((user->u_name) && (strcasecmp(sharename, "IPC$") == 0)) { 1128 /* 1129 * Only report normal users, i.e. ignore W2K misuse 1130 * of the IPC connection by filtering out internal 1131 * names such as nobody and root. 1132 */ 1133 if ((strcmp(user->u_name, "root") == 0) || 1134 (strcmp(user->u_name, "nobody") == 0)) { 1135 return; 1136 } 1137 } 1138 1139 va_start(ap, fmt); 1140 (void) vsnprintf(buf, 128, fmt, ap); 1141 va_end(ap); 1142 1143 cmn_err(CE_NOTE, "smbd[%s\\%s]: %s %s", 1144 user->u_domain, user->u_name, sharename, buf); 1145 } 1146 1147 /* 1148 * smb_tree_lookup_odir 1149 * 1150 * Find the specified odir in the tree's list of odirs, and 1151 * attempt to obtain a hold on the odir. 1152 * 1153 * Returns NULL if odir not found or a hold cannot be obtained. 1154 */ 1155 smb_odir_t * 1156 smb_tree_lookup_odir(smb_tree_t *tree, uint16_t odid) 1157 { 1158 smb_odir_t *od; 1159 smb_llist_t *od_list; 1160 1161 ASSERT(tree); 1162 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 1163 1164 od_list = &tree->t_odir_list; 1165 smb_llist_enter(od_list, RW_READER); 1166 1167 od = smb_llist_head(od_list); 1168 while (od) { 1169 if (od->d_odid == odid) { 1170 if (!smb_odir_hold(od)) 1171 od = NULL; 1172 break; 1173 } 1174 od = smb_llist_next(od_list, od); 1175 } 1176 1177 smb_llist_exit(od_list); 1178 return (od); 1179 } 1180 1181 boolean_t 1182 smb_tree_is_connected(smb_tree_t *tree) 1183 { 1184 boolean_t rb; 1185 1186 mutex_enter(&tree->t_mutex); 1187 rb = smb_tree_is_connected_locked(tree); 1188 mutex_exit(&tree->t_mutex); 1189 return (rb); 1190 } 1191 1192 /* 1193 * Get the next open ofile in the list. A reference is taken on 1194 * the ofile, which can be released later with smb_ofile_release(). 1195 * 1196 * If the specified ofile is NULL, search from the beginning of the 1197 * list. Otherwise, the search starts just after that ofile. 1198 * 1199 * Returns NULL if there are no open files in the list. 1200 */ 1201 static smb_ofile_t * 1202 smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of) 1203 { 1204 smb_llist_t *ofile_list; 1205 1206 ASSERT(tree); 1207 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 1208 1209 ofile_list = &tree->t_ofile_list; 1210 smb_llist_enter(ofile_list, RW_READER); 1211 1212 if (of) { 1213 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 1214 of = smb_llist_next(ofile_list, of); 1215 } else { 1216 of = smb_llist_head(ofile_list); 1217 } 1218 1219 while (of) { 1220 if (smb_ofile_hold(of)) 1221 break; 1222 1223 of = smb_llist_next(ofile_list, of); 1224 } 1225 1226 smb_llist_exit(ofile_list); 1227 return (of); 1228 } 1229 1230 /* 1231 * smb_tree_get_odir 1232 * 1233 * Find the next odir in the tree's list of odirs, and obtain a 1234 * hold on it. 1235 * If the specified odir is NULL the search starts at the beginning 1236 * of the tree's odir list, otherwise the search starts after the 1237 * specified odir. 1238 */ 1239 static smb_odir_t * 1240 smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od) 1241 { 1242 smb_llist_t *od_list; 1243 1244 ASSERT(tree); 1245 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 1246 1247 od_list = &tree->t_odir_list; 1248 smb_llist_enter(od_list, RW_READER); 1249 1250 if (od) { 1251 ASSERT(od->d_magic == SMB_ODIR_MAGIC); 1252 od = smb_llist_next(od_list, od); 1253 } else { 1254 od = smb_llist_head(od_list); 1255 } 1256 1257 while (od) { 1258 ASSERT(od->d_magic == SMB_ODIR_MAGIC); 1259 1260 if (smb_odir_hold(od)) 1261 break; 1262 od = smb_llist_next(od_list, od); 1263 } 1264 1265 smb_llist_exit(od_list); 1266 return (od); 1267 } 1268 1269 /* 1270 * smb_tree_close_odirs 1271 * 1272 * Close all open odirs in the tree's list which were opened by 1273 * the process identified by pid. 1274 * If pid is zero, close all open odirs in the tree's list. 1275 */ 1276 static void 1277 smb_tree_close_odirs(smb_tree_t *tree, uint16_t pid) 1278 { 1279 smb_odir_t *od, *next_od; 1280 1281 ASSERT(tree); 1282 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 1283 1284 od = smb_tree_get_odir(tree, NULL); 1285 while (od) { 1286 ASSERT(od->d_magic == SMB_ODIR_MAGIC); 1287 ASSERT(od->d_tree == tree); 1288 1289 next_od = smb_tree_get_odir(tree, od); 1290 if ((pid == 0) || (od->d_opened_by_pid == pid)) 1291 smb_odir_close(od); 1292 smb_odir_release(od); 1293 1294 od = next_od; 1295 } 1296 } 1297 1298 static void 1299 smb_tree_set_execsub_info(smb_tree_t *tree, smb_execsub_info_t *subs) 1300 { 1301 subs->e_winname = tree->t_user->u_name; 1302 subs->e_userdom = tree->t_user->u_domain; 1303 subs->e_srv_ipaddr = tree->t_session->local_ipaddr; 1304 subs->e_cli_ipaddr = tree->t_session->ipaddr; 1305 subs->e_cli_netbiosname = tree->t_session->workstation; 1306 subs->e_uid = crgetuid(tree->t_user->u_cred); 1307 } 1308 1309 /* 1310 * Private function to support smb_tree_enum. 1311 */ 1312 static int 1313 smb_tree_enum_private(smb_tree_t *tree, smb_svcenum_t *svcenum) 1314 { 1315 uint8_t *pb; 1316 uint_t nbytes; 1317 int rc; 1318 1319 if (svcenum->se_nskip > 0) { 1320 svcenum->se_nskip--; 1321 return (0); 1322 } 1323 1324 if (svcenum->se_nitems >= svcenum->se_nlimit) { 1325 svcenum->se_nitems = svcenum->se_nlimit; 1326 return (0); 1327 } 1328 1329 pb = &svcenum->se_buf[svcenum->se_bused]; 1330 rc = smb_tree_netinfo_encode(tree, pb, svcenum->se_bavail, &nbytes); 1331 if (rc == 0) { 1332 svcenum->se_bavail -= nbytes; 1333 svcenum->se_bused += nbytes; 1334 svcenum->se_nitems++; 1335 } 1336 1337 return (rc); 1338 } 1339 1340 /* 1341 * Encode connection information into a buffer: connection information 1342 * needed in user space to support RPC requests. 1343 */ 1344 static int 1345 smb_tree_netinfo_encode(smb_tree_t *tree, uint8_t *buf, size_t buflen, 1346 uint32_t *nbytes) 1347 { 1348 smb_netconnectinfo_t info; 1349 int rc; 1350 1351 smb_tree_netinfo_init(tree, &info); 1352 rc = smb_netconnectinfo_encode(&info, buf, buflen, nbytes); 1353 smb_tree_netinfo_fini(&info); 1354 1355 return (rc); 1356 } 1357 1358 /* 1359 * Note: ci_numusers should be the number of users connected to 1360 * the share rather than the number of references on the tree but 1361 * we don't have a mechanism to track users/share in smbsrv yet. 1362 */ 1363 static void 1364 smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *info) 1365 { 1366 smb_user_t *user; 1367 1368 ASSERT(tree); 1369 1370 info->ci_id = tree->t_tid; 1371 info->ci_type = tree->t_res_type; 1372 info->ci_numopens = tree->t_open_files; 1373 info->ci_numusers = tree->t_refcnt; 1374 info->ci_time = gethrestime_sec() - tree->t_connect_time; 1375 1376 info->ci_sharelen = strlen(tree->t_sharename) + 1; 1377 info->ci_share = smb_mem_strdup(tree->t_sharename); 1378 1379 user = tree->t_user; 1380 ASSERT(user); 1381 1382 info->ci_namelen = user->u_domain_len + user->u_name_len + 2; 1383 info->ci_username = kmem_alloc(info->ci_namelen, KM_SLEEP); 1384 (void) snprintf(info->ci_username, info->ci_namelen, "%s\\%s", 1385 user->u_domain, user->u_name); 1386 } 1387 1388 static void 1389 smb_tree_netinfo_fini(smb_netconnectinfo_t *info) 1390 { 1391 if (info == NULL) 1392 return; 1393 1394 if (info->ci_username) 1395 kmem_free(info->ci_username, info->ci_namelen); 1396 if (info->ci_share) 1397 smb_mem_free(info->ci_share); 1398 1399 bzero(info, sizeof (smb_netconnectinfo_t)); 1400 } 1401