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