xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_tree.c (revision 9b4e3ac25d882519cad3fc11f0c53b07f4e60536)
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 2008 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 <smbsrv/smb_incl.h>
173 #include <smbsrv/lmerr.h>
174 #include <smbsrv/smb_fsops.h>
175 #include <smbsrv/smb_door_svc.h>
176 #include <smbsrv/smb_share.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 char *, const char *,
183     int32_t, smb_node_t *);
184 static void smb_tree_dealloc(smb_tree_t *);
185 static boolean_t smb_tree_is_connected(smb_tree_t *);
186 static boolean_t smb_tree_is_disconnected(smb_tree_t *);
187 static const char *smb_tree_get_sharename(const char *);
188 static int smb_tree_get_stype(const char *, const char *, int32_t *);
189 static int smb_tree_getattr(smb_node_t *, smb_tree_t *);
190 static void smb_tree_get_volname(vfs_t *, smb_tree_t *);
191 static void smb_tree_get_flags(vfs_t *, smb_tree_t *);
192 static void smb_tree_log(smb_request_t *, const char *, const char *, ...);
193 
194 /*
195  * Extract the share name and share type and connect as appropriate.
196  * Share names are case insensitive so we map the share name to
197  * lower-case as a convenience for internal processing.
198  */
199 smb_tree_t *
200 smb_tree_connect(smb_request_t *sr)
201 {
202 	char *unc_path = sr->arg.tcon.path;
203 	char *service = sr->arg.tcon.service;
204 	smb_tree_t *tree = NULL;
205 	const char *name;
206 	int32_t stype;
207 
208 	(void) utf8_strlwr(unc_path);
209 
210 	if ((name = smb_tree_get_sharename(unc_path)) == NULL) {
211 		smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
212 		return (NULL);
213 	}
214 
215 	if (smb_tree_get_stype(name, service, &stype) != 0) {
216 		smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
217 		    ERRDOS, ERROR_BAD_DEV_TYPE);
218 		return (NULL);
219 	}
220 
221 	switch (stype & STYPE_MASK) {
222 	case STYPE_DISKTREE:
223 		tree = smb_tree_connect_disk(sr, name);
224 		break;
225 
226 	case STYPE_IPC:
227 		tree = smb_tree_connect_ipc(sr, name);
228 		break;
229 
230 	default:
231 		smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
232 		    ERRDOS, ERROR_BAD_DEV_TYPE);
233 		break;
234 	}
235 
236 	return (tree);
237 }
238 
239 /*
240  * Disconnect a tree.
241  */
242 void
243 smb_tree_disconnect(
244     smb_tree_t	*tree)
245 {
246 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
247 
248 	mutex_enter(&tree->t_mutex);
249 	ASSERT(tree->t_refcnt);
250 
251 	if (smb_tree_is_connected(tree)) {
252 		/*
253 		 * Indicate that the disconnect process has started.
254 		 */
255 		tree->t_state = SMB_TREE_STATE_DISCONNECTING;
256 		mutex_exit(&tree->t_mutex);
257 		atomic_dec_32(&tree->t_server->sv_open_trees);
258 
259 		/*
260 		 * The files opened under this tree are closed.
261 		 */
262 		smb_ofile_close_all(tree);
263 		/*
264 		 * The directories opened under this tree are closed.
265 		 */
266 		smb_odir_close_all(tree);
267 		mutex_enter(&tree->t_mutex);
268 		tree->t_state = SMB_TREE_STATE_DISCONNECTED;
269 	}
270 
271 	mutex_exit(&tree->t_mutex);
272 }
273 
274 /*
275  * Take a reference on a tree.
276  */
277 boolean_t
278 smb_tree_hold(
279     smb_tree_t		*tree)
280 {
281 	ASSERT(tree);
282 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
283 
284 	mutex_enter(&tree->t_mutex);
285 
286 	if (smb_tree_is_connected(tree)) {
287 		tree->t_refcnt++;
288 		mutex_exit(&tree->t_mutex);
289 		return (B_TRUE);
290 	}
291 
292 	mutex_exit(&tree->t_mutex);
293 	return (B_FALSE);
294 }
295 
296 /*
297  * Release a reference on a tree.  If the tree is disconnected and the
298  * reference count falls to zero, the tree will be deallocated.
299  */
300 void
301 smb_tree_release(
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 	ASSERT(tree->t_refcnt);
309 	tree->t_refcnt--;
310 
311 	if (smb_tree_is_disconnected(tree) && (tree->t_refcnt == 0)) {
312 		mutex_exit(&tree->t_mutex);
313 		smb_tree_dealloc(tree);
314 		return;
315 	}
316 
317 	mutex_exit(&tree->t_mutex);
318 }
319 
320 /*
321  * Close ofiles and odirs that match pid.
322  */
323 void
324 smb_tree_close_pid(
325     smb_tree_t		*tree,
326     uint16_t		pid)
327 {
328 	ASSERT(tree);
329 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
330 
331 	smb_ofile_close_all_by_pid(tree, pid);
332 	smb_odir_close_all_by_pid(tree, pid);
333 }
334 
335 /*
336  * Check whether or not a tree supports the features identified by flags.
337  */
338 boolean_t
339 smb_tree_has_feature(smb_tree_t *tree, uint32_t flags)
340 {
341 	ASSERT(tree);
342 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
343 
344 	return ((tree->t_flags & flags) == flags);
345 }
346 
347 /* *************************** Static Functions ***************************** */
348 
349 /*
350  * Connect a share for use with files and directories.
351  */
352 static smb_tree_t *
353 smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
354 {
355 	smb_user_t		*user = sr->uid_user;
356 	smb_node_t		*dir_snode = NULL;
357 	smb_node_t		*snode = NULL;
358 	char			last_component[MAXNAMELEN];
359 	smb_tree_t		*tree;
360 	smb_share_t 		*si;
361 	smb_attr_t		attr;
362 	cred_t			*u_cred;
363 	int			rc;
364 	uint32_t		access = 0; /* read/write is assumed */
365 	uint32_t		hostaccess;
366 
367 	ASSERT(user);
368 	u_cred = user->u_cred;
369 	ASSERT(u_cred);
370 
371 	if (user->u_flags & SMB_USER_FLAG_IPC) {
372 		smb_tree_log(sr, sharename, "access denied: IPC only");
373 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
374 		return (NULL);
375 	}
376 
377 	si = kmem_zalloc(sizeof (smb_share_t), KM_SLEEP);
378 
379 	if (smb_kshare_getinfo(sr->sr_server->sv_lmshrd, (char *)sharename, si,
380 	    sr->session->ipaddr) != NERR_Success) {
381 		smb_tree_log(sr, sharename, "share not found");
382 		smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
383 		kmem_free(si, sizeof (smb_share_t));
384 		return (NULL);
385 	}
386 
387 	/*
388 	 * Handle the default administration shares: C$, D$ etc.
389 	 * Only a user with admin rights is allowed to map these
390 	 * shares.
391 	 */
392 	if (si->shr_flags & SMB_SHRF_ADMIN) {
393 		if (!smb_user_is_admin(user)) {
394 			smb_tree_log(sr, sharename, "access denied: not admin");
395 			smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
396 			    ERRSRV, ERRaccess);
397 			kmem_free(si, sizeof (smb_share_t));
398 			return (NULL);
399 		}
400 	}
401 
402 	/*
403 	 * Set up the OptionalSupport for this share.
404 	 */
405 	sr->arg.tcon.optional_support = SMB_SUPPORT_SEARCH_BITS;
406 
407 	switch (si->shr_flags & SMB_SHRF_CSC_MASK) {
408 	case SMB_SHRF_CSC_DISABLED:
409 		sr->arg.tcon.optional_support |= SMB_CSC_CACHE_NONE;
410 		break;
411 	case SMB_SHRF_CSC_AUTO:
412 		sr->arg.tcon.optional_support |= SMB_CSC_CACHE_AUTO_REINT;
413 		break;
414 	case SMB_SHRF_CSC_VDO:
415 		sr->arg.tcon.optional_support |= SMB_CSC_CACHE_VDO;
416 		break;
417 	case SMB_SHRF_CSC_MANUAL:
418 	default:
419 		/*
420 		 * Default to SMB_CSC_CACHE_MANUAL_REINT.
421 		 */
422 		break;
423 	}
424 
425 	hostaccess = si->shr_access_value & SMB_SHRF_ACC_ALL;
426 
427 	if (hostaccess == SMB_SHRF_ACC_RO) {
428 		access = SMB_TREE_READONLY;
429 	} else if (hostaccess == SMB_SHRF_ACC_NONE) {
430 		kmem_free(si, sizeof (smb_share_t));
431 		smb_tree_log(sr, sharename, "access denied: host access");
432 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
433 		return (NULL);
434 	}
435 
436 	/*
437 	 * Check that the shared directory exists.
438 	 */
439 	rc = smb_pathname_reduce(sr, u_cred, si->shr_path, 0, 0, &dir_snode,
440 	    last_component);
441 
442 	if (rc == 0) {
443 		rc = smb_fsop_lookup(sr, u_cred, SMB_FOLLOW_LINKS, 0,
444 		    dir_snode, last_component, &snode, &attr, 0, 0);
445 
446 		smb_node_release(dir_snode);
447 	}
448 
449 	if (rc) {
450 		if (snode)
451 			smb_node_release(snode);
452 
453 		smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
454 		smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
455 		kmem_free(si, sizeof (smb_share_t));
456 		return (NULL);
457 	}
458 
459 	tree = smb_tree_alloc(user, sharename, si->shr_path, STYPE_DISKTREE,
460 	    snode);
461 
462 	if (tree == NULL)
463 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
464 	else
465 		tree->t_flags |= access;
466 
467 	smb_node_release(snode);
468 	kmem_free(si, sizeof (smb_share_t));
469 	return (tree);
470 }
471 
472 /*
473  * Connect an IPC share for use with named pipes.
474  */
475 static smb_tree_t *
476 smb_tree_connect_ipc(smb_request_t *sr, const char *name)
477 {
478 	smb_user_t *user = sr->uid_user;
479 	smb_tree_t *tree;
480 
481 	ASSERT(user);
482 
483 	if ((user->u_flags & SMB_USER_FLAG_IPC) &&
484 	    sr->sr_cfg->skc_restrict_anon) {
485 		smb_tree_log(sr, name, "access denied: restrict anonymous");
486 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
487 		return (NULL);
488 	}
489 
490 	sr->arg.tcon.optional_support = SMB_SUPPORT_SEARCH_BITS;
491 
492 	tree = smb_tree_alloc(user, name, name, STYPE_IPC, NULL);
493 	if (tree == NULL) {
494 		smb_tree_log(sr, name, "access denied");
495 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
496 	}
497 
498 	return (tree);
499 }
500 
501 /*
502  * Allocate a tree.
503  */
504 static smb_tree_t *
505 smb_tree_alloc(
506     smb_user_t		*user,
507     const char		*sharename,
508     const char		*resource,
509     int32_t		stype,
510     smb_node_t		*snode)
511 {
512 	smb_tree_t	*tree;
513 	uint16_t	tid;
514 
515 	if (smb_idpool_alloc(&user->u_tid_pool, &tid))
516 		return (NULL);
517 
518 	tree = kmem_cache_alloc(user->u_server->si_cache_tree, KM_SLEEP);
519 	bzero(tree, sizeof (smb_tree_t));
520 
521 	if (STYPE_ISDSK(stype)) {
522 		if (smb_tree_getattr(snode, tree) != 0) {
523 			smb_idpool_free(&user->u_tid_pool, tid);
524 			kmem_cache_free(user->u_server->si_cache_tree, tree);
525 			return (NULL);
526 		}
527 	}
528 
529 	if (smb_idpool_constructor(&tree->t_fid_pool)) {
530 		smb_idpool_free(&user->u_tid_pool, tid);
531 		kmem_cache_free(user->u_server->si_cache_tree, tree);
532 		return (NULL);
533 	}
534 
535 	if (smb_idpool_constructor(&tree->t_sid_pool)) {
536 		smb_idpool_destructor(&tree->t_fid_pool);
537 		smb_idpool_free(&user->u_tid_pool, tid);
538 		kmem_cache_free(user->u_server->si_cache_tree, tree);
539 		return (NULL);
540 	}
541 
542 	smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t),
543 	    offsetof(smb_ofile_t, f_lnd));
544 
545 	smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t),
546 	    offsetof(smb_odir_t, d_lnd));
547 
548 	(void) strlcpy(tree->t_sharename, sharename,
549 	    sizeof (tree->t_sharename));
550 	(void) strlcpy(tree->t_resource, resource, sizeof (tree->t_resource));
551 
552 	mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL);
553 
554 	tree->t_user = user;
555 	tree->t_session = user->u_session;
556 	tree->t_server = user->u_server;
557 	tree->t_refcnt = 1;
558 	tree->t_tid = tid;
559 	tree->t_res_type = stype;
560 	tree->t_state = SMB_TREE_STATE_CONNECTED;
561 	tree->t_magic = SMB_TREE_MAGIC;
562 
563 	if (STYPE_ISDSK(stype)) {
564 		smb_node_ref(snode);
565 		tree->t_snode = snode;
566 		tree->t_acltype = smb_fsop_acltype(snode);
567 	}
568 
569 	smb_llist_enter(&user->u_tree_list, RW_WRITER);
570 	smb_llist_insert_head(&user->u_tree_list, tree);
571 	smb_llist_exit(&user->u_tree_list);
572 	atomic_inc_32(&user->u_session->s_tree_cnt);
573 	atomic_inc_32(&user->u_server->sv_open_trees);
574 
575 	return (tree);
576 }
577 
578 /*
579  * Deallocate a tree: release all resources associated with a tree and
580  * remove the tree from the user's tree list.
581  *
582  * The tree being destroyed must be in the "destroying" state and the
583  * reference count must be zero. This function assumes it's single threaded
584  * i.e. only one thread will attempt to destroy a specific tree, which
585  * should be the case if the tree is in disconnected and has a reference
586  * count of zero.
587  */
588 static void
589 smb_tree_dealloc(smb_tree_t *tree)
590 {
591 	ASSERT(tree);
592 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
593 	ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
594 	ASSERT(tree->t_refcnt == 0);
595 
596 	/*
597 	 * Remove the tree from the user's tree list.  This must be done
598 	 * before any resources associated with the tree are released.
599 	 */
600 	smb_llist_enter(&tree->t_user->u_tree_list, RW_WRITER);
601 	smb_llist_remove(&tree->t_user->u_tree_list, tree);
602 	smb_llist_exit(&tree->t_user->u_tree_list);
603 
604 	tree->t_magic = (uint32_t)~SMB_TREE_MAGIC;
605 	smb_idpool_free(&tree->t_user->u_tid_pool, tree->t_tid);
606 	atomic_dec_32(&tree->t_session->s_tree_cnt);
607 
608 	if (tree->t_snode)
609 		smb_node_release(tree->t_snode);
610 
611 	mutex_destroy(&tree->t_mutex);
612 
613 	/*
614 	 * The list of open files and open directories should be empty.
615 	 */
616 	smb_llist_destructor(&tree->t_ofile_list);
617 	smb_llist_destructor(&tree->t_odir_list);
618 	smb_idpool_destructor(&tree->t_fid_pool);
619 	smb_idpool_destructor(&tree->t_sid_pool);
620 	kmem_cache_free(tree->t_server->si_cache_tree, tree);
621 }
622 
623 /*
624  * Determine whether or not a tree is connected.
625  * This function must be called with the tree mutex held.
626  */
627 static boolean_t
628 smb_tree_is_connected(smb_tree_t *tree)
629 {
630 	switch (tree->t_state) {
631 	case SMB_TREE_STATE_CONNECTED:
632 		return (B_TRUE);
633 
634 	case SMB_TREE_STATE_DISCONNECTING:
635 	case SMB_TREE_STATE_DISCONNECTED:
636 		/*
637 		 * The tree exists but being diconnected or destroyed.
638 		 */
639 		return (B_FALSE);
640 
641 	default:
642 		ASSERT(0);
643 		return (B_FALSE);
644 	}
645 }
646 
647 /*
648  * Determine whether or not a tree is disconnected.
649  * This function must be called with the tree mutex held.
650  */
651 static boolean_t
652 smb_tree_is_disconnected(smb_tree_t *tree)
653 {
654 	switch (tree->t_state) {
655 	case SMB_TREE_STATE_DISCONNECTED:
656 		return (B_TRUE);
657 
658 	case SMB_TREE_STATE_CONNECTED:
659 	case SMB_TREE_STATE_DISCONNECTING:
660 		return (B_FALSE);
661 
662 	default:
663 		ASSERT(0);
664 		return (B_FALSE);
665 	}
666 }
667 
668 /*
669  * Return a pointer to the share name within a share resource path.
670  *
671  * The share path may be a Uniform Naming Convention (UNC) string
672  * (\\server\share) or simply the share name.  We validate the UNC
673  * format but we don't look at the server name.
674  */
675 static const char *
676 smb_tree_get_sharename(const char *unc_path)
677 {
678 	const char *sharename = unc_path;
679 
680 	if (sharename[0] == '\\') {
681 		/*
682 		 * Looks like a UNC path, validate the format.
683 		 */
684 		if (sharename[1] != '\\')
685 			return (NULL);
686 
687 		if ((sharename = strchr(sharename+2, '\\')) == NULL)
688 			return (NULL);
689 
690 		++sharename;
691 	} else if (strchr(sharename, '\\') != NULL) {
692 		/*
693 		 * This should be a share name (no embedded \'s).
694 		 */
695 		return (NULL);
696 	}
697 
698 	return (sharename);
699 }
700 
701 /*
702  * Map the service to a resource type.  Valid values for service are:
703  *
704  *	A:      Disk share
705  *	LPT1:   Printer
706  *	IPC     Named pipe
707  *	COMM    Communications device
708  *	?????   Any type of device (wildcard)
709  *
710  * We support IPC and disk shares; anything else is currently treated
711  * as an error.  IPC$ is reserved as the named pipe share.
712  */
713 static int
714 smb_tree_get_stype(const char *sharename, const char *service,
715     int32_t *stype_ret)
716 {
717 	const char *any = "?????";
718 
719 	if ((strcmp(service, any) == 0) || (strcasecmp(service, "IPC") == 0)) {
720 		if (strcasecmp(sharename, "IPC$") == 0) {
721 			*stype_ret = STYPE_IPC;
722 			return (0);
723 		}
724 	}
725 
726 	if ((strcmp(service, any) == 0) || (strcasecmp(service, "A:") == 0)) {
727 		if (strcasecmp(sharename, "IPC$") == 0)
728 			return (-1);
729 
730 		*stype_ret = STYPE_DISKTREE;
731 		return (0);
732 	}
733 
734 	return (-1);
735 }
736 
737 /*
738  * Obtain the tree attributes: volume name, typename and flags.
739  */
740 static int
741 smb_tree_getattr(smb_node_t *node, smb_tree_t *tree)
742 {
743 	vfs_t *vfsp = SMB_NODE_VFS(node);
744 
745 	ASSERT(vfsp);
746 
747 	if (getvfs(&vfsp->vfs_fsid) != vfsp)
748 		return (ESTALE);
749 
750 	smb_tree_get_volname(vfsp, tree);
751 	smb_tree_get_flags(vfsp, tree);
752 
753 	VFS_RELE(vfsp);
754 	return (0);
755 }
756 
757 /*
758  * Extract the volume name.
759  */
760 static void
761 smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree)
762 {
763 	refstr_t *vfs_mntpoint;
764 	const char *s;
765 	char *name;
766 
767 	vfs_mntpoint = vfs_getmntpoint(vfsp);
768 
769 	s = vfs_mntpoint->rs_string;
770 	s += strspn(s, "/");
771 	(void) strlcpy(tree->t_volume, s, SMB_VOLNAMELEN);
772 
773 	refstr_rele(vfs_mntpoint);
774 
775 	name = tree->t_volume;
776 	(void) strsep((char **)&name, "/");
777 }
778 
779 /*
780  * Always set ACL support because the VFS will fake ACLs for file systems
781  * that don't support them.
782  *
783  * Some flags are dependent on the typename, which is also set up here.
784  * File system types are hardcoded in uts/common/os/vfs_conf.c.
785  */
786 static void
787 smb_tree_get_flags(vfs_t *vfsp, smb_tree_t *tree)
788 {
789 	uint32_t flags = SMB_TREE_SUPPORTS_ACLS;
790 	char *name;
791 
792 	if (vfsp->vfs_flag & VFS_RDONLY)
793 		flags |= SMB_TREE_READONLY;
794 
795 	if (vfsp->vfs_flag & VFS_XATTR)
796 		flags |= SMB_TREE_STREAMS;
797 
798 	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
799 		flags |= SMB_TREE_NO_ATIME;
800 
801 	name = vfssw[vfsp->vfs_fstype].vsw_name;
802 
803 	if (strcmp(name, "tmpfs") == 0)
804 		flags |= SMB_TREE_NO_EXPORT;
805 
806 	if (strncasecmp(name, NFS, sizeof (NFS)) == 0)
807 		flags |= SMB_TREE_NFS_MOUNTED;
808 
809 	(void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN);
810 	(void) utf8_strupr((char *)tree->t_typename);
811 
812 	if (vfs_has_feature(vfsp, VFSFT_XVATTR))
813 		flags |= SMB_TREE_XVATTR;
814 
815 	if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE))
816 		flags |= SMB_TREE_CASEINSENSITIVE;
817 
818 	if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE))
819 		flags |= SMB_TREE_NO_CASESENSITIVE;
820 
821 	if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS))
822 		flags |= SMB_TREE_DIRENTFLAGS;
823 
824 	if (vfs_has_feature(vfsp, VFSFT_ACLONCREATE))
825 		flags |= SMB_TREE_ACLONCREATE;
826 
827 	if (vfs_has_feature(vfsp, VFSFT_ACEMASKONACCESS))
828 		flags |= SMB_TREE_ACEMASKONACCESS;
829 
830 	DTRACE_PROBE1(smb__tree__flags, uint32_t, flags);
831 
832 	tree->t_flags = flags;
833 }
834 
835 /*
836  * Report share access result to syslog.
837  */
838 static void
839 smb_tree_log(smb_request_t *sr, const char *sharename, const char *fmt, ...)
840 {
841 	va_list ap;
842 	char buf[128];
843 	smb_user_t *user = sr->uid_user;
844 
845 	ASSERT(user);
846 
847 	if (smb_tcon_mute)
848 		return;
849 
850 	if ((user->u_name) && (strcasecmp(sharename, "IPC$") == 0)) {
851 		/*
852 		 * Only report normal users, i.e. ignore W2K misuse
853 		 * of the IPC connection by filtering out internal
854 		 * names such as nobody and root.
855 		 */
856 		if ((strcmp(user->u_name, "root") == 0) ||
857 		    (strcmp(user->u_name, "nobody") == 0)) {
858 			return;
859 		}
860 	}
861 
862 	va_start(ap, fmt);
863 	(void) vsnprintf(buf, 128, fmt, ap);
864 	va_end(ap);
865 
866 	cmn_err(CE_NOTE, "smbd[%s\\%s]: %s %s",
867 	    user->u_domain, user->u_name, sharename, buf);
868 }
869