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