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