xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_node.c (revision 8670:6da349c3f817)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
22*8670SJose.Borrego@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
267348SJose.Borrego@Sun.COM #pragma ident	"@(#)smb_node.c	1.9	08/08/07 SMI"
275331Samw 
285331Samw /*
295331Samw  * SMB Node State Machine
305331Samw  * ----------------------
315331Samw  *
325331Samw  *    +----------------------------+	 T0
335331Samw  *    |  SMB_NODE_STATE_AVAILABLE  |<----------- Creation/Allocation
345331Samw  *    +----------------------------+
355331Samw  *		    |
365331Samw  *		    | T1
375331Samw  *		    |
385331Samw  *		    v
395331Samw  *    +-----------------------------+    T2
405331Samw  *    |  SMB_NODE_STATE_DESTROYING  |----------> Deletion/Free
415331Samw  *    +-----------------------------+
425331Samw  *
435331Samw  * Transition T0
445331Samw  *
455331Samw  *    This transition occurs in smb_node_lookup(). If the node looked for is
465331Samw  *    not found in the has table a new node is created. The reference count is
475331Samw  *    initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE.
485331Samw  *
495331Samw  * Transition T1
505331Samw  *
515331Samw  *    This transition occurs in smb_node_release(). If the reference count
525331Samw  *    drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more
535331Samw  *    reference count will be given out for that node.
545331Samw  *
555331Samw  * Transition T2
565331Samw  *
575331Samw  *    This transition occurs in smb_node_release(). The structure is deleted.
585331Samw  *
595331Samw  * Comments
605331Samw  * --------
615331Samw  *
625331Samw  *    The reason the smb node has 2 states is the following synchronization
635331Samw  *    rule:
645331Samw  *
655331Samw  *    There's a mutex embedded in the node used to protect its fields and
665331Samw  *    there's a lock embedded in the bucket of the hash table the node belongs
675331Samw  *    to. To increment or to decrement the reference count the mutex must be
685331Samw  *    entered. To insert the node into the bucket and to remove it from the
695331Samw  *    bucket the lock must be entered in RW_WRITER mode. When both (mutex and
705331Samw  *    lock) have to be entered, the lock has always to be entered first then
715331Samw  *    the mutex. This prevents a deadlock between smb_node_lookup() and
725331Samw  *    smb_node_release() from occurring. However, in smb_node_release() when the
735331Samw  *    reference count drops to zero and triggers the deletion of the node, the
745331Samw  *    mutex has to be released before entering the lock of the bucket (to
755331Samw  *    remove the node). This creates a window during which the node that is
765331Samw  *    about to be freed could be given out by smb_node_lookup(). To close that
775331Samw  *    window the node is moved to the state SMB_NODE_STATE_DESTROYING before
785331Samw  *    releasing the mutex. That way, even if smb_node_lookup() finds it, the
795331Samw  *    state will indicate that the node should be treated as non existent (of
805331Samw  *    course the state of the node should be tested/updated under the
815331Samw  *    protection of the mutex).
825331Samw  */
835331Samw #include <smbsrv/smb_incl.h>
845331Samw #include <smbsrv/smb_fsops.h>
855331Samw #include <sys/pathname.h>
865331Samw #include <sys/sdt.h>
875772Sas200622 #include <sys/nbmlock.h>
885331Samw 
895331Samw uint32_t smb_is_executable(char *path);
905331Samw static void smb_node_delete_on_close(smb_node_t *node);
915331Samw 
925331Samw #define	VALIDATE_DIR_NODE(_dir_, _node_) \
935331Samw     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
945331Samw     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
955331Samw     ASSERT((_dir_)->dir_snode != (_node_));
965331Samw 
976139Sjb150015 static boolean_t	smb_node_initialized = B_FALSE;
986139Sjb150015 static smb_llist_t	smb_node_hash_table[SMBND_HASH_MASK+1];
996139Sjb150015 
1006139Sjb150015 /*
1016139Sjb150015  * smb_node_init
1026139Sjb150015  *
1036139Sjb150015  * Initialization of the SMB node layer.
1046139Sjb150015  *
1056139Sjb150015  * This function is not multi-thread safe. The caller must make sure only one
1066139Sjb150015  * thread makes the call.
1076139Sjb150015  */
1086139Sjb150015 int
1096139Sjb150015 smb_node_init(void)
1106139Sjb150015 {
1116139Sjb150015 	int	i;
1126139Sjb150015 
1136139Sjb150015 	if (smb_node_initialized)
1146139Sjb150015 		return (0);
1156139Sjb150015 
1166139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
1176139Sjb150015 		smb_llist_constructor(&smb_node_hash_table[i],
1186139Sjb150015 		    sizeof (smb_node_t), offsetof(smb_node_t, n_lnd));
1196139Sjb150015 	}
1206139Sjb150015 	smb_node_initialized = B_TRUE;
1216139Sjb150015 	return (0);
1226139Sjb150015 }
1236139Sjb150015 
1246139Sjb150015 /*
1256139Sjb150015  * smb_node_fini
1266139Sjb150015  *
1276139Sjb150015  * This function is not multi-thread safe. The caller must make sure only one
1286139Sjb150015  * thread makes the call.
1296139Sjb150015  */
1306139Sjb150015 void
1316139Sjb150015 smb_node_fini(void)
1326139Sjb150015 {
1336139Sjb150015 	int	i;
1346139Sjb150015 
1356139Sjb150015 	if (!smb_node_initialized)
1366139Sjb150015 		return;
1376139Sjb150015 
1386139Sjb150015 #ifdef DEBUG
1396139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
1406139Sjb150015 		smb_node_t	*node;
1416139Sjb150015 
1426139Sjb150015 		/*
1436139Sjb150015 		 * The following sequence is just intended for sanity check.
1446139Sjb150015 		 * This will have to be modified when the code goes into
1456139Sjb150015 		 * production.
1466139Sjb150015 		 *
1476139Sjb150015 		 * The SMB node hash table should be emtpy at this point. If the
1486139Sjb150015 		 * hash table is not empty a panic will be triggered.
1496139Sjb150015 		 *
1506139Sjb150015 		 * The reason why SMB nodes are still remaining in the hash
1516139Sjb150015 		 * table is problably due to a mismatch between calls to
1526139Sjb150015 		 * smb_node_lookup() and smb_node_release(). You must track that
1536139Sjb150015 		 * down.
1546139Sjb150015 		 */
1556139Sjb150015 		node = smb_llist_head(&smb_node_hash_table[i]);
1566139Sjb150015 		ASSERT(node == NULL);
1576139Sjb150015 	}
1586139Sjb150015 #endif
1596139Sjb150015 
1606139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
1616139Sjb150015 		smb_llist_destructor(&smb_node_hash_table[i]);
1626139Sjb150015 	}
1636139Sjb150015 	smb_node_initialized = B_FALSE;
1646139Sjb150015 }
1656139Sjb150015 
1665331Samw /*
1675331Samw  * smb_node_lookup()
1685331Samw  *
1695331Samw  * NOTE: This routine should only be called by the file system interface layer,
1705331Samw  * and not by SMB.
1715331Samw  *
1725331Samw  * smb_node_lookup() is called upon successful lookup, mkdir, and create
1735331Samw  * (for both non-streams and streams).  In each of these cases, a held vnode is
174*8670SJose.Borrego@Sun.COM  * passed into this routine.  If a new smb_node is created it will take its
175*8670SJose.Borrego@Sun.COM  * own hold on the vnode.  The caller's hold therefore still belongs to, and
176*8670SJose.Borrego@Sun.COM  * should be released by, the caller.
1775331Samw  *
1785331Samw  * A reference is taken on the smb_node whether found in the hash table
1795331Samw  * or newly created.
1805331Samw  *
1815331Samw  * If an smb_node needs to be created, a reference is also taken on the
1825331Samw  * dir_snode (if passed in).
1835331Samw  *
1845331Samw  * See smb_node_release() for details on the release of these references.
1855331Samw  */
1865331Samw 
1875331Samw /*ARGSUSED*/
1885331Samw smb_node_t *
1895331Samw smb_node_lookup(
1905331Samw     struct smb_request	*sr,
1915331Samw     struct open_param	*op,
1925331Samw     cred_t		*cred,
1935331Samw     vnode_t		*vp,
1945331Samw     char		*od_name,
1955331Samw     smb_node_t		*dir_snode,
1965331Samw     smb_node_t		*unnamed_node,
1975331Samw     smb_attr_t		*attr)
1985331Samw {
1995331Samw 	smb_llist_t		*node_hdr;
2005331Samw 	smb_node_t		*node;
2015331Samw 	uint32_t		hashkey = 0;
2027348SJose.Borrego@Sun.COM 	fsid_t			fsid;
2035331Samw 	int			error;
2045331Samw 	krw_t			lock_mode;
2055331Samw 	vnode_t			*unnamed_vp = NULL;
2065331Samw 
2075331Samw 	/*
2085331Samw 	 * smb_vop_getattr() is called here instead of smb_fsop_getattr(),
2095331Samw 	 * because the node may not yet exist.  We also do not want to call
2105331Samw 	 * it with the list lock held.
2115331Samw 	 */
2125331Samw 
2135331Samw 	if (unnamed_node)
2145331Samw 		unnamed_vp = unnamed_node->vp;
2155331Samw 
2165331Samw 	/*
2175331Samw 	 * This getattr is performed on behalf of the server
2185331Samw 	 * that's why kcred is used not the user's cred
2195331Samw 	 */
2205331Samw 	attr->sa_mask = SMB_AT_ALL;
2215772Sas200622 	error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred);
2225331Samw 	if (error)
2235331Samw 		return (NULL);
2245331Samw 
2257348SJose.Borrego@Sun.COM 	if (sr && sr->tid_tree) {
2267348SJose.Borrego@Sun.COM 		/*
2277348SJose.Borrego@Sun.COM 		 * The fsid for a file is that of the tree, even
2287348SJose.Borrego@Sun.COM 		 * if the file resides in a different mountpoint
2297348SJose.Borrego@Sun.COM 		 * under the share.
2307348SJose.Borrego@Sun.COM 		 */
2317348SJose.Borrego@Sun.COM 		fsid = SMB_TREE_FSID(sr->tid_tree);
2325331Samw 	} else {
2337348SJose.Borrego@Sun.COM 		/*
2347348SJose.Borrego@Sun.COM 		 * This should be getting executed only for the
2357348SJose.Borrego@Sun.COM 		 * tree root smb_node.
2367348SJose.Borrego@Sun.COM 		 */
2377348SJose.Borrego@Sun.COM 		fsid = vp->v_vfsp->vfs_fsid;
2385331Samw 	}
2395331Samw 
2407348SJose.Borrego@Sun.COM 	hashkey = fsid.val[0] + attr->sa_vattr.va_nodeid;
2415331Samw 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
2426139Sjb150015 	node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)];
2435331Samw 	lock_mode = RW_READER;
2445331Samw 
2455331Samw 	smb_llist_enter(node_hdr, lock_mode);
2465331Samw 	for (;;) {
2475331Samw 		node = list_head(&node_hdr->ll_list);
2485331Samw 		while (node) {
2495331Samw 			ASSERT(node->n_magic == SMB_NODE_MAGIC);
2505331Samw 			ASSERT(node->n_hash_bucket == node_hdr);
2515331Samw 			if ((node->n_hashkey == hashkey) && (node->vp == vp)) {
2525331Samw 				smb_rwx_xenter(&node->n_lock);
2535331Samw 				DTRACE_PROBE1(smb_node_lookup_hit,
2545331Samw 				    smb_node_t *, node);
2555331Samw 				switch (node->n_state) {
2565331Samw 				case SMB_NODE_STATE_AVAILABLE:
2575331Samw 					/* The node was found. */
2585331Samw 					node->n_refcnt++;
2595331Samw 					if ((node->dir_snode == NULL) &&
2605331Samw 					    (dir_snode != NULL) &&
2615331Samw 					    (strcmp(od_name, "..") != 0) &&
2625331Samw 					    (strcmp(od_name, ".") != 0)) {
2635331Samw 						VALIDATE_DIR_NODE(dir_snode,
2645331Samw 						    node);
2655331Samw 						node->dir_snode = dir_snode;
2665331Samw 						smb_node_ref(dir_snode);
2675331Samw 					}
2685331Samw 					node->attr = *attr;
2695772Sas200622 					node->n_size = attr->sa_vattr.va_size;
2705331Samw 
2715331Samw 					smb_audit_node(node);
2725331Samw 					smb_rwx_xexit(&node->n_lock);
2735331Samw 					smb_llist_exit(node_hdr);
2745331Samw 					return (node);
2755331Samw 
2765331Samw 				case SMB_NODE_STATE_DESTROYING:
2775331Samw 					/*
2785331Samw 					 * Although the node exists it is about
2795331Samw 					 * to be destroyed. We act as it hasn't
2805331Samw 					 * been found.
2815331Samw 					 */
2825331Samw 					smb_rwx_xexit(&node->n_lock);
2835331Samw 					break;
2845331Samw 				default:
2855331Samw 					/*
2865331Samw 					 * Although the node exists it is in an
2875331Samw 					 * unknown state. We act as it hasn't
2885331Samw 					 * been found.
2895331Samw 					 */
2905331Samw 					ASSERT(0);
2915331Samw 					smb_rwx_xexit(&node->n_lock);
2925331Samw 					break;
2935331Samw 				}
2945331Samw 			}
2955331Samw 			node = smb_llist_next(node_hdr, node);
2965331Samw 		}
2975331Samw 		if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) {
2985331Samw 			lock_mode = RW_WRITER;
2995331Samw 			continue;
3005331Samw 		}
3015331Samw 		break;
3025331Samw 	}
3036139Sjb150015 	node = kmem_cache_alloc(sr->sr_server->si_cache_node, KM_SLEEP);
3045331Samw 	bzero(node, sizeof (smb_node_t));
3055331Samw 
3065331Samw 	node->n_state = SMB_NODE_STATE_AVAILABLE;
3075331Samw 	node->n_hash_bucket = node_hdr;
3086139Sjb150015 	node->n_sr = sr;
3096139Sjb150015 	node->vp = vp;
310*8670SJose.Borrego@Sun.COM 	VN_HOLD(node->vp);
3116139Sjb150015 	node->n_hashkey = hashkey;
3126139Sjb150015 	node->n_refcnt = 1;
3136139Sjb150015 	node->attr = *attr;
3146139Sjb150015 	node->flags |= NODE_FLAGS_ATTR_VALID;
3156139Sjb150015 	node->n_size = node->attr.sa_vattr.va_size;
3166139Sjb150015 	node->n_orig_session_id = sr->session->s_kid;
3176139Sjb150015 	node->n_orig_uid = crgetuid(sr->user_cr);
3186139Sjb150015 	node->n_cache = sr->sr_server->si_cache_node;
3195331Samw 
3206139Sjb150015 	ASSERT(od_name);
3216139Sjb150015 	(void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
3226139Sjb150015 
3235331Samw 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
3245331Samw 	    offsetof(smb_ofile_t, f_nnd));
3255331Samw 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
3265331Samw 	    offsetof(smb_lock_t, l_lnd));
3275331Samw 
3285331Samw 
3295967Scp160787 	if (strcmp(od_name, XATTR_DIR) == 0)
3305967Scp160787 		node->flags |= NODE_XATTR_DIR;
3315331Samw 	if (op)
3325331Samw 		node->flags |= smb_is_executable(op->fqi.last_comp);
3335331Samw 
3345331Samw 	if (dir_snode) {
3355331Samw 		smb_node_ref(dir_snode);
3365331Samw 		node->dir_snode = dir_snode;
3375331Samw 		ASSERT(dir_snode->dir_snode != node);
3385331Samw 		ASSERT((dir_snode->vp->v_xattrdir) ||
3395331Samw 		    (dir_snode->vp->v_type == VDIR));
3405331Samw 	}
3415331Samw 
3425331Samw 	if (unnamed_node) {
3435331Samw 		smb_node_ref(unnamed_node);
3445331Samw 		node->unnamed_stream_node = unnamed_node;
3455331Samw 	}
3465331Samw 
3475331Samw 	smb_rwx_init(&node->n_lock);
3485331Samw 	node->n_magic = SMB_NODE_MAGIC;
3495331Samw 	smb_audit_buf_node_create(node);
3505331Samw 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
3515331Samw 	smb_audit_node(node);
3525331Samw 	smb_llist_insert_head(node_hdr, node);
3536139Sjb150015 
3545331Samw 	smb_llist_exit(node_hdr);
3555331Samw 	return (node);
3565331Samw }
3575331Samw 
3585331Samw /*
3595331Samw  * smb_stream_node_lookup()
3605331Samw  *
3615331Samw  * Note: stream_name (the name that will be stored in the "od_name" field
3625331Samw  * of a stream's smb_node) is the same as the on-disk name for the stream
3635331Samw  * except that it does not have SMB_STREAM_PREFIX prepended.
3645331Samw  */
3655331Samw 
3665331Samw smb_node_t *
3675331Samw smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode,
3685331Samw     vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr)
3695331Samw {
3705331Samw 	smb_node_t	*xattrdir_node;
3715331Samw 	smb_node_t	*snode;
3725331Samw 	smb_attr_t	tmp_attr;
3735331Samw 
3745331Samw 	xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR,
3755331Samw 	    fnode, NULL, &tmp_attr);
3765331Samw 
3775331Samw 	if (xattrdir_node == NULL)
3785331Samw 		return (NULL);
3795331Samw 
3805331Samw 	snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node,
3815331Samw 	    fnode, ret_attr);
3825331Samw 
3835331Samw 	(void) smb_node_release(xattrdir_node);
3845331Samw 	return (snode);
3855331Samw }
3865331Samw 
3875331Samw 
3885331Samw /*
3895331Samw  * This function should be called whenever a reference is needed on an
3905331Samw  * smb_node pointer.  The copy of an smb_node pointer from one non-local
3915331Samw  * data structure to another requires a reference to be taken on the smb_node
3925331Samw  * (unless the usage is localized).  Each data structure deallocation routine
3935331Samw  * will call smb_node_release() on its smb_node pointers.
3945331Samw  *
3955331Samw  * In general, an smb_node pointer residing in a structure should never be
3965331Samw  * stale.  A node pointer may be NULL, however, and care should be taken
3975331Samw  * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid.
3985331Samw  * Care also needs to be taken with respect to racing deallocations of a
3995331Samw  * structure.
4005331Samw  */
4015331Samw 
4025331Samw void
4035331Samw smb_node_ref(smb_node_t *node)
4045331Samw {
4055331Samw 	ASSERT(node);
4065331Samw 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
4075331Samw 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
4085331Samw 
4095331Samw 	smb_rwx_xenter(&node->n_lock);
4105331Samw 	node->n_refcnt++;
4115331Samw 	ASSERT(node->n_refcnt);
4125331Samw 	DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node);
4135331Samw 	smb_audit_node(node);
4145331Samw 	smb_rwx_xexit(&node->n_lock);
4155331Samw }
4165331Samw 
4175331Samw /*
4185331Samw  * smb_node_lookup() takes a hold on an smb_node, whether found in the
4195331Samw  * hash table or newly created.  This hold is expected to be released
4205331Samw  * in the following manner.
4215331Samw  *
4225331Samw  * smb_node_lookup() takes an address of an smb_node pointer.  This should
4235331Samw  * be getting passed down via a lookup (whether path name or component), mkdir,
4245331Samw  * create.  If the original smb_node pointer resides in a data structure, then
4255331Samw  * the deallocation routine for the data structure is responsible for calling
4265331Samw  * smb_node_release() on the smb_node pointer.  Alternatively,
4275331Samw  * smb_node_release() can be called as soon as the smb_node pointer is no longer
4285331Samw  * needed.  In this case, callers are responsible for setting an embedded
4295331Samw  * pointer to NULL if it is known that the last reference is being released.
4305331Samw  *
4315331Samw  * If the passed-in address of the smb_node pointer belongs to a local variable,
4325331Samw  * then the caller with the local variable should call smb_node_release()
4335331Samw  * directly.
4345331Samw  *
4355331Samw  * smb_node_release() itself will call smb_node_release() on a node's dir_snode,
4365331Samw  * as smb_node_lookup() takes a hold on dir_snode.
4375331Samw  */
4385331Samw void
4395331Samw smb_node_release(smb_node_t *node)
4405331Samw {
4415331Samw 	ASSERT(node);
4425331Samw 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
4435331Samw 
4445331Samw 	smb_rwx_xenter(&node->n_lock);
4455331Samw 	ASSERT(node->n_refcnt);
4465331Samw 	DTRACE_PROBE1(smb_node_release, smb_node_t *, node);
4475331Samw 	if (--node->n_refcnt == 0) {
4485331Samw 		switch (node->n_state) {
4495331Samw 
4505331Samw 		case SMB_NODE_STATE_AVAILABLE:
4515331Samw 			node->n_state = SMB_NODE_STATE_DESTROYING;
4525331Samw 			smb_rwx_xexit(&node->n_lock);
4535331Samw 
4545331Samw 			smb_llist_enter(node->n_hash_bucket, RW_WRITER);
4555331Samw 			smb_llist_remove(node->n_hash_bucket, node);
4565331Samw 			smb_llist_exit(node->n_hash_bucket);
4575331Samw 
4585331Samw 			/*
4595331Samw 			 * Check if the file was deleted
4605331Samw 			 */
4615331Samw 			smb_node_delete_on_close(node);
4625331Samw 			node->n_magic = (uint32_t)~SMB_NODE_MAGIC;
4635331Samw 
4645331Samw 			/* These lists should be empty. */
4655331Samw 			smb_llist_destructor(&node->n_ofile_list);
4665331Samw 			smb_llist_destructor(&node->n_lock_list);
4675331Samw 
4685331Samw 			if (node->dir_snode) {
4695331Samw 				ASSERT(node->dir_snode->n_magic ==
4705331Samw 				    SMB_NODE_MAGIC);
4715331Samw 				smb_node_release(node->dir_snode);
4725331Samw 			}
4735331Samw 
4745331Samw 			if (node->unnamed_stream_node) {
4755331Samw 				ASSERT(node->unnamed_stream_node->n_magic ==
4765331Samw 				    SMB_NODE_MAGIC);
4775331Samw 				smb_node_release(node->unnamed_stream_node);
4785331Samw 			}
4795331Samw 
4805331Samw 			ASSERT(node->vp);
4815331Samw 			VN_RELE(node->vp);
4825331Samw 
4835331Samw 			smb_audit_buf_node_destroy(node);
4845331Samw 			smb_rwx_destroy(&node->n_lock);
4856139Sjb150015 			kmem_cache_free(node->n_cache, node);
4865331Samw 			return;
4875331Samw 
4885331Samw 		default:
4895331Samw 			ASSERT(0);
4905331Samw 			break;
4915331Samw 		}
4925331Samw 	}
4935331Samw 	smb_audit_node(node);
4945331Samw 	smb_rwx_xexit(&node->n_lock);
4955331Samw }
4965331Samw 
4975331Samw static void
4985331Samw smb_node_delete_on_close(smb_node_t *node)
4995331Samw {
5005331Samw 	smb_node_t	*d_snode;
5015331Samw 	int		rc = 0;
5025331Samw 
5035331Samw 	d_snode = node->dir_snode;
5045331Samw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
5055331Samw 
5065331Samw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
5075331Samw 		ASSERT(node->od_name != NULL);
5085331Samw 		if (node->attr.sa_vattr.va_type == VDIR)
5095331Samw 			rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
5105331Samw 			    d_snode, node->od_name, 1);
5115331Samw 		else
5125331Samw 			rc = smb_fsop_remove(0, node->delete_on_close_cred,
5135331Samw 			    d_snode, node->od_name, 1);
5145331Samw 		smb_cred_rele(node->delete_on_close_cred);
5155331Samw 	}
5165331Samw 	if (rc != 0)
5175331Samw 		cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
5185331Samw 		    node->od_name, rc);
5195331Samw 	DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
5205331Samw }
5215331Samw 
5225331Samw /*
5235331Samw  * smb_node_rename()
5245331Samw  *
5255331Samw  */
5265331Samw int
5275331Samw smb_node_rename(
5285331Samw     smb_node_t	*from_dir_snode,
5295331Samw     smb_node_t	*ret_snode,
5305331Samw     smb_node_t	*to_dir_snode,
5315331Samw     char	*to_name)
5325331Samw {
5335331Samw 	ASSERT(from_dir_snode);
5345331Samw 	ASSERT(to_dir_snode);
5355331Samw 	ASSERT(ret_snode);
5365331Samw 	ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC);
5375331Samw 	ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC);
5385331Samw 	ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC);
5395331Samw 	ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
5405331Samw 	ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
5415331Samw 	ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE);
5425331Samw 
5435331Samw 	smb_node_ref(to_dir_snode);
5445331Samw 	smb_rwx_xenter(&ret_snode->n_lock);
5455331Samw 	ret_snode->dir_snode = to_dir_snode;
5465331Samw 	smb_rwx_xexit(&ret_snode->n_lock);
5475331Samw 	ASSERT(to_dir_snode->dir_snode != ret_snode);
5485331Samw 	ASSERT((to_dir_snode->vp->v_xattrdir) ||
5495331Samw 	    (to_dir_snode->vp->v_type == VDIR));
5505331Samw 	smb_node_release(from_dir_snode);
5515331Samw 
5525331Samw 	(void) strcpy(ret_snode->od_name, to_name);
5535331Samw 
5545331Samw 	/*
5555331Samw 	 * XXX Need to update attributes?
5565331Samw 	 */
5575331Samw 
5585331Samw 	return (0);
5595331Samw }
5605331Samw 
5615331Samw int
5626139Sjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root)
5635331Samw {
5646139Sjb150015 	smb_attr_t	va;
5656139Sjb150015 	int		error;
5666139Sjb150015 	uint32_t	hashkey;
5676139Sjb150015 	smb_llist_t	*node_hdr;
5686139Sjb150015 	smb_node_t	*node;
5695331Samw 
5705331Samw 	/*
5715331Samw 	 * Take an explicit hold on rootdir.  This goes with the
5725331Samw 	 * corresponding release in smb_node_root_fini()/smb_node_release().
5735331Samw 	 */
5746139Sjb150015 	VN_HOLD(vp);
5755331Samw 
5766139Sjb150015 	va.sa_mask = SMB_AT_ALL;
5776139Sjb150015 	error = smb_vop_getattr(vp, NULL, &va, 0, kcred);
5786139Sjb150015 	if (error) {
5796139Sjb150015 		VN_RELE(vp);
5806139Sjb150015 		return (error);
5816139Sjb150015 	}
5826139Sjb150015 
5836139Sjb150015 	hashkey = vp->v_vfsp->vfs_fsid.val[0] + va.sa_vattr.va_nodeid;
5846139Sjb150015 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
5856139Sjb150015 	node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)];
5866139Sjb150015 
5876139Sjb150015 	node = kmem_cache_alloc(sv->si_cache_node, KM_SLEEP);
5886139Sjb150015 	bzero(node, sizeof (smb_node_t));
5895331Samw 
5906139Sjb150015 	node->n_state = SMB_NODE_STATE_AVAILABLE;
5916139Sjb150015 	node->n_hash_bucket = node_hdr;
5926139Sjb150015 	node->vp = vp;
5936139Sjb150015 	node->n_hashkey = hashkey;
5946139Sjb150015 	node->n_refcnt = 1;
5956139Sjb150015 	node->attr = va;
5966139Sjb150015 	node->flags |= NODE_FLAGS_ATTR_VALID;
5976139Sjb150015 	node->n_size = node->attr.sa_vattr.va_size;
5986139Sjb150015 	node->n_cache = sv->si_cache_node;
5996139Sjb150015 	(void) strlcpy(node->od_name, ROOTVOL, sizeof (node->od_name));
6006139Sjb150015 
6016139Sjb150015 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
6026139Sjb150015 	    offsetof(smb_ofile_t, f_nnd));
6036139Sjb150015 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
6046139Sjb150015 	    offsetof(smb_lock_t, l_lnd));
6056139Sjb150015 
6066139Sjb150015 	smb_rwx_init(&node->n_lock);
6076139Sjb150015 	node->n_magic = SMB_NODE_MAGIC;
6086139Sjb150015 	smb_audit_buf_node_create(node);
6096139Sjb150015 
6106139Sjb150015 	sv->si_root_smb_node = node;
6116139Sjb150015 
6126139Sjb150015 	smb_audit_node(node);
6136139Sjb150015 	smb_llist_enter(node_hdr, RW_WRITER);
6146139Sjb150015 	smb_llist_insert_head(node_hdr, node);
6156139Sjb150015 	smb_llist_exit(node_hdr);
6166139Sjb150015 
6176139Sjb150015 	*root = node;
6186139Sjb150015 
6196139Sjb150015 	return (0);
6205331Samw }
6215331Samw 
6225331Samw /*
6235331Samw  * smb_node_get_size
6245331Samw  */
6256432Sas200622 u_offset_t
6266432Sas200622 smb_node_get_size(smb_node_t *node, smb_attr_t *attr)
6275331Samw {
6286432Sas200622 	u_offset_t size;
6295331Samw 
6305331Samw 	if (attr->sa_vattr.va_type == VDIR)
6315331Samw 		return (0);
6325331Samw 
6335331Samw 	smb_rwx_xenter(&node->n_lock);
6345331Samw 	if (node && (node->flags & NODE_FLAGS_SET_SIZE))
6355331Samw 		size = node->n_size;
6365331Samw 	else
6375331Samw 		size = attr->sa_vattr.va_size;
6385331Samw 	smb_rwx_xexit(&node->n_lock);
6395331Samw 	return (size);
6405331Samw }
6415331Samw 
6425331Samw static int
6435331Samw timeval_cmp(timestruc_t *a, timestruc_t *b)
6445331Samw {
6455331Samw 	if (a->tv_sec < b->tv_sec)
6465331Samw 		return (-1);
6475331Samw 	if (a->tv_sec > b->tv_sec)
6485331Samw 		return (1);
6495331Samw 	/* Seconds are equal compare tv_nsec */
6505331Samw 	if (a->tv_nsec < b->tv_nsec)
6515331Samw 		return (-1);
6525331Samw 	return (a->tv_nsec > b->tv_nsec);
6535331Samw }
6545331Samw 
6555331Samw /*
6565331Samw  * smb_node_set_time
6575331Samw  *
6585331Samw  * This function will update the time stored in the node and
6597348SJose.Borrego@Sun.COM  * set the appropriate flags. If there is nothing to update,
6607348SJose.Borrego@Sun.COM  * the function will return without any updates.  The update
6617348SJose.Borrego@Sun.COM  * is only in the node level and the attribute in the file system
6627348SJose.Borrego@Sun.COM  * will be updated when client close the file.
6635331Samw  */
6645331Samw void
6655331Samw smb_node_set_time(struct smb_node *node, struct timestruc *crtime,
6665331Samw     struct timestruc *mtime, struct timestruc *atime,
6675331Samw     struct timestruc *ctime, unsigned int what)
6685331Samw {
6697348SJose.Borrego@Sun.COM 	if (what == 0)
6705331Samw 		return;
6715331Samw 
6725331Samw 	if ((what & SMB_AT_CRTIME && crtime == 0) ||
6735331Samw 	    (what & SMB_AT_MTIME && mtime == 0) ||
6745331Samw 	    (what & SMB_AT_ATIME && atime == 0) ||
6757348SJose.Borrego@Sun.COM 	    (what & SMB_AT_CTIME && ctime == 0))
6765331Samw 		return;
6777348SJose.Borrego@Sun.COM 
6787348SJose.Borrego@Sun.COM 	smb_rwx_xenter(&node->n_lock);
6795331Samw 
6805331Samw 	if ((what & SMB_AT_CRTIME) &&
6815331Samw 	    timeval_cmp((timestruc_t *)&node->attr.sa_crtime,
6825331Samw 	    crtime) != 0) {
6835331Samw 		node->what |= SMB_AT_CRTIME;
6845331Samw 		node->attr.sa_crtime = *((timestruc_t *)crtime);
6855331Samw 	}
6865331Samw 
6875331Samw 	if ((what & SMB_AT_MTIME) &&
6885331Samw 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime,
6895331Samw 	    mtime) != 0) {
6905331Samw 		node->what |= SMB_AT_MTIME;
6915331Samw 		node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime);
6925331Samw 	}
6935331Samw 
6945331Samw 	if ((what & SMB_AT_ATIME) &&
6955331Samw 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime,
6965331Samw 	    atime) != 0) {
6975331Samw 			node->what |= SMB_AT_ATIME;
6985331Samw 			node->attr.sa_vattr.va_atime = *((timestruc_t *)atime);
6995331Samw 	}
7005331Samw 
7015331Samw 	/*
7025331Samw 	 * The ctime handling is trickier. It has three scenarios.
7035331Samw 	 * 1. Only ctime need to be set and it is the same as the ctime
7045331Samw 	 *    stored in the node. (update not necessary)
7055331Samw 	 * 2. The ctime is the same as the ctime stored in the node but
7065331Samw 	 *    is not the only time need to be set. (update required)
7075331Samw 	 * 3. The ctime need to be set and is not the same as the ctime
7085331Samw 	 *    stored in the node. (update required)
7095331Samw 	 * Unlike other time setting, the ctime needs to be set even when
7105331Samw 	 * it is the same as the ctime in the node if there are other time
7115331Samw 	 * needs to be set (#2). This will ensure the ctime not being
7125331Samw 	 * updated when other times are being updated in the file system.
7135331Samw 	 *
7145331Samw 	 * Retained file rules:
7155331Samw 	 *
7165331Samw 	 * 1. Don't add SMB_AT_CTIME to node->what by default because the
7175331Samw 	 *    request will be rejected by filesystem
7185331Samw 	 * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e.
7195331Samw 	 *    any request for changing ctime on these files should have
7205331Samw 	 *    been already rejected
7215331Samw 	 */
7225331Samw 	node->what |= SMB_AT_CTIME;
7235331Samw 	if (what & SMB_AT_CTIME) {
7245331Samw 		if ((what == SMB_AT_CTIME) &&
7255331Samw 		    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime,
7265331Samw 		    ctime) == 0) {
7275331Samw 			node->what &= ~SMB_AT_CTIME;
7285331Samw 		} else {
7295331Samw 			gethrestime(&node->attr.sa_vattr.va_ctime);
7305331Samw 		}
7315331Samw 	} else {
7325331Samw 		gethrestime(&node->attr.sa_vattr.va_ctime);
7335331Samw 	}
7345331Samw 	smb_rwx_xexit(&node->n_lock);
7355331Samw }
7365331Samw 
7375331Samw 
7385331Samw timestruc_t *
7395331Samw smb_node_get_crtime(smb_node_t *node)
7405331Samw {
7415331Samw 	return ((timestruc_t *)&node->attr.sa_crtime);
7425331Samw }
7435331Samw 
7445331Samw timestruc_t *
7455331Samw smb_node_get_atime(smb_node_t *node)
7465331Samw {
7475331Samw 	return ((timestruc_t *)&node->attr.sa_vattr.va_atime);
7485331Samw }
7495331Samw 
7505331Samw timestruc_t *
7515331Samw smb_node_get_ctime(smb_node_t *node)
7525331Samw {
7535331Samw 	return ((timestruc_t *)&node->attr.sa_vattr.va_ctime);
7545331Samw }
7555331Samw 
7565331Samw timestruc_t *
7575331Samw smb_node_get_mtime(smb_node_t *node)
7585331Samw {
7595331Samw 	return ((timestruc_t *)&node->attr.sa_vattr.va_mtime);
7605331Samw }
7615331Samw 
7625331Samw /*
7635331Samw  * smb_node_set_dosattr
7645331Samw  *
7655331Samw  * Parse the specified DOS attributes and, if they have been modified,
7665331Samw  * update the node cache. This call should be followed by a
7675331Samw  * smb_sync_fsattr() call to write the attribute changes to filesystem.
7685331Samw  */
7695331Samw void
7707052Samw smb_node_set_dosattr(smb_node_t *node, uint32_t dosattr)
7715331Samw {
7727052Samw 	uint32_t mode = dosattr & (FILE_ATTRIBUTE_ARCHIVE |
7737052Samw 	    FILE_ATTRIBUTE_READONLY |
7747052Samw 	    FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
7755331Samw 
7765331Samw 	smb_rwx_xenter(&node->n_lock);
7775331Samw 	if (node->attr.sa_dosattr != mode) {
7785331Samw 		node->attr.sa_dosattr = mode;
7795331Samw 		node->what |= SMB_AT_DOSATTR;
7805331Samw 	}
7815331Samw 	smb_rwx_xexit(&node->n_lock);
7825331Samw }
7835331Samw 
7845331Samw /*
7857348SJose.Borrego@Sun.COM  * smb_node_get_dosattr()
7865331Samw  *
7877348SJose.Borrego@Sun.COM  * This function is used to provide clients with information as to whether
7887348SJose.Borrego@Sun.COM  * the readonly bit is set.  Hence both the node attribute cache (which
7897348SJose.Borrego@Sun.COM  * reflects the on-disk attributes) and node->readonly_creator (which
7907348SJose.Borrego@Sun.COM  * reflects whether a readonly set is pending from a readonly create) are
7917348SJose.Borrego@Sun.COM  * checked.  In the latter case, the readonly attribute should be visible to
7927348SJose.Borrego@Sun.COM  * all clients even though the readonly creator fid is immune to the readonly
7937348SJose.Borrego@Sun.COM  * bit until close.
7945331Samw  */
7957348SJose.Borrego@Sun.COM 
7965331Samw uint32_t
7975331Samw smb_node_get_dosattr(smb_node_t *node)
7985331Samw {
7997348SJose.Borrego@Sun.COM 	uint32_t dosattr = node->attr.sa_dosattr;
8007348SJose.Borrego@Sun.COM 
8017348SJose.Borrego@Sun.COM 	if (node->readonly_creator)
8027348SJose.Borrego@Sun.COM 		dosattr |= FILE_ATTRIBUTE_READONLY;
8037348SJose.Borrego@Sun.COM 
8047348SJose.Borrego@Sun.COM 	if (!dosattr)
8057348SJose.Borrego@Sun.COM 		dosattr = FILE_ATTRIBUTE_NORMAL;
8067348SJose.Borrego@Sun.COM 
8077348SJose.Borrego@Sun.COM 	return (dosattr);
8085331Samw }
8095331Samw 
8105331Samw int
8115331Samw smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr)
8125331Samw {
8135331Samw 	int	rc = -1;
8145331Samw 
8155331Samw 	smb_rwx_xenter(&node->n_lock);
8165331Samw 	if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) &&
8175331Samw 	    !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) {
8185331Samw 		crhold(cr);
8195331Samw 		node->delete_on_close_cred = cr;
8205331Samw 		node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
8215331Samw 		rc = 0;
8225331Samw 	}
8235331Samw 	smb_rwx_xexit(&node->n_lock);
8245331Samw 	return (rc);
8255331Samw }
8265331Samw 
8275331Samw void
8285331Samw smb_node_reset_delete_on_close(smb_node_t *node)
8295331Samw {
8305331Samw 	smb_rwx_xenter(&node->n_lock);
8315331Samw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
8325331Samw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
8335331Samw 		crfree(node->delete_on_close_cred);
8345331Samw 		node->delete_on_close_cred = NULL;
8355331Samw 	}
8365331Samw 	smb_rwx_xexit(&node->n_lock);
8375331Samw }
8385772Sas200622 
8395772Sas200622 /*
8406771Sjb150015  * smb_node_open_check
8415772Sas200622  *
8425772Sas200622  * check file sharing rules for current open request
8435772Sas200622  * against all existing opens for a file.
8445772Sas200622  *
8455772Sas200622  * Returns NT_STATUS_SHARING_VIOLATION if there is any
8465772Sas200622  * sharing conflict, otherwise returns NT_STATUS_SUCCESS.
8475772Sas200622  */
8485772Sas200622 uint32_t
8495772Sas200622 smb_node_open_check(struct smb_node *node, cred_t *cr,
8505772Sas200622     uint32_t desired_access, uint32_t share_access)
8515772Sas200622 {
8525772Sas200622 	smb_ofile_t *of;
8535772Sas200622 	uint32_t status;
8545772Sas200622 
8555772Sas200622 	ASSERT(node);
8565772Sas200622 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
8575772Sas200622 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
8585772Sas200622 
8595772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
8605772Sas200622 	of = smb_llist_head(&node->n_ofile_list);
8615772Sas200622 	while (of) {
8626771Sjb150015 		status = smb_ofile_open_check(of, cr, desired_access,
8636771Sjb150015 		    share_access);
8646771Sjb150015 
8656771Sjb150015 		switch (status) {
8666771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
8676771Sjb150015 		case NT_STATUS_SUCCESS:
8686771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
8696771Sjb150015 			break;
8706771Sjb150015 		default:
8716771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
8725772Sas200622 			smb_llist_exit(&node->n_ofile_list);
8735772Sas200622 			return (status);
8745772Sas200622 		}
8755772Sas200622 	}
8766771Sjb150015 
8775772Sas200622 	smb_llist_exit(&node->n_ofile_list);
8785772Sas200622 	return (NT_STATUS_SUCCESS);
8795772Sas200622 }
8805772Sas200622 
8815772Sas200622 uint32_t
8825772Sas200622 smb_node_rename_check(struct smb_node *node)
8835772Sas200622 {
8846771Sjb150015 	struct smb_ofile *of;
8856771Sjb150015 	uint32_t status;
8865772Sas200622 
8875772Sas200622 	ASSERT(node);
8885772Sas200622 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
8895772Sas200622 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
8905772Sas200622 
8915772Sas200622 	/*
8925772Sas200622 	 * Intra-CIFS check
8935772Sas200622 	 */
8945772Sas200622 
8955772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
8966771Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
8976771Sjb150015 	while (of) {
8986771Sjb150015 		status = smb_ofile_rename_check(of);
8995772Sas200622 
9006771Sjb150015 		switch (status) {
9016771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
9026771Sjb150015 		case NT_STATUS_SUCCESS:
9036771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
9046771Sjb150015 			break;
9056771Sjb150015 		default:
9066771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
9076771Sjb150015 			smb_llist_exit(&node->n_ofile_list);
9086771Sjb150015 			return (status);
9095772Sas200622 		}
9105772Sas200622 	}
9115772Sas200622 	smb_llist_exit(&node->n_ofile_list);
9125772Sas200622 
9135772Sas200622 	/*
9145772Sas200622 	 * system-wide share check
9155772Sas200622 	 */
9165772Sas200622 
9175772Sas200622 	if (nbl_share_conflict(node->vp, NBL_RENAME, NULL))
9185772Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
9195772Sas200622 	else
9205772Sas200622 		return (NT_STATUS_SUCCESS);
9215772Sas200622 }
9225772Sas200622 
9236771Sjb150015 uint32_t
9245772Sas200622 smb_node_delete_check(smb_node_t *node)
9255772Sas200622 {
9266771Sjb150015 	smb_ofile_t *of;
9276771Sjb150015 	uint32_t status;
9285772Sas200622 
9295772Sas200622 	ASSERT(node);
9305772Sas200622 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
9315772Sas200622 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
9325772Sas200622 
9335772Sas200622 	if (node->attr.sa_vattr.va_type == VDIR)
9345772Sas200622 		return (NT_STATUS_SUCCESS);
9355772Sas200622 
9365772Sas200622 	/*
9375772Sas200622 	 * intra-CIFS check
9385772Sas200622 	 */
9395772Sas200622 
9405772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
9416771Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
9426771Sjb150015 	while (of) {
9436771Sjb150015 		status = smb_ofile_delete_check(of);
9446771Sjb150015 
9456771Sjb150015 		switch (status) {
9466771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
9476771Sjb150015 		case NT_STATUS_SUCCESS:
9486771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
9496771Sjb150015 			break;
9506771Sjb150015 		default:
9516771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
9525772Sas200622 			smb_llist_exit(&node->n_ofile_list);
9536771Sjb150015 			return (status);
9545772Sas200622 		}
9555772Sas200622 	}
9565772Sas200622 	smb_llist_exit(&node->n_ofile_list);
9575772Sas200622 
9585772Sas200622 	/*
9595772Sas200622 	 * system-wide share check
9605772Sas200622 	 */
9615772Sas200622 
9625772Sas200622 	if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL))
9635772Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
9645772Sas200622 	else
9655772Sas200622 		return (NT_STATUS_SUCCESS);
9665772Sas200622 }
9675772Sas200622 
9685772Sas200622 /*
9695772Sas200622  * smb_node_start_crit()
9705772Sas200622  *
9715772Sas200622  * Enter critical region for share reservations.
9725772Sas200622  * See comments above smb_fsop_shrlock().
9735772Sas200622  */
9745772Sas200622 
9755772Sas200622 void
9765772Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode)
9775772Sas200622 {
9785772Sas200622 	rw_enter(&node->n_share_lock, mode);
9795772Sas200622 	nbl_start_crit(node->vp, mode);
9805772Sas200622 }
9815772Sas200622 
9825772Sas200622 /*
9835772Sas200622  * smb_node_end_crit()
9845772Sas200622  *
9855772Sas200622  * Exit critical region for share reservations.
9865772Sas200622  */
9875772Sas200622 
9885772Sas200622 void
9895772Sas200622 smb_node_end_crit(smb_node_t *node)
9905772Sas200622 {
9915772Sas200622 	nbl_end_crit(node->vp);
9925772Sas200622 	rw_exit(&node->n_share_lock);
9935772Sas200622 }
9945772Sas200622 
9955772Sas200622 int
9965772Sas200622 smb_node_in_crit(smb_node_t *node)
9975772Sas200622 {
9985772Sas200622 	return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock));
9995772Sas200622 }
1000