xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_node.c (revision 7f667e74610492ddbce8ce60f52ece95d2401949)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
22*7f667e74Sjose borrego  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24da6c28aaSamw  */
25da6c28aaSamw 
26c8ec8eeaSjose borrego #pragma ident	"@(#)smb_node.c	1.9	08/08/07 SMI"
27da6c28aaSamw 
28da6c28aaSamw /*
29da6c28aaSamw  * SMB Node State Machine
30da6c28aaSamw  * ----------------------
31da6c28aaSamw  *
32da6c28aaSamw  *    +----------------------------+	 T0
33da6c28aaSamw  *    |  SMB_NODE_STATE_AVAILABLE  |<----------- Creation/Allocation
34da6c28aaSamw  *    +----------------------------+
35da6c28aaSamw  *		    |
36da6c28aaSamw  *		    | T1
37da6c28aaSamw  *		    |
38da6c28aaSamw  *		    v
39da6c28aaSamw  *    +-----------------------------+    T2
40da6c28aaSamw  *    |  SMB_NODE_STATE_DESTROYING  |----------> Deletion/Free
41da6c28aaSamw  *    +-----------------------------+
42da6c28aaSamw  *
43da6c28aaSamw  * Transition T0
44da6c28aaSamw  *
45da6c28aaSamw  *    This transition occurs in smb_node_lookup(). If the node looked for is
46da6c28aaSamw  *    not found in the has table a new node is created. The reference count is
47da6c28aaSamw  *    initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE.
48da6c28aaSamw  *
49da6c28aaSamw  * Transition T1
50da6c28aaSamw  *
51da6c28aaSamw  *    This transition occurs in smb_node_release(). If the reference count
52da6c28aaSamw  *    drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more
53da6c28aaSamw  *    reference count will be given out for that node.
54da6c28aaSamw  *
55da6c28aaSamw  * Transition T2
56da6c28aaSamw  *
57da6c28aaSamw  *    This transition occurs in smb_node_release(). The structure is deleted.
58da6c28aaSamw  *
59da6c28aaSamw  * Comments
60da6c28aaSamw  * --------
61da6c28aaSamw  *
62da6c28aaSamw  *    The reason the smb node has 2 states is the following synchronization
63da6c28aaSamw  *    rule:
64da6c28aaSamw  *
65da6c28aaSamw  *    There's a mutex embedded in the node used to protect its fields and
66da6c28aaSamw  *    there's a lock embedded in the bucket of the hash table the node belongs
67da6c28aaSamw  *    to. To increment or to decrement the reference count the mutex must be
68da6c28aaSamw  *    entered. To insert the node into the bucket and to remove it from the
69da6c28aaSamw  *    bucket the lock must be entered in RW_WRITER mode. When both (mutex and
70da6c28aaSamw  *    lock) have to be entered, the lock has always to be entered first then
71da6c28aaSamw  *    the mutex. This prevents a deadlock between smb_node_lookup() and
72da6c28aaSamw  *    smb_node_release() from occurring. However, in smb_node_release() when the
73da6c28aaSamw  *    reference count drops to zero and triggers the deletion of the node, the
74da6c28aaSamw  *    mutex has to be released before entering the lock of the bucket (to
75da6c28aaSamw  *    remove the node). This creates a window during which the node that is
76da6c28aaSamw  *    about to be freed could be given out by smb_node_lookup(). To close that
77da6c28aaSamw  *    window the node is moved to the state SMB_NODE_STATE_DESTROYING before
78da6c28aaSamw  *    releasing the mutex. That way, even if smb_node_lookup() finds it, the
79da6c28aaSamw  *    state will indicate that the node should be treated as non existent (of
80da6c28aaSamw  *    course the state of the node should be tested/updated under the
81da6c28aaSamw  *    protection of the mutex).
82da6c28aaSamw  */
83da6c28aaSamw #include <smbsrv/smb_incl.h>
84da6c28aaSamw #include <smbsrv/smb_fsops.h>
85da6c28aaSamw #include <sys/pathname.h>
86da6c28aaSamw #include <sys/sdt.h>
87dc20a302Sas200622 #include <sys/nbmlock.h>
88da6c28aaSamw 
89da6c28aaSamw uint32_t smb_is_executable(char *path);
90da6c28aaSamw static void smb_node_delete_on_close(smb_node_t *node);
91da6c28aaSamw 
92da6c28aaSamw #define	VALIDATE_DIR_NODE(_dir_, _node_) \
93da6c28aaSamw     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
94da6c28aaSamw     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
95da6c28aaSamw     ASSERT((_dir_)->dir_snode != (_node_));
96da6c28aaSamw 
97faa1795aSjb150015 static boolean_t	smb_node_initialized = B_FALSE;
98faa1795aSjb150015 static smb_llist_t	smb_node_hash_table[SMBND_HASH_MASK+1];
99faa1795aSjb150015 
100faa1795aSjb150015 /*
101faa1795aSjb150015  * smb_node_init
102faa1795aSjb150015  *
103faa1795aSjb150015  * Initialization of the SMB node layer.
104faa1795aSjb150015  *
105faa1795aSjb150015  * This function is not multi-thread safe. The caller must make sure only one
106faa1795aSjb150015  * thread makes the call.
107faa1795aSjb150015  */
108faa1795aSjb150015 int
109faa1795aSjb150015 smb_node_init(void)
110faa1795aSjb150015 {
111faa1795aSjb150015 	int	i;
112faa1795aSjb150015 
113faa1795aSjb150015 	if (smb_node_initialized)
114faa1795aSjb150015 		return (0);
115faa1795aSjb150015 
116faa1795aSjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
117faa1795aSjb150015 		smb_llist_constructor(&smb_node_hash_table[i],
118faa1795aSjb150015 		    sizeof (smb_node_t), offsetof(smb_node_t, n_lnd));
119faa1795aSjb150015 	}
120faa1795aSjb150015 	smb_node_initialized = B_TRUE;
121faa1795aSjb150015 	return (0);
122faa1795aSjb150015 }
123faa1795aSjb150015 
124faa1795aSjb150015 /*
125faa1795aSjb150015  * smb_node_fini
126faa1795aSjb150015  *
127faa1795aSjb150015  * This function is not multi-thread safe. The caller must make sure only one
128faa1795aSjb150015  * thread makes the call.
129faa1795aSjb150015  */
130faa1795aSjb150015 void
131faa1795aSjb150015 smb_node_fini(void)
132faa1795aSjb150015 {
133faa1795aSjb150015 	int	i;
134faa1795aSjb150015 
135faa1795aSjb150015 	if (!smb_node_initialized)
136faa1795aSjb150015 		return;
137faa1795aSjb150015 
138faa1795aSjb150015 #ifdef DEBUG
139faa1795aSjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
140faa1795aSjb150015 		smb_node_t	*node;
141faa1795aSjb150015 
142faa1795aSjb150015 		/*
143faa1795aSjb150015 		 * The following sequence is just intended for sanity check.
144faa1795aSjb150015 		 * This will have to be modified when the code goes into
145faa1795aSjb150015 		 * production.
146faa1795aSjb150015 		 *
147faa1795aSjb150015 		 * The SMB node hash table should be emtpy at this point. If the
148faa1795aSjb150015 		 * hash table is not empty a panic will be triggered.
149faa1795aSjb150015 		 *
150faa1795aSjb150015 		 * The reason why SMB nodes are still remaining in the hash
151faa1795aSjb150015 		 * table is problably due to a mismatch between calls to
152faa1795aSjb150015 		 * smb_node_lookup() and smb_node_release(). You must track that
153faa1795aSjb150015 		 * down.
154faa1795aSjb150015 		 */
155faa1795aSjb150015 		node = smb_llist_head(&smb_node_hash_table[i]);
156faa1795aSjb150015 		ASSERT(node == NULL);
157faa1795aSjb150015 	}
158faa1795aSjb150015 #endif
159faa1795aSjb150015 
160faa1795aSjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
161faa1795aSjb150015 		smb_llist_destructor(&smb_node_hash_table[i]);
162faa1795aSjb150015 	}
163faa1795aSjb150015 	smb_node_initialized = B_FALSE;
164faa1795aSjb150015 }
165faa1795aSjb150015 
166da6c28aaSamw /*
167da6c28aaSamw  * smb_node_lookup()
168da6c28aaSamw  *
169da6c28aaSamw  * NOTE: This routine should only be called by the file system interface layer,
170da6c28aaSamw  * and not by SMB.
171da6c28aaSamw  *
172da6c28aaSamw  * smb_node_lookup() is called upon successful lookup, mkdir, and create
173da6c28aaSamw  * (for both non-streams and streams).  In each of these cases, a held vnode is
174*7f667e74Sjose borrego  * passed into this routine.  If a new smb_node is created it will take its
175*7f667e74Sjose borrego  * own hold on the vnode.  The caller's hold therefore still belongs to, and
176*7f667e74Sjose borrego  * should be released by, the caller.
177da6c28aaSamw  *
178da6c28aaSamw  * A reference is taken on the smb_node whether found in the hash table
179da6c28aaSamw  * or newly created.
180da6c28aaSamw  *
181da6c28aaSamw  * If an smb_node needs to be created, a reference is also taken on the
182da6c28aaSamw  * dir_snode (if passed in).
183da6c28aaSamw  *
184da6c28aaSamw  * See smb_node_release() for details on the release of these references.
185da6c28aaSamw  */
186da6c28aaSamw 
187da6c28aaSamw /*ARGSUSED*/
188da6c28aaSamw smb_node_t *
189da6c28aaSamw smb_node_lookup(
190da6c28aaSamw     struct smb_request	*sr,
191da6c28aaSamw     struct open_param	*op,
192da6c28aaSamw     cred_t		*cred,
193da6c28aaSamw     vnode_t		*vp,
194da6c28aaSamw     char		*od_name,
195da6c28aaSamw     smb_node_t		*dir_snode,
196da6c28aaSamw     smb_node_t		*unnamed_node,
197da6c28aaSamw     smb_attr_t		*attr)
198da6c28aaSamw {
199da6c28aaSamw 	smb_llist_t		*node_hdr;
200da6c28aaSamw 	smb_node_t		*node;
201da6c28aaSamw 	uint32_t		hashkey = 0;
202c8ec8eeaSjose borrego 	fsid_t			fsid;
203da6c28aaSamw 	int			error;
204da6c28aaSamw 	krw_t			lock_mode;
205da6c28aaSamw 	vnode_t			*unnamed_vp = NULL;
206da6c28aaSamw 
207da6c28aaSamw 	/*
208da6c28aaSamw 	 * smb_vop_getattr() is called here instead of smb_fsop_getattr(),
209da6c28aaSamw 	 * because the node may not yet exist.  We also do not want to call
210da6c28aaSamw 	 * it with the list lock held.
211da6c28aaSamw 	 */
212da6c28aaSamw 
213da6c28aaSamw 	if (unnamed_node)
214da6c28aaSamw 		unnamed_vp = unnamed_node->vp;
215da6c28aaSamw 
216da6c28aaSamw 	/*
217da6c28aaSamw 	 * This getattr is performed on behalf of the server
218da6c28aaSamw 	 * that's why kcred is used not the user's cred
219da6c28aaSamw 	 */
220da6c28aaSamw 	attr->sa_mask = SMB_AT_ALL;
221dc20a302Sas200622 	error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred);
222da6c28aaSamw 	if (error)
223da6c28aaSamw 		return (NULL);
224da6c28aaSamw 
225c8ec8eeaSjose borrego 	if (sr && sr->tid_tree) {
226da6c28aaSamw 		/*
227c8ec8eeaSjose borrego 		 * The fsid for a file is that of the tree, even
228da6c28aaSamw 		 * if the file resides in a different mountpoint
229da6c28aaSamw 		 * under the share.
230da6c28aaSamw 		 */
231c8ec8eeaSjose borrego 		fsid = SMB_TREE_FSID(sr->tid_tree);
232da6c28aaSamw 	} else {
233da6c28aaSamw 		/*
234da6c28aaSamw 		 * This should be getting executed only for the
235c8ec8eeaSjose borrego 		 * tree root smb_node.
236da6c28aaSamw 		 */
237c8ec8eeaSjose borrego 		fsid = vp->v_vfsp->vfs_fsid;
238da6c28aaSamw 	}
239da6c28aaSamw 
240c8ec8eeaSjose borrego 	hashkey = fsid.val[0] + attr->sa_vattr.va_nodeid;
241da6c28aaSamw 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
242faa1795aSjb150015 	node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)];
243da6c28aaSamw 	lock_mode = RW_READER;
244da6c28aaSamw 
245da6c28aaSamw 	smb_llist_enter(node_hdr, lock_mode);
246da6c28aaSamw 	for (;;) {
247da6c28aaSamw 		node = list_head(&node_hdr->ll_list);
248da6c28aaSamw 		while (node) {
249da6c28aaSamw 			ASSERT(node->n_magic == SMB_NODE_MAGIC);
250da6c28aaSamw 			ASSERT(node->n_hash_bucket == node_hdr);
251da6c28aaSamw 			if ((node->n_hashkey == hashkey) && (node->vp == vp)) {
252da6c28aaSamw 				smb_rwx_xenter(&node->n_lock);
253da6c28aaSamw 				DTRACE_PROBE1(smb_node_lookup_hit,
254da6c28aaSamw 				    smb_node_t *, node);
255da6c28aaSamw 				switch (node->n_state) {
256da6c28aaSamw 				case SMB_NODE_STATE_AVAILABLE:
257da6c28aaSamw 					/* The node was found. */
258da6c28aaSamw 					node->n_refcnt++;
259da6c28aaSamw 					if ((node->dir_snode == NULL) &&
260da6c28aaSamw 					    (dir_snode != NULL) &&
261da6c28aaSamw 					    (strcmp(od_name, "..") != 0) &&
262da6c28aaSamw 					    (strcmp(od_name, ".") != 0)) {
263da6c28aaSamw 						VALIDATE_DIR_NODE(dir_snode,
264da6c28aaSamw 						    node);
265da6c28aaSamw 						node->dir_snode = dir_snode;
266da6c28aaSamw 						smb_node_ref(dir_snode);
267da6c28aaSamw 					}
268da6c28aaSamw 					node->attr = *attr;
269dc20a302Sas200622 					node->n_size = attr->sa_vattr.va_size;
270da6c28aaSamw 
271da6c28aaSamw 					smb_audit_node(node);
272da6c28aaSamw 					smb_rwx_xexit(&node->n_lock);
273da6c28aaSamw 					smb_llist_exit(node_hdr);
274da6c28aaSamw 					return (node);
275da6c28aaSamw 
276da6c28aaSamw 				case SMB_NODE_STATE_DESTROYING:
277da6c28aaSamw 					/*
278da6c28aaSamw 					 * Although the node exists it is about
279da6c28aaSamw 					 * to be destroyed. We act as it hasn't
280da6c28aaSamw 					 * been found.
281da6c28aaSamw 					 */
282da6c28aaSamw 					smb_rwx_xexit(&node->n_lock);
283da6c28aaSamw 					break;
284da6c28aaSamw 				default:
285da6c28aaSamw 					/*
286da6c28aaSamw 					 * Although the node exists it is in an
287da6c28aaSamw 					 * unknown state. We act as it hasn't
288da6c28aaSamw 					 * been found.
289da6c28aaSamw 					 */
290da6c28aaSamw 					ASSERT(0);
291da6c28aaSamw 					smb_rwx_xexit(&node->n_lock);
292da6c28aaSamw 					break;
293da6c28aaSamw 				}
294da6c28aaSamw 			}
295da6c28aaSamw 			node = smb_llist_next(node_hdr, node);
296da6c28aaSamw 		}
297da6c28aaSamw 		if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) {
298da6c28aaSamw 			lock_mode = RW_WRITER;
299da6c28aaSamw 			continue;
300da6c28aaSamw 		}
301da6c28aaSamw 		break;
302da6c28aaSamw 	}
303faa1795aSjb150015 	node = kmem_cache_alloc(sr->sr_server->si_cache_node, KM_SLEEP);
304da6c28aaSamw 	bzero(node, sizeof (smb_node_t));
305da6c28aaSamw 
306da6c28aaSamw 	node->n_state = SMB_NODE_STATE_AVAILABLE;
307da6c28aaSamw 	node->n_hash_bucket = node_hdr;
308faa1795aSjb150015 	node->n_sr = sr;
309faa1795aSjb150015 	node->vp = vp;
310*7f667e74Sjose borrego 	VN_HOLD(node->vp);
311faa1795aSjb150015 	node->n_hashkey = hashkey;
312faa1795aSjb150015 	node->n_refcnt = 1;
313faa1795aSjb150015 	node->attr = *attr;
314faa1795aSjb150015 	node->flags |= NODE_FLAGS_ATTR_VALID;
315faa1795aSjb150015 	node->n_size = node->attr.sa_vattr.va_size;
316faa1795aSjb150015 	node->n_orig_session_id = sr->session->s_kid;
317faa1795aSjb150015 	node->n_orig_uid = crgetuid(sr->user_cr);
318faa1795aSjb150015 	node->n_cache = sr->sr_server->si_cache_node;
319da6c28aaSamw 
320faa1795aSjb150015 	ASSERT(od_name);
321faa1795aSjb150015 	(void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
322faa1795aSjb150015 
323da6c28aaSamw 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
324da6c28aaSamw 	    offsetof(smb_ofile_t, f_nnd));
325da6c28aaSamw 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
326da6c28aaSamw 	    offsetof(smb_lock_t, l_lnd));
327da6c28aaSamw 
328da6c28aaSamw 
329cbfb650aScp160787 	if (strcmp(od_name, XATTR_DIR) == 0)
330cbfb650aScp160787 		node->flags |= NODE_XATTR_DIR;
331da6c28aaSamw 	if (op)
332da6c28aaSamw 		node->flags |= smb_is_executable(op->fqi.last_comp);
333da6c28aaSamw 
334da6c28aaSamw 	if (dir_snode) {
335da6c28aaSamw 		smb_node_ref(dir_snode);
336da6c28aaSamw 		node->dir_snode = dir_snode;
337da6c28aaSamw 		ASSERT(dir_snode->dir_snode != node);
338da6c28aaSamw 		ASSERT((dir_snode->vp->v_xattrdir) ||
339da6c28aaSamw 		    (dir_snode->vp->v_type == VDIR));
340da6c28aaSamw 	}
341da6c28aaSamw 
342da6c28aaSamw 	if (unnamed_node) {
343da6c28aaSamw 		smb_node_ref(unnamed_node);
344da6c28aaSamw 		node->unnamed_stream_node = unnamed_node;
345da6c28aaSamw 	}
346da6c28aaSamw 
347da6c28aaSamw 	smb_rwx_init(&node->n_lock);
348da6c28aaSamw 	node->n_magic = SMB_NODE_MAGIC;
349da6c28aaSamw 	smb_audit_buf_node_create(node);
350da6c28aaSamw 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
351da6c28aaSamw 	smb_audit_node(node);
352da6c28aaSamw 	smb_llist_insert_head(node_hdr, node);
353faa1795aSjb150015 
354da6c28aaSamw 	smb_llist_exit(node_hdr);
355da6c28aaSamw 	return (node);
356da6c28aaSamw }
357da6c28aaSamw 
358da6c28aaSamw /*
359da6c28aaSamw  * smb_stream_node_lookup()
360da6c28aaSamw  *
361da6c28aaSamw  * Note: stream_name (the name that will be stored in the "od_name" field
362da6c28aaSamw  * of a stream's smb_node) is the same as the on-disk name for the stream
363da6c28aaSamw  * except that it does not have SMB_STREAM_PREFIX prepended.
364da6c28aaSamw  */
365da6c28aaSamw 
366da6c28aaSamw smb_node_t *
367da6c28aaSamw smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode,
368da6c28aaSamw     vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr)
369da6c28aaSamw {
370da6c28aaSamw 	smb_node_t	*xattrdir_node;
371da6c28aaSamw 	smb_node_t	*snode;
372da6c28aaSamw 	smb_attr_t	tmp_attr;
373da6c28aaSamw 
374da6c28aaSamw 	xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR,
375da6c28aaSamw 	    fnode, NULL, &tmp_attr);
376da6c28aaSamw 
377da6c28aaSamw 	if (xattrdir_node == NULL)
378da6c28aaSamw 		return (NULL);
379da6c28aaSamw 
380da6c28aaSamw 	snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node,
381da6c28aaSamw 	    fnode, ret_attr);
382da6c28aaSamw 
383da6c28aaSamw 	(void) smb_node_release(xattrdir_node);
384da6c28aaSamw 	return (snode);
385da6c28aaSamw }
386da6c28aaSamw 
387da6c28aaSamw 
388da6c28aaSamw /*
389da6c28aaSamw  * This function should be called whenever a reference is needed on an
390da6c28aaSamw  * smb_node pointer.  The copy of an smb_node pointer from one non-local
391da6c28aaSamw  * data structure to another requires a reference to be taken on the smb_node
392da6c28aaSamw  * (unless the usage is localized).  Each data structure deallocation routine
393da6c28aaSamw  * will call smb_node_release() on its smb_node pointers.
394da6c28aaSamw  *
395da6c28aaSamw  * In general, an smb_node pointer residing in a structure should never be
396da6c28aaSamw  * stale.  A node pointer may be NULL, however, and care should be taken
397da6c28aaSamw  * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid.
398da6c28aaSamw  * Care also needs to be taken with respect to racing deallocations of a
399da6c28aaSamw  * structure.
400da6c28aaSamw  */
401da6c28aaSamw 
402da6c28aaSamw void
403da6c28aaSamw smb_node_ref(smb_node_t *node)
404da6c28aaSamw {
405da6c28aaSamw 	ASSERT(node);
406da6c28aaSamw 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
407da6c28aaSamw 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
408da6c28aaSamw 
409da6c28aaSamw 	smb_rwx_xenter(&node->n_lock);
410da6c28aaSamw 	node->n_refcnt++;
411da6c28aaSamw 	ASSERT(node->n_refcnt);
412da6c28aaSamw 	DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node);
413da6c28aaSamw 	smb_audit_node(node);
414da6c28aaSamw 	smb_rwx_xexit(&node->n_lock);
415da6c28aaSamw }
416da6c28aaSamw 
417da6c28aaSamw /*
418da6c28aaSamw  * smb_node_lookup() takes a hold on an smb_node, whether found in the
419da6c28aaSamw  * hash table or newly created.  This hold is expected to be released
420da6c28aaSamw  * in the following manner.
421da6c28aaSamw  *
422da6c28aaSamw  * smb_node_lookup() takes an address of an smb_node pointer.  This should
423da6c28aaSamw  * be getting passed down via a lookup (whether path name or component), mkdir,
424da6c28aaSamw  * create.  If the original smb_node pointer resides in a data structure, then
425da6c28aaSamw  * the deallocation routine for the data structure is responsible for calling
426da6c28aaSamw  * smb_node_release() on the smb_node pointer.  Alternatively,
427da6c28aaSamw  * smb_node_release() can be called as soon as the smb_node pointer is no longer
428da6c28aaSamw  * needed.  In this case, callers are responsible for setting an embedded
429da6c28aaSamw  * pointer to NULL if it is known that the last reference is being released.
430da6c28aaSamw  *
431da6c28aaSamw  * If the passed-in address of the smb_node pointer belongs to a local variable,
432da6c28aaSamw  * then the caller with the local variable should call smb_node_release()
433da6c28aaSamw  * directly.
434da6c28aaSamw  *
435da6c28aaSamw  * smb_node_release() itself will call smb_node_release() on a node's dir_snode,
436da6c28aaSamw  * as smb_node_lookup() takes a hold on dir_snode.
437da6c28aaSamw  */
438da6c28aaSamw void
439da6c28aaSamw smb_node_release(smb_node_t *node)
440da6c28aaSamw {
441da6c28aaSamw 	ASSERT(node);
442da6c28aaSamw 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
443da6c28aaSamw 
444da6c28aaSamw 	smb_rwx_xenter(&node->n_lock);
445da6c28aaSamw 	ASSERT(node->n_refcnt);
446da6c28aaSamw 	DTRACE_PROBE1(smb_node_release, smb_node_t *, node);
447da6c28aaSamw 	if (--node->n_refcnt == 0) {
448da6c28aaSamw 		switch (node->n_state) {
449da6c28aaSamw 
450da6c28aaSamw 		case SMB_NODE_STATE_AVAILABLE:
451da6c28aaSamw 			node->n_state = SMB_NODE_STATE_DESTROYING;
452da6c28aaSamw 			smb_rwx_xexit(&node->n_lock);
453da6c28aaSamw 
454da6c28aaSamw 			smb_llist_enter(node->n_hash_bucket, RW_WRITER);
455da6c28aaSamw 			smb_llist_remove(node->n_hash_bucket, node);
456da6c28aaSamw 			smb_llist_exit(node->n_hash_bucket);
457da6c28aaSamw 
458da6c28aaSamw 			/*
459da6c28aaSamw 			 * Check if the file was deleted
460da6c28aaSamw 			 */
461da6c28aaSamw 			smb_node_delete_on_close(node);
462da6c28aaSamw 			node->n_magic = (uint32_t)~SMB_NODE_MAGIC;
463da6c28aaSamw 
464da6c28aaSamw 			/* These lists should be empty. */
465da6c28aaSamw 			smb_llist_destructor(&node->n_ofile_list);
466da6c28aaSamw 			smb_llist_destructor(&node->n_lock_list);
467da6c28aaSamw 
468da6c28aaSamw 			if (node->dir_snode) {
469da6c28aaSamw 				ASSERT(node->dir_snode->n_magic ==
470da6c28aaSamw 				    SMB_NODE_MAGIC);
471da6c28aaSamw 				smb_node_release(node->dir_snode);
472da6c28aaSamw 			}
473da6c28aaSamw 
474da6c28aaSamw 			if (node->unnamed_stream_node) {
475da6c28aaSamw 				ASSERT(node->unnamed_stream_node->n_magic ==
476da6c28aaSamw 				    SMB_NODE_MAGIC);
477da6c28aaSamw 				smb_node_release(node->unnamed_stream_node);
478da6c28aaSamw 			}
479da6c28aaSamw 
480da6c28aaSamw 			ASSERT(node->vp);
481da6c28aaSamw 			VN_RELE(node->vp);
482da6c28aaSamw 
483da6c28aaSamw 			smb_audit_buf_node_destroy(node);
484da6c28aaSamw 			smb_rwx_destroy(&node->n_lock);
485faa1795aSjb150015 			kmem_cache_free(node->n_cache, node);
486da6c28aaSamw 			return;
487da6c28aaSamw 
488da6c28aaSamw 		default:
489da6c28aaSamw 			ASSERT(0);
490da6c28aaSamw 			break;
491da6c28aaSamw 		}
492da6c28aaSamw 	}
493da6c28aaSamw 	smb_audit_node(node);
494da6c28aaSamw 	smb_rwx_xexit(&node->n_lock);
495da6c28aaSamw }
496da6c28aaSamw 
497da6c28aaSamw static void
498da6c28aaSamw smb_node_delete_on_close(smb_node_t *node)
499da6c28aaSamw {
500da6c28aaSamw 	smb_node_t	*d_snode;
501da6c28aaSamw 	int		rc = 0;
502da6c28aaSamw 
503da6c28aaSamw 	d_snode = node->dir_snode;
504da6c28aaSamw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
505da6c28aaSamw 
506da6c28aaSamw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
507da6c28aaSamw 		ASSERT(node->od_name != NULL);
508da6c28aaSamw 		if (node->attr.sa_vattr.va_type == VDIR)
509da6c28aaSamw 			rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
510da6c28aaSamw 			    d_snode, node->od_name, 1);
511da6c28aaSamw 		else
512da6c28aaSamw 			rc = smb_fsop_remove(0, node->delete_on_close_cred,
513da6c28aaSamw 			    d_snode, node->od_name, 1);
514da6c28aaSamw 		smb_cred_rele(node->delete_on_close_cred);
515da6c28aaSamw 	}
516da6c28aaSamw 	if (rc != 0)
517da6c28aaSamw 		cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
518da6c28aaSamw 		    node->od_name, rc);
519da6c28aaSamw 	DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
520da6c28aaSamw }
521da6c28aaSamw 
522da6c28aaSamw /*
523da6c28aaSamw  * smb_node_rename()
524da6c28aaSamw  *
525da6c28aaSamw  */
526da6c28aaSamw int
527da6c28aaSamw smb_node_rename(
528da6c28aaSamw     smb_node_t	*from_dir_snode,
529da6c28aaSamw     smb_node_t	*ret_snode,
530da6c28aaSamw     smb_node_t	*to_dir_snode,
531da6c28aaSamw     char	*to_name)
532da6c28aaSamw {
533da6c28aaSamw 	ASSERT(from_dir_snode);
534da6c28aaSamw 	ASSERT(to_dir_snode);
535da6c28aaSamw 	ASSERT(ret_snode);
536da6c28aaSamw 	ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC);
537da6c28aaSamw 	ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC);
538da6c28aaSamw 	ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC);
539da6c28aaSamw 	ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
540da6c28aaSamw 	ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
541da6c28aaSamw 	ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE);
542da6c28aaSamw 
543da6c28aaSamw 	smb_node_ref(to_dir_snode);
544da6c28aaSamw 	smb_rwx_xenter(&ret_snode->n_lock);
545da6c28aaSamw 	ret_snode->dir_snode = to_dir_snode;
546da6c28aaSamw 	smb_rwx_xexit(&ret_snode->n_lock);
547da6c28aaSamw 	ASSERT(to_dir_snode->dir_snode != ret_snode);
548da6c28aaSamw 	ASSERT((to_dir_snode->vp->v_xattrdir) ||
549da6c28aaSamw 	    (to_dir_snode->vp->v_type == VDIR));
550da6c28aaSamw 	smb_node_release(from_dir_snode);
551da6c28aaSamw 
552da6c28aaSamw 	(void) strcpy(ret_snode->od_name, to_name);
553da6c28aaSamw 
554da6c28aaSamw 	/*
555da6c28aaSamw 	 * XXX Need to update attributes?
556da6c28aaSamw 	 */
557da6c28aaSamw 
558da6c28aaSamw 	return (0);
559da6c28aaSamw }
560da6c28aaSamw 
561da6c28aaSamw int
562faa1795aSjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root)
563da6c28aaSamw {
564da6c28aaSamw 	smb_attr_t	va;
565faa1795aSjb150015 	int		error;
566faa1795aSjb150015 	uint32_t	hashkey;
567faa1795aSjb150015 	smb_llist_t	*node_hdr;
568faa1795aSjb150015 	smb_node_t	*node;
569da6c28aaSamw 
570da6c28aaSamw 	/*
571da6c28aaSamw 	 * Take an explicit hold on rootdir.  This goes with the
572da6c28aaSamw 	 * corresponding release in smb_node_root_fini()/smb_node_release().
573da6c28aaSamw 	 */
574faa1795aSjb150015 	VN_HOLD(vp);
575da6c28aaSamw 
576faa1795aSjb150015 	va.sa_mask = SMB_AT_ALL;
577faa1795aSjb150015 	error = smb_vop_getattr(vp, NULL, &va, 0, kcred);
578faa1795aSjb150015 	if (error) {
579faa1795aSjb150015 		VN_RELE(vp);
580faa1795aSjb150015 		return (error);
581faa1795aSjb150015 	}
582da6c28aaSamw 
583faa1795aSjb150015 	hashkey = vp->v_vfsp->vfs_fsid.val[0] + va.sa_vattr.va_nodeid;
584faa1795aSjb150015 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
585faa1795aSjb150015 	node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)];
586faa1795aSjb150015 
587faa1795aSjb150015 	node = kmem_cache_alloc(sv->si_cache_node, KM_SLEEP);
588faa1795aSjb150015 	bzero(node, sizeof (smb_node_t));
589faa1795aSjb150015 
590faa1795aSjb150015 	node->n_state = SMB_NODE_STATE_AVAILABLE;
591faa1795aSjb150015 	node->n_hash_bucket = node_hdr;
592faa1795aSjb150015 	node->vp = vp;
593faa1795aSjb150015 	node->n_hashkey = hashkey;
594faa1795aSjb150015 	node->n_refcnt = 1;
595faa1795aSjb150015 	node->attr = va;
596faa1795aSjb150015 	node->flags |= NODE_FLAGS_ATTR_VALID;
597faa1795aSjb150015 	node->n_size = node->attr.sa_vattr.va_size;
598faa1795aSjb150015 	node->n_cache = sv->si_cache_node;
599faa1795aSjb150015 	(void) strlcpy(node->od_name, ROOTVOL, sizeof (node->od_name));
600faa1795aSjb150015 
601faa1795aSjb150015 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
602faa1795aSjb150015 	    offsetof(smb_ofile_t, f_nnd));
603faa1795aSjb150015 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
604faa1795aSjb150015 	    offsetof(smb_lock_t, l_lnd));
605faa1795aSjb150015 
606faa1795aSjb150015 	smb_rwx_init(&node->n_lock);
607faa1795aSjb150015 	node->n_magic = SMB_NODE_MAGIC;
608faa1795aSjb150015 	smb_audit_buf_node_create(node);
609faa1795aSjb150015 
610faa1795aSjb150015 	sv->si_root_smb_node = node;
611faa1795aSjb150015 
612faa1795aSjb150015 	smb_audit_node(node);
613faa1795aSjb150015 	smb_llist_enter(node_hdr, RW_WRITER);
614faa1795aSjb150015 	smb_llist_insert_head(node_hdr, node);
615faa1795aSjb150015 	smb_llist_exit(node_hdr);
616faa1795aSjb150015 
617faa1795aSjb150015 	*root = node;
618faa1795aSjb150015 
619da6c28aaSamw 	return (0);
620da6c28aaSamw }
621da6c28aaSamw 
622da6c28aaSamw /*
623da6c28aaSamw  * smb_node_get_size
624da6c28aaSamw  */
6256537f381Sas200622 u_offset_t
6266537f381Sas200622 smb_node_get_size(smb_node_t *node, smb_attr_t *attr)
627da6c28aaSamw {
6286537f381Sas200622 	u_offset_t size;
629da6c28aaSamw 
630da6c28aaSamw 	if (attr->sa_vattr.va_type == VDIR)
631da6c28aaSamw 		return (0);
632da6c28aaSamw 
633da6c28aaSamw 	smb_rwx_xenter(&node->n_lock);
634da6c28aaSamw 	if (node && (node->flags & NODE_FLAGS_SET_SIZE))
635da6c28aaSamw 		size = node->n_size;
636da6c28aaSamw 	else
637da6c28aaSamw 		size = attr->sa_vattr.va_size;
638da6c28aaSamw 	smb_rwx_xexit(&node->n_lock);
639da6c28aaSamw 	return (size);
640da6c28aaSamw }
641da6c28aaSamw 
642da6c28aaSamw static int
643da6c28aaSamw timeval_cmp(timestruc_t *a, timestruc_t *b)
644da6c28aaSamw {
645da6c28aaSamw 	if (a->tv_sec < b->tv_sec)
646da6c28aaSamw 		return (-1);
647da6c28aaSamw 	if (a->tv_sec > b->tv_sec)
648da6c28aaSamw 		return (1);
649da6c28aaSamw 	/* Seconds are equal compare tv_nsec */
650da6c28aaSamw 	if (a->tv_nsec < b->tv_nsec)
651da6c28aaSamw 		return (-1);
652da6c28aaSamw 	return (a->tv_nsec > b->tv_nsec);
653da6c28aaSamw }
654da6c28aaSamw 
655da6c28aaSamw /*
656da6c28aaSamw  * smb_node_set_time
657da6c28aaSamw  *
658da6c28aaSamw  * This function will update the time stored in the node and
659c8ec8eeaSjose borrego  * set the appropriate flags. If there is nothing to update,
660c8ec8eeaSjose borrego  * the function will return without any updates.  The update
661c8ec8eeaSjose borrego  * is only in the node level and the attribute in the file system
662c8ec8eeaSjose borrego  * will be updated when client close the file.
663da6c28aaSamw  */
664da6c28aaSamw void
665da6c28aaSamw smb_node_set_time(struct smb_node *node, struct timestruc *crtime,
666da6c28aaSamw     struct timestruc *mtime, struct timestruc *atime,
667da6c28aaSamw     struct timestruc *ctime, unsigned int what)
668da6c28aaSamw {
669c8ec8eeaSjose borrego 	if (what == 0)
670da6c28aaSamw 		return;
671da6c28aaSamw 
672da6c28aaSamw 	if ((what & SMB_AT_CRTIME && crtime == 0) ||
673da6c28aaSamw 	    (what & SMB_AT_MTIME && mtime == 0) ||
674da6c28aaSamw 	    (what & SMB_AT_ATIME && atime == 0) ||
675c8ec8eeaSjose borrego 	    (what & SMB_AT_CTIME && ctime == 0))
676da6c28aaSamw 		return;
677c8ec8eeaSjose borrego 
678c8ec8eeaSjose borrego 	smb_rwx_xenter(&node->n_lock);
679da6c28aaSamw 
680da6c28aaSamw 	if ((what & SMB_AT_CRTIME) &&
681da6c28aaSamw 	    timeval_cmp((timestruc_t *)&node->attr.sa_crtime,
682da6c28aaSamw 	    crtime) != 0) {
683da6c28aaSamw 		node->what |= SMB_AT_CRTIME;
684da6c28aaSamw 		node->attr.sa_crtime = *((timestruc_t *)crtime);
685da6c28aaSamw 	}
686da6c28aaSamw 
687da6c28aaSamw 	if ((what & SMB_AT_MTIME) &&
688da6c28aaSamw 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime,
689da6c28aaSamw 	    mtime) != 0) {
690da6c28aaSamw 		node->what |= SMB_AT_MTIME;
691da6c28aaSamw 		node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime);
692da6c28aaSamw 	}
693da6c28aaSamw 
694da6c28aaSamw 	if ((what & SMB_AT_ATIME) &&
695da6c28aaSamw 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime,
696da6c28aaSamw 	    atime) != 0) {
697da6c28aaSamw 			node->what |= SMB_AT_ATIME;
698da6c28aaSamw 			node->attr.sa_vattr.va_atime = *((timestruc_t *)atime);
699da6c28aaSamw 	}
700da6c28aaSamw 
701da6c28aaSamw 	/*
702da6c28aaSamw 	 * The ctime handling is trickier. It has three scenarios.
703da6c28aaSamw 	 * 1. Only ctime need to be set and it is the same as the ctime
704da6c28aaSamw 	 *    stored in the node. (update not necessary)
705da6c28aaSamw 	 * 2. The ctime is the same as the ctime stored in the node but
706da6c28aaSamw 	 *    is not the only time need to be set. (update required)
707da6c28aaSamw 	 * 3. The ctime need to be set and is not the same as the ctime
708da6c28aaSamw 	 *    stored in the node. (update required)
709da6c28aaSamw 	 * Unlike other time setting, the ctime needs to be set even when
710da6c28aaSamw 	 * it is the same as the ctime in the node if there are other time
711da6c28aaSamw 	 * needs to be set (#2). This will ensure the ctime not being
712da6c28aaSamw 	 * updated when other times are being updated in the file system.
713da6c28aaSamw 	 *
714da6c28aaSamw 	 * Retained file rules:
715da6c28aaSamw 	 *
716da6c28aaSamw 	 * 1. Don't add SMB_AT_CTIME to node->what by default because the
717da6c28aaSamw 	 *    request will be rejected by filesystem
718da6c28aaSamw 	 * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e.
719da6c28aaSamw 	 *    any request for changing ctime on these files should have
720da6c28aaSamw 	 *    been already rejected
721da6c28aaSamw 	 */
722da6c28aaSamw 	node->what |= SMB_AT_CTIME;
723da6c28aaSamw 	if (what & SMB_AT_CTIME) {
724da6c28aaSamw 		if ((what == SMB_AT_CTIME) &&
725da6c28aaSamw 		    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime,
726da6c28aaSamw 		    ctime) == 0) {
727da6c28aaSamw 			node->what &= ~SMB_AT_CTIME;
728da6c28aaSamw 		} else {
729da6c28aaSamw 			gethrestime(&node->attr.sa_vattr.va_ctime);
730da6c28aaSamw 		}
731da6c28aaSamw 	} else {
732da6c28aaSamw 		gethrestime(&node->attr.sa_vattr.va_ctime);
733da6c28aaSamw 	}
734da6c28aaSamw 	smb_rwx_xexit(&node->n_lock);
735da6c28aaSamw }
736da6c28aaSamw 
737da6c28aaSamw 
738da6c28aaSamw timestruc_t *
739da6c28aaSamw smb_node_get_crtime(smb_node_t *node)
740da6c28aaSamw {
741da6c28aaSamw 	return ((timestruc_t *)&node->attr.sa_crtime);
742da6c28aaSamw }
743da6c28aaSamw 
744da6c28aaSamw timestruc_t *
745da6c28aaSamw smb_node_get_atime(smb_node_t *node)
746da6c28aaSamw {
747da6c28aaSamw 	return ((timestruc_t *)&node->attr.sa_vattr.va_atime);
748da6c28aaSamw }
749da6c28aaSamw 
750da6c28aaSamw timestruc_t *
751da6c28aaSamw smb_node_get_ctime(smb_node_t *node)
752da6c28aaSamw {
753da6c28aaSamw 	return ((timestruc_t *)&node->attr.sa_vattr.va_ctime);
754da6c28aaSamw }
755da6c28aaSamw 
756da6c28aaSamw timestruc_t *
757da6c28aaSamw smb_node_get_mtime(smb_node_t *node)
758da6c28aaSamw {
759da6c28aaSamw 	return ((timestruc_t *)&node->attr.sa_vattr.va_mtime);
760da6c28aaSamw }
761da6c28aaSamw 
762da6c28aaSamw /*
763da6c28aaSamw  * smb_node_set_dosattr
764da6c28aaSamw  *
765da6c28aaSamw  * Parse the specified DOS attributes and, if they have been modified,
766da6c28aaSamw  * update the node cache. This call should be followed by a
767da6c28aaSamw  * smb_sync_fsattr() call to write the attribute changes to filesystem.
768da6c28aaSamw  */
769da6c28aaSamw void
7703db3f65cSamw smb_node_set_dosattr(smb_node_t *node, uint32_t dosattr)
771da6c28aaSamw {
7723db3f65cSamw 	uint32_t mode = dosattr & (FILE_ATTRIBUTE_ARCHIVE |
7733db3f65cSamw 	    FILE_ATTRIBUTE_READONLY |
7743db3f65cSamw 	    FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
775da6c28aaSamw 
776da6c28aaSamw 	smb_rwx_xenter(&node->n_lock);
777da6c28aaSamw 	if (node->attr.sa_dosattr != mode) {
778da6c28aaSamw 		node->attr.sa_dosattr = mode;
779da6c28aaSamw 		node->what |= SMB_AT_DOSATTR;
780da6c28aaSamw 	}
781da6c28aaSamw 	smb_rwx_xexit(&node->n_lock);
782da6c28aaSamw }
783da6c28aaSamw 
784da6c28aaSamw /*
785c8ec8eeaSjose borrego  * smb_node_get_dosattr()
786da6c28aaSamw  *
787c8ec8eeaSjose borrego  * This function is used to provide clients with information as to whether
788c8ec8eeaSjose borrego  * the readonly bit is set.  Hence both the node attribute cache (which
789c8ec8eeaSjose borrego  * reflects the on-disk attributes) and node->readonly_creator (which
790c8ec8eeaSjose borrego  * reflects whether a readonly set is pending from a readonly create) are
791c8ec8eeaSjose borrego  * checked.  In the latter case, the readonly attribute should be visible to
792c8ec8eeaSjose borrego  * all clients even though the readonly creator fid is immune to the readonly
793c8ec8eeaSjose borrego  * bit until close.
794da6c28aaSamw  */
795c8ec8eeaSjose borrego 
796da6c28aaSamw uint32_t
797da6c28aaSamw smb_node_get_dosattr(smb_node_t *node)
798da6c28aaSamw {
799c8ec8eeaSjose borrego 	uint32_t dosattr = node->attr.sa_dosattr;
800c8ec8eeaSjose borrego 
801c8ec8eeaSjose borrego 	if (node->readonly_creator)
802c8ec8eeaSjose borrego 		dosattr |= FILE_ATTRIBUTE_READONLY;
803c8ec8eeaSjose borrego 
804c8ec8eeaSjose borrego 	if (!dosattr)
805c8ec8eeaSjose borrego 		dosattr = FILE_ATTRIBUTE_NORMAL;
806c8ec8eeaSjose borrego 
807c8ec8eeaSjose borrego 	return (dosattr);
808da6c28aaSamw }
809da6c28aaSamw 
810da6c28aaSamw int
811da6c28aaSamw smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr)
812da6c28aaSamw {
813da6c28aaSamw 	int	rc = -1;
814da6c28aaSamw 
815da6c28aaSamw 	smb_rwx_xenter(&node->n_lock);
816da6c28aaSamw 	if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) &&
817da6c28aaSamw 	    !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) {
818da6c28aaSamw 		crhold(cr);
819da6c28aaSamw 		node->delete_on_close_cred = cr;
820da6c28aaSamw 		node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
821da6c28aaSamw 		rc = 0;
822da6c28aaSamw 	}
823da6c28aaSamw 	smb_rwx_xexit(&node->n_lock);
824da6c28aaSamw 	return (rc);
825da6c28aaSamw }
826da6c28aaSamw 
827da6c28aaSamw void
828da6c28aaSamw smb_node_reset_delete_on_close(smb_node_t *node)
829da6c28aaSamw {
830da6c28aaSamw 	smb_rwx_xenter(&node->n_lock);
831da6c28aaSamw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
832da6c28aaSamw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
833da6c28aaSamw 		crfree(node->delete_on_close_cred);
834da6c28aaSamw 		node->delete_on_close_cred = NULL;
835da6c28aaSamw 	}
836da6c28aaSamw 	smb_rwx_xexit(&node->n_lock);
837da6c28aaSamw }
838dc20a302Sas200622 
839dc20a302Sas200622 /*
8403ad684d6Sjb150015  * smb_node_open_check
841dc20a302Sas200622  *
842dc20a302Sas200622  * check file sharing rules for current open request
843dc20a302Sas200622  * against all existing opens for a file.
844dc20a302Sas200622  *
845dc20a302Sas200622  * Returns NT_STATUS_SHARING_VIOLATION if there is any
846dc20a302Sas200622  * sharing conflict, otherwise returns NT_STATUS_SUCCESS.
847dc20a302Sas200622  */
848dc20a302Sas200622 uint32_t
849dc20a302Sas200622 smb_node_open_check(struct smb_node *node, cred_t *cr,
850dc20a302Sas200622     uint32_t desired_access, uint32_t share_access)
851dc20a302Sas200622 {
852dc20a302Sas200622 	smb_ofile_t *of;
853dc20a302Sas200622 	uint32_t status;
854dc20a302Sas200622 
855dc20a302Sas200622 	ASSERT(node);
856dc20a302Sas200622 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
857dc20a302Sas200622 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
858dc20a302Sas200622 
859dc20a302Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
860dc20a302Sas200622 	of = smb_llist_head(&node->n_ofile_list);
861dc20a302Sas200622 	while (of) {
8623ad684d6Sjb150015 		status = smb_ofile_open_check(of, cr, desired_access,
8633ad684d6Sjb150015 		    share_access);
8643ad684d6Sjb150015 
8653ad684d6Sjb150015 		switch (status) {
8663ad684d6Sjb150015 		case NT_STATUS_INVALID_HANDLE:
8673ad684d6Sjb150015 		case NT_STATUS_SUCCESS:
8683ad684d6Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
8693ad684d6Sjb150015 			break;
8703ad684d6Sjb150015 		default:
8713ad684d6Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
872dc20a302Sas200622 			smb_llist_exit(&node->n_ofile_list);
873dc20a302Sas200622 			return (status);
874dc20a302Sas200622 		}
875dc20a302Sas200622 	}
8763ad684d6Sjb150015 
877dc20a302Sas200622 	smb_llist_exit(&node->n_ofile_list);
878dc20a302Sas200622 	return (NT_STATUS_SUCCESS);
879dc20a302Sas200622 }
880dc20a302Sas200622 
881dc20a302Sas200622 uint32_t
882dc20a302Sas200622 smb_node_rename_check(struct smb_node *node)
883dc20a302Sas200622 {
8843ad684d6Sjb150015 	struct smb_ofile *of;
8853ad684d6Sjb150015 	uint32_t status;
886dc20a302Sas200622 
887dc20a302Sas200622 	ASSERT(node);
888dc20a302Sas200622 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
889dc20a302Sas200622 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
890dc20a302Sas200622 
891dc20a302Sas200622 	/*
892dc20a302Sas200622 	 * Intra-CIFS check
893dc20a302Sas200622 	 */
894dc20a302Sas200622 
895dc20a302Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
8963ad684d6Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
8973ad684d6Sjb150015 	while (of) {
8983ad684d6Sjb150015 		status = smb_ofile_rename_check(of);
899dc20a302Sas200622 
9003ad684d6Sjb150015 		switch (status) {
9013ad684d6Sjb150015 		case NT_STATUS_INVALID_HANDLE:
9023ad684d6Sjb150015 		case NT_STATUS_SUCCESS:
9033ad684d6Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
9043ad684d6Sjb150015 			break;
9053ad684d6Sjb150015 		default:
9063ad684d6Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
907dc20a302Sas200622 			smb_llist_exit(&node->n_ofile_list);
9083ad684d6Sjb150015 			return (status);
909dc20a302Sas200622 		}
910dc20a302Sas200622 	}
911dc20a302Sas200622 	smb_llist_exit(&node->n_ofile_list);
912dc20a302Sas200622 
913dc20a302Sas200622 	/*
914dc20a302Sas200622 	 * system-wide share check
915dc20a302Sas200622 	 */
916dc20a302Sas200622 
917dc20a302Sas200622 	if (nbl_share_conflict(node->vp, NBL_RENAME, NULL))
918dc20a302Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
919dc20a302Sas200622 	else
920dc20a302Sas200622 		return (NT_STATUS_SUCCESS);
921dc20a302Sas200622 }
922dc20a302Sas200622 
9233ad684d6Sjb150015 uint32_t
924dc20a302Sas200622 smb_node_delete_check(smb_node_t *node)
925dc20a302Sas200622 {
9263ad684d6Sjb150015 	smb_ofile_t *of;
9273ad684d6Sjb150015 	uint32_t status;
928dc20a302Sas200622 
929dc20a302Sas200622 	ASSERT(node);
930dc20a302Sas200622 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
931dc20a302Sas200622 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
932dc20a302Sas200622 
933dc20a302Sas200622 	if (node->attr.sa_vattr.va_type == VDIR)
934dc20a302Sas200622 		return (NT_STATUS_SUCCESS);
935dc20a302Sas200622 
936dc20a302Sas200622 	/*
937dc20a302Sas200622 	 * intra-CIFS check
938dc20a302Sas200622 	 */
939dc20a302Sas200622 
940dc20a302Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
9413ad684d6Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
9423ad684d6Sjb150015 	while (of) {
9433ad684d6Sjb150015 		status = smb_ofile_delete_check(of);
9443ad684d6Sjb150015 
9453ad684d6Sjb150015 		switch (status) {
9463ad684d6Sjb150015 		case NT_STATUS_INVALID_HANDLE:
9473ad684d6Sjb150015 		case NT_STATUS_SUCCESS:
9483ad684d6Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
9493ad684d6Sjb150015 			break;
9503ad684d6Sjb150015 		default:
9513ad684d6Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
952dc20a302Sas200622 			smb_llist_exit(&node->n_ofile_list);
9533ad684d6Sjb150015 			return (status);
954dc20a302Sas200622 		}
955dc20a302Sas200622 	}
956dc20a302Sas200622 	smb_llist_exit(&node->n_ofile_list);
957dc20a302Sas200622 
958dc20a302Sas200622 	/*
959dc20a302Sas200622 	 * system-wide share check
960dc20a302Sas200622 	 */
961dc20a302Sas200622 
962dc20a302Sas200622 	if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL))
963dc20a302Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
964dc20a302Sas200622 	else
965dc20a302Sas200622 		return (NT_STATUS_SUCCESS);
966dc20a302Sas200622 }
967dc20a302Sas200622 
968dc20a302Sas200622 /*
969dc20a302Sas200622  * smb_node_start_crit()
970dc20a302Sas200622  *
971dc20a302Sas200622  * Enter critical region for share reservations.
972dc20a302Sas200622  * See comments above smb_fsop_shrlock().
973dc20a302Sas200622  */
974dc20a302Sas200622 
975dc20a302Sas200622 void
976dc20a302Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode)
977dc20a302Sas200622 {
978dc20a302Sas200622 	rw_enter(&node->n_share_lock, mode);
979dc20a302Sas200622 	nbl_start_crit(node->vp, mode);
980dc20a302Sas200622 }
981dc20a302Sas200622 
982dc20a302Sas200622 /*
983dc20a302Sas200622  * smb_node_end_crit()
984dc20a302Sas200622  *
985dc20a302Sas200622  * Exit critical region for share reservations.
986dc20a302Sas200622  */
987dc20a302Sas200622 
988dc20a302Sas200622 void
989dc20a302Sas200622 smb_node_end_crit(smb_node_t *node)
990dc20a302Sas200622 {
991dc20a302Sas200622 	nbl_end_crit(node->vp);
992dc20a302Sas200622 	rw_exit(&node->n_share_lock);
993dc20a302Sas200622 }
994dc20a302Sas200622 
995dc20a302Sas200622 int
996dc20a302Sas200622 smb_node_in_crit(smb_node_t *node)
997dc20a302Sas200622 {
998dc20a302Sas200622 	return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock));
999dc20a302Sas200622 }
1000