xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_node.c (revision 03100a6332bd4edc7a53091fcf7c9a7131bcdaa7)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% 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 
88 uint32_t smb_is_executable(char *path);
89 static void smb_node_delete_on_close(smb_node_t *node);
90 
91 uint32_t	smb_node_hit = 0;
92 uint32_t	smb_node_miss = 0;
93 uint32_t	smb_node_alloc = 0;
94 uint32_t	smb_node_free = 0;
95 
96 #define	VALIDATE_DIR_NODE(_dir_, _node_) \
97     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
98     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
99     ASSERT((_dir_)->dir_snode != (_node_));
100 
101 /*
102  * smb_node_lookup()
103  *
104  * NOTE: This routine should only be called by the file system interface layer,
105  * and not by SMB.
106  *
107  * smb_node_lookup() is called upon successful lookup, mkdir, and create
108  * (for both non-streams and streams).  In each of these cases, a held vnode is
109  * passed into this routine.  If an smb_node already exists for this vnode,
110  * the vp is released.  Otherwise, a new smb_node will be created and the
111  * reference will be held until the refcnt on the node goes to 0 (see
112  * smb_node_release()).
113  *
114  * A reference is taken on the smb_node whether found in the hash table
115  * or newly created.
116  *
117  * If an smb_node needs to be created, a reference is also taken on the
118  * dir_snode (if passed in).
119  *
120  * See smb_node_release() for details on the release of these references.
121  */
122 
123 /*ARGSUSED*/
124 smb_node_t *
125 smb_node_lookup(
126     struct smb_request	*sr,
127     struct open_param	*op,
128     cred_t		*cred,
129     vnode_t		*vp,
130     char		*od_name,
131     smb_node_t		*dir_snode,
132     smb_node_t		*unnamed_node,
133     smb_attr_t		*attr)
134 {
135 	smb_llist_t		*node_hdr;
136 	smb_node_t		*node;
137 	uint32_t		hashkey = 0;
138 	fs_desc_t		fsd;
139 	int			error;
140 	krw_t			lock_mode;
141 	caller_context_t 	ct;
142 	vnode_t			*unnamed_vp = NULL;
143 
144 	/*
145 	 * smb_vop_getattr() is called here instead of smb_fsop_getattr(),
146 	 * because the node may not yet exist.  We also do not want to call
147 	 * it with the list lock held.
148 	 */
149 
150 	if (unnamed_node)
151 		unnamed_vp = unnamed_node->vp;
152 
153 	/*
154 	 * This getattr is performed on behalf of the server
155 	 * that's why kcred is used not the user's cred
156 	 */
157 	smb_get_caller_context(sr, &ct);
158 	attr->sa_mask = SMB_AT_ALL;
159 	error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred, &ct);
160 	if (error)
161 		return (NULL);
162 
163 	if (sr) {
164 		if (sr->tid_tree) {
165 			/*
166 			 * The fsd for a file is that of the tree, even
167 			 * if the file resides in a different mountpoint
168 			 * under the share.
169 			 */
170 			fsd = sr->tid_tree->t_fsd;
171 		} else {
172 			/*
173 			 * This should be getting executed only for the
174 			 * tree's root smb_node.
175 			 */
176 			fsd = vp->v_vfsp->vfs_fsid;
177 		}
178 	} else {
179 		fsd = vp->v_vfsp->vfs_fsid;
180 	}
181 
182 	hashkey = fsd.val[0] + attr->sa_vattr.va_nodeid;
183 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
184 	node_hdr = &smb_info.node_hash_table[(hashkey & SMBND_HASH_MASK)];
185 	lock_mode = RW_READER;
186 
187 	smb_llist_enter(node_hdr, lock_mode);
188 	for (;;) {
189 		node = list_head(&node_hdr->ll_list);
190 		while (node) {
191 			ASSERT(node->n_magic == SMB_NODE_MAGIC);
192 			ASSERT(node->n_hash_bucket == node_hdr);
193 			if ((node->n_hashkey == hashkey) && (node->vp == vp)) {
194 				smb_rwx_xenter(&node->n_lock);
195 				DTRACE_PROBE1(smb_node_lookup_hit,
196 				    smb_node_t *, node);
197 				switch (node->n_state) {
198 				case SMB_NODE_STATE_AVAILABLE:
199 					/* The node was found. */
200 					node->n_refcnt++;
201 					if ((node->dir_snode == NULL) &&
202 					    (dir_snode != NULL) &&
203 					    (strcmp(od_name, "..") != 0) &&
204 					    (strcmp(od_name, ".") != 0)) {
205 						VALIDATE_DIR_NODE(dir_snode,
206 						    node);
207 						node->dir_snode = dir_snode;
208 						smb_node_ref(dir_snode);
209 					}
210 					node->attr = *attr;
211 
212 					smb_audit_node(node);
213 					smb_rwx_xexit(&node->n_lock);
214 					smb_llist_exit(node_hdr);
215 					VN_RELE(vp);
216 					return (node);
217 
218 				case SMB_NODE_STATE_DESTROYING:
219 					/*
220 					 * Although the node exists it is about
221 					 * to be destroyed. We act as it hasn't
222 					 * been found.
223 					 */
224 					smb_rwx_xexit(&node->n_lock);
225 					break;
226 				default:
227 					/*
228 					 * Although the node exists it is in an
229 					 * unknown state. We act as it hasn't
230 					 * been found.
231 					 */
232 					ASSERT(0);
233 					smb_rwx_xexit(&node->n_lock);
234 					break;
235 				}
236 			}
237 			node = smb_llist_next(node_hdr, node);
238 		}
239 		if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) {
240 			lock_mode = RW_WRITER;
241 			continue;
242 		}
243 		break;
244 	}
245 	node = kmem_cache_alloc(smb_info.si_cache_node, KM_SLEEP);
246 	smb_node_alloc++;
247 
248 	bzero(node, sizeof (smb_node_t));
249 
250 	node->n_state = SMB_NODE_STATE_AVAILABLE;
251 	node->n_hash_bucket = node_hdr;
252 
253 	if (fsd_chkcap(&fsd, FSOLF_READONLY) > 0) {
254 		node->flags |= NODE_READ_ONLY;
255 	}
256 
257 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
258 	    offsetof(smb_ofile_t, f_nnd));
259 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
260 	    offsetof(smb_lock_t, l_lnd));
261 	node->n_hashkey = hashkey;
262 	node->n_refcnt = 1;
263 
264 	if (sr) {
265 		node->n_orig_session_id = sr->session->s_kid;
266 		node->n_orig_uid = crgetuid(sr->user_cr);
267 	}
268 
269 	node->vp = vp;
270 
271 	ASSERT(od_name);
272 	(void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
273 	node->tree_fsd = fsd;
274 
275 	if (op)
276 		node->flags |= smb_is_executable(op->fqi.last_comp);
277 
278 	if (dir_snode) {
279 		smb_node_ref(dir_snode);
280 		node->dir_snode = dir_snode;
281 		ASSERT(dir_snode->dir_snode != node);
282 		ASSERT((dir_snode->vp->v_xattrdir) ||
283 		    (dir_snode->vp->v_type == VDIR));
284 	}
285 
286 	if (unnamed_node) {
287 		smb_node_ref(unnamed_node);
288 		node->unnamed_stream_node = unnamed_node;
289 	}
290 
291 	node->attr = *attr;
292 	node->flags |= NODE_FLAGS_ATTR_VALID;
293 	node->n_size = node->attr.sa_vattr.va_size;
294 
295 	smb_rwx_init(&node->n_lock);
296 	node->n_magic = SMB_NODE_MAGIC;
297 	smb_audit_buf_node_create(node);
298 
299 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
300 	smb_audit_node(node);
301 	smb_llist_insert_head(node_hdr, node);
302 	smb_llist_exit(node_hdr);
303 	return (node);
304 }
305 
306 /*
307  * smb_stream_node_lookup()
308  *
309  * Note: stream_name (the name that will be stored in the "od_name" field
310  * of a stream's smb_node) is the same as the on-disk name for the stream
311  * except that it does not have SMB_STREAM_PREFIX prepended.
312  */
313 
314 smb_node_t *
315 smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode,
316     vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr)
317 {
318 	smb_node_t	*xattrdir_node;
319 	smb_node_t	*snode;
320 	smb_attr_t	tmp_attr;
321 
322 	xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR,
323 	    fnode, NULL, &tmp_attr);
324 
325 	if (xattrdir_node == NULL)
326 		return (NULL);
327 
328 	snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node,
329 	    fnode, ret_attr);
330 
331 	/*
332 	 * The following VN_HOLD is necessary because the caller will VN_RELE
333 	 * xattrdirvp in the case of an error.  (xattrdir_node has the original
334 	 * hold on the vnode, which the smb_node_release() call below will
335 	 * release.)
336 	 */
337 	if (snode == NULL) {
338 		VN_HOLD(xattrdirvp);
339 	}
340 	(void) smb_node_release(xattrdir_node);
341 	return (snode);
342 }
343 
344 
345 /*
346  * This function should be called whenever a reference is needed on an
347  * smb_node pointer.  The copy of an smb_node pointer from one non-local
348  * data structure to another requires a reference to be taken on the smb_node
349  * (unless the usage is localized).  Each data structure deallocation routine
350  * will call smb_node_release() on its smb_node pointers.
351  *
352  * In general, an smb_node pointer residing in a structure should never be
353  * stale.  A node pointer may be NULL, however, and care should be taken
354  * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid.
355  * Care also needs to be taken with respect to racing deallocations of a
356  * structure.
357  */
358 
359 void
360 smb_node_ref(smb_node_t *node)
361 {
362 	ASSERT(node);
363 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
364 	ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE);
365 
366 	smb_rwx_xenter(&node->n_lock);
367 	node->n_refcnt++;
368 	ASSERT(node->n_refcnt);
369 	DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node);
370 	smb_audit_node(node);
371 	smb_rwx_xexit(&node->n_lock);
372 }
373 
374 /*
375  * smb_node_lookup() takes a hold on an smb_node, whether found in the
376  * hash table or newly created.  This hold is expected to be released
377  * in the following manner.
378  *
379  * smb_node_lookup() takes an address of an smb_node pointer.  This should
380  * be getting passed down via a lookup (whether path name or component), mkdir,
381  * create.  If the original smb_node pointer resides in a data structure, then
382  * the deallocation routine for the data structure is responsible for calling
383  * smb_node_release() on the smb_node pointer.  Alternatively,
384  * smb_node_release() can be called as soon as the smb_node pointer is no longer
385  * needed.  In this case, callers are responsible for setting an embedded
386  * pointer to NULL if it is known that the last reference is being released.
387  *
388  * If the passed-in address of the smb_node pointer belongs to a local variable,
389  * then the caller with the local variable should call smb_node_release()
390  * directly.
391  *
392  * smb_node_release() itself will call smb_node_release() on a node's dir_snode,
393  * as smb_node_lookup() takes a hold on dir_snode.
394  */
395 
396 void
397 smb_node_release(smb_node_t *node)
398 {
399 	ASSERT(node);
400 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
401 
402 	smb_rwx_xenter(&node->n_lock);
403 	ASSERT(node->n_refcnt);
404 	DTRACE_PROBE1(smb_node_release, smb_node_t *, node);
405 	if (--node->n_refcnt == 0) {
406 		switch (node->n_state) {
407 
408 		case SMB_NODE_STATE_AVAILABLE:
409 			node->n_state = SMB_NODE_STATE_DESTROYING;
410 			smb_rwx_xexit(&node->n_lock);
411 
412 			smb_llist_enter(node->n_hash_bucket, RW_WRITER);
413 			smb_llist_remove(node->n_hash_bucket, node);
414 			smb_llist_exit(node->n_hash_bucket);
415 
416 			/*
417 			 * Check if the file was deleted
418 			 */
419 			smb_node_delete_on_close(node);
420 			node->n_magic = (uint32_t)~SMB_NODE_MAGIC;
421 
422 			/* These lists should be empty. */
423 			smb_llist_destructor(&node->n_ofile_list);
424 			smb_llist_destructor(&node->n_lock_list);
425 
426 			if (node->dir_snode) {
427 				ASSERT(node->dir_snode->n_magic ==
428 				    SMB_NODE_MAGIC);
429 				smb_node_release(node->dir_snode);
430 			}
431 
432 			if (node->unnamed_stream_node) {
433 				ASSERT(node->unnamed_stream_node->n_magic ==
434 				    SMB_NODE_MAGIC);
435 				smb_node_release(node->unnamed_stream_node);
436 			}
437 
438 			ASSERT(node->vp);
439 			VN_RELE(node->vp);
440 
441 			smb_audit_buf_node_destroy(node);
442 			smb_rwx_destroy(&node->n_lock);
443 			kmem_cache_free(smb_info.si_cache_node, node);
444 			++smb_node_free;
445 			return;
446 
447 		default:
448 			ASSERT(0);
449 			break;
450 		}
451 	}
452 	smb_audit_node(node);
453 	smb_rwx_xexit(&node->n_lock);
454 }
455 
456 static void
457 smb_node_delete_on_close(smb_node_t *node)
458 {
459 	smb_node_t	*d_snode;
460 	int		rc = 0;
461 
462 	d_snode = node->dir_snode;
463 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
464 
465 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
466 		ASSERT(node->od_name != NULL);
467 		if (node->attr.sa_vattr.va_type == VDIR)
468 			rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
469 			    d_snode, node->od_name, 1);
470 		else
471 			rc = smb_fsop_remove(0, node->delete_on_close_cred,
472 			    d_snode, node->od_name, 1);
473 		smb_cred_rele(node->delete_on_close_cred);
474 	}
475 	if (rc != 0)
476 		cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
477 		    node->od_name, rc);
478 	DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
479 }
480 
481 /*
482  * smb_node_rename()
483  *
484  */
485 int
486 smb_node_rename(
487     smb_node_t	*from_dir_snode,
488     smb_node_t	*ret_snode,
489     smb_node_t	*to_dir_snode,
490     char	*to_name)
491 {
492 	ASSERT(from_dir_snode);
493 	ASSERT(to_dir_snode);
494 	ASSERT(ret_snode);
495 	ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC);
496 	ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC);
497 	ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC);
498 	ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
499 	ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE);
500 	ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE);
501 
502 	smb_node_ref(to_dir_snode);
503 	smb_rwx_xenter(&ret_snode->n_lock);
504 	ret_snode->dir_snode = to_dir_snode;
505 	smb_rwx_xexit(&ret_snode->n_lock);
506 	ASSERT(to_dir_snode->dir_snode != ret_snode);
507 	ASSERT((to_dir_snode->vp->v_xattrdir) ||
508 	    (to_dir_snode->vp->v_type == VDIR));
509 	smb_node_release(from_dir_snode);
510 
511 	(void) strcpy(ret_snode->od_name, to_name);
512 
513 	/*
514 	 * XXX Need to update attributes?
515 	 */
516 
517 	return (0);
518 }
519 
520 int
521 smb_node_root_init()
522 {
523 	smb_attr_t va;
524 
525 	/*
526 	 * Take an explicit hold on rootdir.  This goes with the
527 	 * corresponding release in smb_node_root_fini()/smb_node_release().
528 	 */
529 
530 	VN_HOLD(rootdir);
531 
532 	if ((smb_info.si_root_smb_node = smb_node_lookup(NULL, NULL, kcred,
533 	    rootdir, ROOTVOL, NULL, NULL, &va)) == NULL)
534 		return (-1);
535 	else
536 		return (0);
537 }
538 
539 void
540 smb_node_root_fini()
541 {
542 	if (smb_info.si_root_smb_node) {
543 		smb_node_release(smb_info.si_root_smb_node);
544 		smb_info.si_root_smb_node = NULL;
545 	}
546 }
547 
548 /*
549  * smb_node_get_size
550  */
551 uint64_t
552 smb_node_get_size(
553     smb_node_t		*node,
554     smb_attr_t		*attr)
555 {
556 	uint64_t	size;
557 
558 	if (attr->sa_vattr.va_type == VDIR)
559 		return (0);
560 
561 	smb_rwx_xenter(&node->n_lock);
562 	if (node && (node->flags & NODE_FLAGS_SET_SIZE))
563 		size = node->n_size;
564 	else
565 		size = attr->sa_vattr.va_size;
566 	smb_rwx_xexit(&node->n_lock);
567 	return (size);
568 }
569 
570 static int
571 timeval_cmp(timestruc_t *a, timestruc_t *b)
572 {
573 	if (a->tv_sec < b->tv_sec)
574 		return (-1);
575 	if (a->tv_sec > b->tv_sec)
576 		return (1);
577 	/* Seconds are equal compare tv_nsec */
578 	if (a->tv_nsec < b->tv_nsec)
579 		return (-1);
580 	return (a->tv_nsec > b->tv_nsec);
581 }
582 
583 /*
584  * smb_node_set_time
585  *
586  * This function will update the time stored in the node and
587  * set the appropriate flags. If there is nothing to update
588  * or the node is readonly, the function would return without
589  * any updates. The update is only in the node level and the
590  * attribute in the file system will be updated when client
591  * close the file.
592  */
593 void
594 smb_node_set_time(struct smb_node *node, struct timestruc *crtime,
595     struct timestruc *mtime, struct timestruc *atime,
596     struct timestruc *ctime, unsigned int what)
597 {
598 	smb_rwx_xenter(&node->n_lock);
599 	if (node->flags & NODE_READ_ONLY || what == 0) {
600 		smb_rwx_xexit(&node->n_lock);
601 		return;
602 	}
603 
604 	if ((what & SMB_AT_CRTIME && crtime == 0) ||
605 	    (what & SMB_AT_MTIME && mtime == 0) ||
606 	    (what & SMB_AT_ATIME && atime == 0) ||
607 	    (what & SMB_AT_CTIME && ctime == 0)) {
608 		smb_rwx_xexit(&node->n_lock);
609 		return;
610 	}
611 
612 	if ((what & SMB_AT_CRTIME) &&
613 	    timeval_cmp((timestruc_t *)&node->attr.sa_crtime,
614 	    crtime) != 0) {
615 		node->what |= SMB_AT_CRTIME;
616 		node->attr.sa_crtime = *((timestruc_t *)crtime);
617 	}
618 
619 	if ((what & SMB_AT_MTIME) &&
620 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime,
621 	    mtime) != 0) {
622 		node->what |= SMB_AT_MTIME;
623 		node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime);
624 	}
625 
626 	if ((what & SMB_AT_ATIME) &&
627 	    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime,
628 	    atime) != 0) {
629 			node->what |= SMB_AT_ATIME;
630 			node->attr.sa_vattr.va_atime = *((timestruc_t *)atime);
631 	}
632 
633 	/*
634 	 * The ctime handling is trickier. It has three scenarios.
635 	 * 1. Only ctime need to be set and it is the same as the ctime
636 	 *    stored in the node. (update not necessary)
637 	 * 2. The ctime is the same as the ctime stored in the node but
638 	 *    is not the only time need to be set. (update required)
639 	 * 3. The ctime need to be set and is not the same as the ctime
640 	 *    stored in the node. (update required)
641 	 * Unlike other time setting, the ctime needs to be set even when
642 	 * it is the same as the ctime in the node if there are other time
643 	 * needs to be set (#2). This will ensure the ctime not being
644 	 * updated when other times are being updated in the file system.
645 	 *
646 	 * Retained file rules:
647 	 *
648 	 * 1. Don't add SMB_AT_CTIME to node->what by default because the
649 	 *    request will be rejected by filesystem
650 	 * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e.
651 	 *    any request for changing ctime on these files should have
652 	 *    been already rejected
653 	 */
654 	node->what |= SMB_AT_CTIME;
655 	if (what & SMB_AT_CTIME) {
656 		if ((what == SMB_AT_CTIME) &&
657 		    timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime,
658 		    ctime) == 0) {
659 			node->what &= ~SMB_AT_CTIME;
660 		} else {
661 			gethrestime(&node->attr.sa_vattr.va_ctime);
662 		}
663 	} else {
664 		gethrestime(&node->attr.sa_vattr.va_ctime);
665 	}
666 	smb_rwx_xexit(&node->n_lock);
667 }
668 
669 
670 timestruc_t *
671 smb_node_get_crtime(smb_node_t *node)
672 {
673 	return ((timestruc_t *)&node->attr.sa_crtime);
674 }
675 
676 timestruc_t *
677 smb_node_get_atime(smb_node_t *node)
678 {
679 	return ((timestruc_t *)&node->attr.sa_vattr.va_atime);
680 }
681 
682 timestruc_t *
683 smb_node_get_ctime(smb_node_t *node)
684 {
685 	return ((timestruc_t *)&node->attr.sa_vattr.va_ctime);
686 }
687 
688 timestruc_t *
689 smb_node_get_mtime(smb_node_t *node)
690 {
691 	return ((timestruc_t *)&node->attr.sa_vattr.va_mtime);
692 }
693 
694 /*
695  * smb_node_set_dosattr
696  *
697  * Parse the specified DOS attributes and, if they have been modified,
698  * update the node cache. This call should be followed by a
699  * smb_sync_fsattr() call to write the attribute changes to filesystem.
700  */
701 void
702 smb_node_set_dosattr(smb_node_t *node, uint32_t dos_attr)
703 {
704 	unsigned int mode;  /* New mode */
705 
706 	mode = 0;
707 
708 	/* Handle the archive bit */
709 	if (dos_attr & SMB_FA_ARCHIVE)
710 		mode |= FILE_ATTRIBUTE_ARCHIVE;
711 
712 	/* Handle the readonly bit */
713 	if (dos_attr & SMB_FA_READONLY)
714 		mode |= FILE_ATTRIBUTE_READONLY;
715 
716 	/* Handle the hidden bit */
717 	if (dos_attr & SMB_FA_HIDDEN)
718 		mode |= FILE_ATTRIBUTE_HIDDEN;
719 
720 	/* Handle the system bit */
721 	if (dos_attr & SMB_FA_SYSTEM)
722 		mode |= FILE_ATTRIBUTE_SYSTEM;
723 
724 	smb_rwx_xenter(&node->n_lock);
725 	if (node->attr.sa_dosattr != mode) {
726 		node->attr.sa_dosattr = mode;
727 		node->what |= SMB_AT_DOSATTR;
728 	}
729 	smb_rwx_xexit(&node->n_lock);
730 }
731 
732 /*
733  * smb_node_get_dosattr
734  *
735  * This function will get dos attribute using the node.
736  */
737 uint32_t
738 smb_node_get_dosattr(smb_node_t *node)
739 {
740 	return (smb_mode_to_dos_attributes(&node->attr));
741 }
742 
743 int
744 smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr)
745 {
746 	int	rc = -1;
747 
748 	smb_rwx_xenter(&node->n_lock);
749 	if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) &&
750 	    !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) {
751 		crhold(cr);
752 		node->delete_on_close_cred = cr;
753 		node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
754 		rc = 0;
755 	}
756 	smb_rwx_xexit(&node->n_lock);
757 	return (rc);
758 }
759 
760 void
761 smb_node_reset_delete_on_close(smb_node_t *node)
762 {
763 	smb_rwx_xenter(&node->n_lock);
764 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
765 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
766 		crfree(node->delete_on_close_cred);
767 		node->delete_on_close_cred = NULL;
768 	}
769 	smb_rwx_xexit(&node->n_lock);
770 }
771