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