xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_node.c (revision a0aa776e20803c84edd153d9cb584fd67163aef3)
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 /*
227f667e74Sjose borrego  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24da6c28aaSamw  */
25da6c28aaSamw /*
26da6c28aaSamw  * SMB Node State Machine
27da6c28aaSamw  * ----------------------
28da6c28aaSamw  *
29fc724630SAlan Wright  *
30fc724630SAlan Wright  *		    +----------- Creation/Allocation
31da6c28aaSamw  *		    |
32fc724630SAlan Wright  *		    | T0
33da6c28aaSamw  *		    |
34da6c28aaSamw  *		    v
35fc724630SAlan Wright  *    +----------------------------+        T1
36fc724630SAlan Wright  *    |  SMB_NODE_STATE_AVAILABLE  |--------------------+
37fc724630SAlan Wright  *    +----------------------------+			|
38fc724630SAlan Wright  *		    |	     ^				|
39fc724630SAlan Wright  *		    |	     |				v
40fc724630SAlan Wright  *		    |	     |	  T2	+-------------------------------+
41fc724630SAlan Wright  *		    |	     |<---------| SMB_NODE_STATE_OPLOCK_GRANTED |
42fc724630SAlan Wright  *		    |	     |		+-------------------------------+
43fc724630SAlan Wright  *		    | T5     |				|
44fc724630SAlan Wright  *		    |	     |				| T3
45fc724630SAlan Wright  *		    |	     |				v
46fc724630SAlan Wright  *		    |	     |	  T4	+--------------------------------+
47fc724630SAlan Wright  *		    |	     +----------| SMB_NODE_STATE_OPLOCK_BREAKING |
48fc724630SAlan Wright  *		    |			+--------------------------------+
49fc724630SAlan Wright  *		    |
50fc724630SAlan Wright  *		    v
51da6c28aaSamw  *    +-----------------------------+
52fc724630SAlan Wright  *    |  SMB_NODE_STATE_DESTROYING  |
53fc724630SAlan Wright  *    +-----------------------------+
54fc724630SAlan Wright  *		    |
55fc724630SAlan Wright  *		    |
56fc724630SAlan Wright  *		    | T6
57fc724630SAlan Wright  *		    |
58fc724630SAlan Wright  *		    +----------> Deletion/Free
59da6c28aaSamw  *
60da6c28aaSamw  * Transition T0
61da6c28aaSamw  *
62da6c28aaSamw  *    This transition occurs in smb_node_lookup(). If the node looked for is
63da6c28aaSamw  *    not found in the has table a new node is created. The reference count is
64da6c28aaSamw  *    initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE.
65da6c28aaSamw  *
66da6c28aaSamw  * Transition T1
67da6c28aaSamw  *
68fc724630SAlan Wright  *    This transition occurs smb_oplock_acquire() during an OPEN.
69fc724630SAlan Wright  *
70fc724630SAlan Wright  * Transition T2
71fc724630SAlan Wright  *
72fc724630SAlan Wright  *    This transition occurs in smb_oplock_release(). The events triggering
73fc724630SAlan Wright  *    it are:
74fc724630SAlan Wright  *
75fc724630SAlan Wright  *	- LockingAndX sent by the client that was granted the oplock.
76fc724630SAlan Wright  *	- Closing of the file.
77fc724630SAlan Wright  *
78fc724630SAlan Wright  * Transition T3
79fc724630SAlan Wright  *
80fc724630SAlan Wright  *    This transition occurs in smb_oplock_break(). The events triggering
81fc724630SAlan Wright  *    it are:
82fc724630SAlan Wright  *
83fc724630SAlan Wright  *	- Another client wants to open the file.
84fc724630SAlan Wright  *	- A client is trying to delete the file.
85fc724630SAlan Wright  *	- A client is trying to rename the file.
86fc724630SAlan Wright  *	- A client is trying to set/modify  the file attributes.
87fc724630SAlan Wright  *
88fc724630SAlan Wright  * Transition T4
89fc724630SAlan Wright  *
90fc724630SAlan Wright  *    This transition occurs in smb_oplock_release or smb_oplock_break(). The
91fc724630SAlan Wright  *    events triggering it are:
92fc724630SAlan Wright  *
93fc724630SAlan Wright  *	- The client that was granting the oplock releases it (close or
94fc724630SAlan Wright  *	  LockingAndx).
95fc724630SAlan Wright  *	- The time alloted to release the oplock expired.
96fc724630SAlan Wright  *
97fc724630SAlan Wright  * Transition T5
98fc724630SAlan Wright  *
99da6c28aaSamw  *    This transition occurs in smb_node_release(). If the reference count
100da6c28aaSamw  *    drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more
101da6c28aaSamw  *    reference count will be given out for that node.
102da6c28aaSamw  *
103fc724630SAlan Wright  * Transition T6
104da6c28aaSamw  *
105da6c28aaSamw  *    This transition occurs in smb_node_release(). The structure is deleted.
106da6c28aaSamw  *
107da6c28aaSamw  * Comments
108da6c28aaSamw  * --------
109da6c28aaSamw  *
110da6c28aaSamw  *    The reason the smb node has 2 states is the following synchronization
111da6c28aaSamw  *    rule:
112da6c28aaSamw  *
113da6c28aaSamw  *    There's a mutex embedded in the node used to protect its fields and
114da6c28aaSamw  *    there's a lock embedded in the bucket of the hash table the node belongs
115da6c28aaSamw  *    to. To increment or to decrement the reference count the mutex must be
116da6c28aaSamw  *    entered. To insert the node into the bucket and to remove it from the
117da6c28aaSamw  *    bucket the lock must be entered in RW_WRITER mode. When both (mutex and
118da6c28aaSamw  *    lock) have to be entered, the lock has always to be entered first then
119da6c28aaSamw  *    the mutex. This prevents a deadlock between smb_node_lookup() and
120da6c28aaSamw  *    smb_node_release() from occurring. However, in smb_node_release() when the
121da6c28aaSamw  *    reference count drops to zero and triggers the deletion of the node, the
122da6c28aaSamw  *    mutex has to be released before entering the lock of the bucket (to
123da6c28aaSamw  *    remove the node). This creates a window during which the node that is
124da6c28aaSamw  *    about to be freed could be given out by smb_node_lookup(). To close that
125da6c28aaSamw  *    window the node is moved to the state SMB_NODE_STATE_DESTROYING before
126da6c28aaSamw  *    releasing the mutex. That way, even if smb_node_lookup() finds it, the
127da6c28aaSamw  *    state will indicate that the node should be treated as non existent (of
128da6c28aaSamw  *    course the state of the node should be tested/updated under the
129da6c28aaSamw  *    protection of the mutex).
130da6c28aaSamw  */
131da6c28aaSamw #include <smbsrv/smb_incl.h>
132da6c28aaSamw #include <smbsrv/smb_fsops.h>
1332c2961f8Sjose borrego #include <smbsrv/smb_kstat.h>
134da6c28aaSamw #include <sys/pathname.h>
135da6c28aaSamw #include <sys/sdt.h>
136dc20a302Sas200622 #include <sys/nbmlock.h>
137da6c28aaSamw 
1382c2961f8Sjose borrego uint32_t smb_is_executable(char *);
1392c2961f8Sjose borrego static void smb_node_delete_on_close(smb_node_t *);
1402c2961f8Sjose borrego static void smb_node_create_audit_buf(smb_node_t *, int);
1412c2961f8Sjose borrego static void smb_node_destroy_audit_buf(smb_node_t *);
1422c2961f8Sjose borrego static void smb_node_audit(smb_node_t *);
143037cac00Sjoyce mcintosh static smb_node_t *smb_node_alloc(char *, vnode_t *, smb_llist_t *, uint32_t);
1442c2961f8Sjose borrego static void smb_node_free(smb_node_t *);
1452c2961f8Sjose borrego static int smb_node_constructor(void *, void *, int);
1462c2961f8Sjose borrego static void smb_node_destructor(void *, void *);
1472c2961f8Sjose borrego static smb_llist_t *smb_node_get_hash(fsid_t *, smb_attr_t *, uint32_t *);
148e3f2c991SKeyur Desai 
149e3f2c991SKeyur Desai static void smb_node_init_cached_data(smb_node_t *);
150e3f2c991SKeyur Desai static void smb_node_clear_cached_data(smb_node_t *);
151e3f2c991SKeyur Desai 
152e3f2c991SKeyur Desai static void smb_node_init_cached_timestamps(smb_node_t *, smb_attr_t *);
153037cac00Sjoyce mcintosh static void smb_node_clear_cached_timestamps(smb_node_t *);
154037cac00Sjoyce mcintosh static void smb_node_get_cached_timestamps(smb_node_t *, smb_attr_t *);
155037cac00Sjoyce mcintosh static void smb_node_set_cached_timestamps(smb_node_t *, smb_attr_t *);
156da6c28aaSamw 
157e3f2c991SKeyur Desai static void smb_node_init_cached_allocsz(smb_node_t *, smb_attr_t *);
158e3f2c991SKeyur Desai static void smb_node_clear_cached_allocsz(smb_node_t *);
159e3f2c991SKeyur Desai static void smb_node_get_cached_allocsz(smb_node_t *, smb_attr_t *);
160e3f2c991SKeyur Desai static void smb_node_set_cached_allocsz(smb_node_t *, smb_attr_t *);
161e3f2c991SKeyur Desai 
162da6c28aaSamw #define	VALIDATE_DIR_NODE(_dir_, _node_) \
163da6c28aaSamw     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
164da6c28aaSamw     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
1651fcced4cSJordan Brown     ASSERT((_dir_)->n_dnode != (_node_));
166da6c28aaSamw 
167e3f2c991SKeyur Desai /* round sz to DEV_BSIZE block */
168e3f2c991SKeyur Desai #define	SMB_ALLOCSZ(sz)	(((sz) + DEV_BSIZE-1) & ~(DEV_BSIZE-1))
169e3f2c991SKeyur Desai 
1702c2961f8Sjose borrego static kmem_cache_t	*smb_node_cache = NULL;
171faa1795aSjb150015 static boolean_t	smb_node_initialized = B_FALSE;
172faa1795aSjb150015 static smb_llist_t	smb_node_hash_table[SMBND_HASH_MASK+1];
173faa1795aSjb150015 
174faa1795aSjb150015 /*
175faa1795aSjb150015  * smb_node_init
176faa1795aSjb150015  *
177faa1795aSjb150015  * Initialization of the SMB node layer.
178faa1795aSjb150015  *
179faa1795aSjb150015  * This function is not multi-thread safe. The caller must make sure only one
180faa1795aSjb150015  * thread makes the call.
181faa1795aSjb150015  */
182faa1795aSjb150015 int
183faa1795aSjb150015 smb_node_init(void)
184faa1795aSjb150015 {
185faa1795aSjb150015 	int	i;
186faa1795aSjb150015 
187faa1795aSjb150015 	if (smb_node_initialized)
188faa1795aSjb150015 		return (0);
1892c2961f8Sjose borrego 	smb_node_cache = kmem_cache_create(SMBSRV_KSTAT_NODE_CACHE,
1902c2961f8Sjose borrego 	    sizeof (smb_node_t), 8, smb_node_constructor, smb_node_destructor,
1912c2961f8Sjose borrego 	    NULL, NULL, NULL, 0);
192faa1795aSjb150015 
193faa1795aSjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
194faa1795aSjb150015 		smb_llist_constructor(&smb_node_hash_table[i],
195faa1795aSjb150015 		    sizeof (smb_node_t), offsetof(smb_node_t, n_lnd));
196faa1795aSjb150015 	}
197faa1795aSjb150015 	smb_node_initialized = B_TRUE;
198faa1795aSjb150015 	return (0);
199faa1795aSjb150015 }
200faa1795aSjb150015 
201faa1795aSjb150015 /*
202faa1795aSjb150015  * smb_node_fini
203faa1795aSjb150015  *
204faa1795aSjb150015  * This function is not multi-thread safe. The caller must make sure only one
205faa1795aSjb150015  * thread makes the call.
206faa1795aSjb150015  */
207faa1795aSjb150015 void
208faa1795aSjb150015 smb_node_fini(void)
209faa1795aSjb150015 {
210faa1795aSjb150015 	int	i;
211faa1795aSjb150015 
212faa1795aSjb150015 	if (!smb_node_initialized)
213faa1795aSjb150015 		return;
214faa1795aSjb150015 
215faa1795aSjb150015 #ifdef DEBUG
216faa1795aSjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
217faa1795aSjb150015 		smb_node_t	*node;
218faa1795aSjb150015 
219faa1795aSjb150015 		/*
220faa1795aSjb150015 		 * The following sequence is just intended for sanity check.
221faa1795aSjb150015 		 * This will have to be modified when the code goes into
222faa1795aSjb150015 		 * production.
223faa1795aSjb150015 		 *
224faa1795aSjb150015 		 * The SMB node hash table should be emtpy at this point. If the
225faa1795aSjb150015 		 * hash table is not empty a panic will be triggered.
226faa1795aSjb150015 		 *
227faa1795aSjb150015 		 * The reason why SMB nodes are still remaining in the hash
228faa1795aSjb150015 		 * table is problably due to a mismatch between calls to
229faa1795aSjb150015 		 * smb_node_lookup() and smb_node_release(). You must track that
230faa1795aSjb150015 		 * down.
231faa1795aSjb150015 		 */
232faa1795aSjb150015 		node = smb_llist_head(&smb_node_hash_table[i]);
233faa1795aSjb150015 		ASSERT(node == NULL);
234faa1795aSjb150015 	}
235faa1795aSjb150015 #endif
236faa1795aSjb150015 
237faa1795aSjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
238faa1795aSjb150015 		smb_llist_destructor(&smb_node_hash_table[i]);
239faa1795aSjb150015 	}
2402c2961f8Sjose borrego 	kmem_cache_destroy(smb_node_cache);
2412c2961f8Sjose borrego 	smb_node_cache = NULL;
242faa1795aSjb150015 	smb_node_initialized = B_FALSE;
243faa1795aSjb150015 }
244faa1795aSjb150015 
245da6c28aaSamw /*
246da6c28aaSamw  * smb_node_lookup()
247da6c28aaSamw  *
248da6c28aaSamw  * NOTE: This routine should only be called by the file system interface layer,
249da6c28aaSamw  * and not by SMB.
250da6c28aaSamw  *
251da6c28aaSamw  * smb_node_lookup() is called upon successful lookup, mkdir, and create
252da6c28aaSamw  * (for both non-streams and streams).  In each of these cases, a held vnode is
2537f667e74Sjose borrego  * passed into this routine.  If a new smb_node is created it will take its
2547f667e74Sjose borrego  * own hold on the vnode.  The caller's hold therefore still belongs to, and
2557f667e74Sjose borrego  * should be released by, the caller.
256da6c28aaSamw  *
257da6c28aaSamw  * A reference is taken on the smb_node whether found in the hash table
258da6c28aaSamw  * or newly created.
259da6c28aaSamw  *
260da6c28aaSamw  * If an smb_node needs to be created, a reference is also taken on the
2611fcced4cSJordan Brown  * dnode (if passed in).
262da6c28aaSamw  *
263da6c28aaSamw  * See smb_node_release() for details on the release of these references.
264da6c28aaSamw  */
265da6c28aaSamw 
266da6c28aaSamw /*ARGSUSED*/
267da6c28aaSamw smb_node_t *
268da6c28aaSamw smb_node_lookup(
269da6c28aaSamw     struct smb_request	*sr,
270da6c28aaSamw     struct open_param	*op,
271da6c28aaSamw     cred_t		*cred,
272da6c28aaSamw     vnode_t		*vp,
273da6c28aaSamw     char		*od_name,
2741fcced4cSJordan Brown     smb_node_t		*dnode,
2751fcced4cSJordan Brown     smb_node_t		*unode)
276da6c28aaSamw {
277da6c28aaSamw 	smb_llist_t		*node_hdr;
278da6c28aaSamw 	smb_node_t		*node;
279037cac00Sjoyce mcintosh 	smb_attr_t		attr;
280da6c28aaSamw 	uint32_t		hashkey = 0;
281c8ec8eeaSjose borrego 	fsid_t			fsid;
282da6c28aaSamw 	int			error;
283da6c28aaSamw 	krw_t			lock_mode;
284da6c28aaSamw 	vnode_t			*unnamed_vp = NULL;
285da6c28aaSamw 
286da6c28aaSamw 	/*
287da6c28aaSamw 	 * smb_vop_getattr() is called here instead of smb_fsop_getattr(),
288da6c28aaSamw 	 * because the node may not yet exist.  We also do not want to call
289da6c28aaSamw 	 * it with the list lock held.
290da6c28aaSamw 	 */
291da6c28aaSamw 
2921fcced4cSJordan Brown 	if (unode)
2931fcced4cSJordan Brown 		unnamed_vp = unode->vp;
294da6c28aaSamw 
295da6c28aaSamw 	/*
296da6c28aaSamw 	 * This getattr is performed on behalf of the server
297da6c28aaSamw 	 * that's why kcred is used not the user's cred
298da6c28aaSamw 	 */
299037cac00Sjoyce mcintosh 	attr.sa_mask = SMB_AT_ALL;
300037cac00Sjoyce mcintosh 	error = smb_vop_getattr(vp, unnamed_vp, &attr, 0, kcred);
301da6c28aaSamw 	if (error)
302da6c28aaSamw 		return (NULL);
303da6c28aaSamw 
304c8ec8eeaSjose borrego 	if (sr && sr->tid_tree) {
305da6c28aaSamw 		/*
306c8ec8eeaSjose borrego 		 * The fsid for a file is that of the tree, even
307da6c28aaSamw 		 * if the file resides in a different mountpoint
308da6c28aaSamw 		 * under the share.
309da6c28aaSamw 		 */
310c8ec8eeaSjose borrego 		fsid = SMB_TREE_FSID(sr->tid_tree);
311da6c28aaSamw 	} else {
312da6c28aaSamw 		/*
313da6c28aaSamw 		 * This should be getting executed only for the
314c8ec8eeaSjose borrego 		 * tree root smb_node.
315da6c28aaSamw 		 */
316c8ec8eeaSjose borrego 		fsid = vp->v_vfsp->vfs_fsid;
317da6c28aaSamw 	}
318da6c28aaSamw 
319037cac00Sjoyce mcintosh 	node_hdr = smb_node_get_hash(&fsid, &attr, &hashkey);
320da6c28aaSamw 	lock_mode = RW_READER;
321da6c28aaSamw 
322da6c28aaSamw 	smb_llist_enter(node_hdr, lock_mode);
323da6c28aaSamw 	for (;;) {
324da6c28aaSamw 		node = list_head(&node_hdr->ll_list);
325da6c28aaSamw 		while (node) {
326da6c28aaSamw 			ASSERT(node->n_magic == SMB_NODE_MAGIC);
327da6c28aaSamw 			ASSERT(node->n_hash_bucket == node_hdr);
328da6c28aaSamw 			if ((node->n_hashkey == hashkey) && (node->vp == vp)) {
3292c2961f8Sjose borrego 				mutex_enter(&node->n_mutex);
330da6c28aaSamw 				DTRACE_PROBE1(smb_node_lookup_hit,
331da6c28aaSamw 				    smb_node_t *, node);
332da6c28aaSamw 				switch (node->n_state) {
3332c2961f8Sjose borrego 				case SMB_NODE_STATE_OPLOCK_GRANTED:
3342c2961f8Sjose borrego 				case SMB_NODE_STATE_OPLOCK_BREAKING:
335da6c28aaSamw 				case SMB_NODE_STATE_AVAILABLE:
336da6c28aaSamw 					/* The node was found. */
337da6c28aaSamw 					node->n_refcnt++;
3381fcced4cSJordan Brown 					if ((node->n_dnode == NULL) &&
3391fcced4cSJordan Brown 					    (dnode != NULL) &&
340da6c28aaSamw 					    (strcmp(od_name, "..") != 0) &&
341da6c28aaSamw 					    (strcmp(od_name, ".") != 0)) {
3421fcced4cSJordan Brown 						VALIDATE_DIR_NODE(dnode, node);
3431fcced4cSJordan Brown 						node->n_dnode = dnode;
3441fcced4cSJordan Brown 						smb_node_ref(dnode);
345da6c28aaSamw 					}
346da6c28aaSamw 
3472c2961f8Sjose borrego 					smb_node_audit(node);
3482c2961f8Sjose borrego 					mutex_exit(&node->n_mutex);
349da6c28aaSamw 					smb_llist_exit(node_hdr);
350da6c28aaSamw 					return (node);
351da6c28aaSamw 
352da6c28aaSamw 				case SMB_NODE_STATE_DESTROYING:
353da6c28aaSamw 					/*
354da6c28aaSamw 					 * Although the node exists it is about
355da6c28aaSamw 					 * to be destroyed. We act as it hasn't
356da6c28aaSamw 					 * been found.
357da6c28aaSamw 					 */
3582c2961f8Sjose borrego 					mutex_exit(&node->n_mutex);
359da6c28aaSamw 					break;
360da6c28aaSamw 				default:
361da6c28aaSamw 					/*
362da6c28aaSamw 					 * Although the node exists it is in an
363da6c28aaSamw 					 * unknown state. We act as it hasn't
364da6c28aaSamw 					 * been found.
365da6c28aaSamw 					 */
366da6c28aaSamw 					ASSERT(0);
3672c2961f8Sjose borrego 					mutex_exit(&node->n_mutex);
368da6c28aaSamw 					break;
369da6c28aaSamw 				}
370da6c28aaSamw 			}
371da6c28aaSamw 			node = smb_llist_next(node_hdr, node);
372da6c28aaSamw 		}
373da6c28aaSamw 		if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) {
374da6c28aaSamw 			lock_mode = RW_WRITER;
375da6c28aaSamw 			continue;
376da6c28aaSamw 		}
377da6c28aaSamw 		break;
378da6c28aaSamw 	}
379037cac00Sjoyce mcintosh 	node = smb_node_alloc(od_name, vp, node_hdr, hashkey);
380faa1795aSjb150015 	node->n_orig_uid = crgetuid(sr->user_cr);
381da6c28aaSamw 
382da6c28aaSamw 	if (op)
383eb1d736bSafshin salek ardakani - Sun Microsystems - Irvine United States 		node->flags |= smb_is_executable(op->fqi.fq_last_comp);
384da6c28aaSamw 
3851fcced4cSJordan Brown 	if (dnode) {
3861fcced4cSJordan Brown 		smb_node_ref(dnode);
3871fcced4cSJordan Brown 		node->n_dnode = dnode;
3881fcced4cSJordan Brown 		ASSERT(dnode->n_dnode != node);
3891fcced4cSJordan Brown 		ASSERT((dnode->vp->v_xattrdir) ||
3901fcced4cSJordan Brown 		    (dnode->vp->v_type == VDIR));
391da6c28aaSamw 	}
392da6c28aaSamw 
3931fcced4cSJordan Brown 	if (unode) {
3941fcced4cSJordan Brown 		smb_node_ref(unode);
3951fcced4cSJordan Brown 		node->n_unode = unode;
396da6c28aaSamw 	}
397da6c28aaSamw 
398da6c28aaSamw 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
3992c2961f8Sjose borrego 	smb_node_audit(node);
400da6c28aaSamw 	smb_llist_insert_head(node_hdr, node);
401da6c28aaSamw 	smb_llist_exit(node_hdr);
402da6c28aaSamw 	return (node);
403da6c28aaSamw }
404da6c28aaSamw 
405da6c28aaSamw /*
406da6c28aaSamw  * smb_stream_node_lookup()
407da6c28aaSamw  *
408da6c28aaSamw  * Note: stream_name (the name that will be stored in the "od_name" field
409da6c28aaSamw  * of a stream's smb_node) is the same as the on-disk name for the stream
410da6c28aaSamw  * except that it does not have SMB_STREAM_PREFIX prepended.
411da6c28aaSamw  */
412da6c28aaSamw 
413da6c28aaSamw smb_node_t *
4142c2961f8Sjose borrego smb_stream_node_lookup(smb_request_t *sr, cred_t *cr, smb_node_t *fnode,
415037cac00Sjoyce mcintosh     vnode_t *xattrdirvp, vnode_t *vp, char *stream_name)
416da6c28aaSamw {
417da6c28aaSamw 	smb_node_t	*xattrdir_node;
418da6c28aaSamw 	smb_node_t	*snode;
419da6c28aaSamw 
420da6c28aaSamw 	xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR,
421037cac00Sjoyce mcintosh 	    fnode, NULL);
422da6c28aaSamw 
423da6c28aaSamw 	if (xattrdir_node == NULL)
424da6c28aaSamw 		return (NULL);
425da6c28aaSamw 
426da6c28aaSamw 	snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node,
427037cac00Sjoyce mcintosh 	    fnode);
428da6c28aaSamw 
429da6c28aaSamw 	(void) smb_node_release(xattrdir_node);
430da6c28aaSamw 	return (snode);
431da6c28aaSamw }
432da6c28aaSamw 
433da6c28aaSamw 
434da6c28aaSamw /*
435da6c28aaSamw  * This function should be called whenever a reference is needed on an
436da6c28aaSamw  * smb_node pointer.  The copy of an smb_node pointer from one non-local
437da6c28aaSamw  * data structure to another requires a reference to be taken on the smb_node
438da6c28aaSamw  * (unless the usage is localized).  Each data structure deallocation routine
439da6c28aaSamw  * will call smb_node_release() on its smb_node pointers.
440da6c28aaSamw  *
441da6c28aaSamw  * In general, an smb_node pointer residing in a structure should never be
442da6c28aaSamw  * stale.  A node pointer may be NULL, however, and care should be taken
443da6c28aaSamw  * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid.
444da6c28aaSamw  * Care also needs to be taken with respect to racing deallocations of a
445da6c28aaSamw  * structure.
446da6c28aaSamw  */
447da6c28aaSamw void
448da6c28aaSamw smb_node_ref(smb_node_t *node)
449da6c28aaSamw {
4502c2961f8Sjose borrego 	SMB_NODE_VALID(node);
451da6c28aaSamw 
4522c2961f8Sjose borrego 	mutex_enter(&node->n_mutex);
4532c2961f8Sjose borrego 	switch (node->n_state) {
4542c2961f8Sjose borrego 	case SMB_NODE_STATE_AVAILABLE:
4552c2961f8Sjose borrego 	case SMB_NODE_STATE_OPLOCK_GRANTED:
4562c2961f8Sjose borrego 	case SMB_NODE_STATE_OPLOCK_BREAKING:
457da6c28aaSamw 		node->n_refcnt++;
458da6c28aaSamw 		ASSERT(node->n_refcnt);
459da6c28aaSamw 		DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node);
4602c2961f8Sjose borrego 		smb_node_audit(node);
4612c2961f8Sjose borrego 		break;
4622c2961f8Sjose borrego 	default:
4632c2961f8Sjose borrego 		SMB_PANIC();
4642c2961f8Sjose borrego 	}
4652c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
466da6c28aaSamw }
467da6c28aaSamw 
468da6c28aaSamw /*
469da6c28aaSamw  * smb_node_lookup() takes a hold on an smb_node, whether found in the
470da6c28aaSamw  * hash table or newly created.  This hold is expected to be released
471da6c28aaSamw  * in the following manner.
472da6c28aaSamw  *
473da6c28aaSamw  * smb_node_lookup() takes an address of an smb_node pointer.  This should
474da6c28aaSamw  * be getting passed down via a lookup (whether path name or component), mkdir,
475da6c28aaSamw  * create.  If the original smb_node pointer resides in a data structure, then
476da6c28aaSamw  * the deallocation routine for the data structure is responsible for calling
477da6c28aaSamw  * smb_node_release() on the smb_node pointer.  Alternatively,
478da6c28aaSamw  * smb_node_release() can be called as soon as the smb_node pointer is no longer
479da6c28aaSamw  * needed.  In this case, callers are responsible for setting an embedded
480da6c28aaSamw  * pointer to NULL if it is known that the last reference is being released.
481da6c28aaSamw  *
482da6c28aaSamw  * If the passed-in address of the smb_node pointer belongs to a local variable,
483da6c28aaSamw  * then the caller with the local variable should call smb_node_release()
484da6c28aaSamw  * directly.
485da6c28aaSamw  *
4861fcced4cSJordan Brown  * smb_node_release() itself will call smb_node_release() on a node's n_dnode,
4871fcced4cSJordan Brown  * as smb_node_lookup() takes a hold on dnode.
488da6c28aaSamw  */
489da6c28aaSamw void
490da6c28aaSamw smb_node_release(smb_node_t *node)
491da6c28aaSamw {
4922c2961f8Sjose borrego 	SMB_NODE_VALID(node);
493da6c28aaSamw 
4942c2961f8Sjose borrego 	mutex_enter(&node->n_mutex);
495da6c28aaSamw 	ASSERT(node->n_refcnt);
496da6c28aaSamw 	DTRACE_PROBE1(smb_node_release, smb_node_t *, node);
497da6c28aaSamw 	if (--node->n_refcnt == 0) {
498da6c28aaSamw 		switch (node->n_state) {
499da6c28aaSamw 
500da6c28aaSamw 		case SMB_NODE_STATE_AVAILABLE:
501da6c28aaSamw 			node->n_state = SMB_NODE_STATE_DESTROYING;
5022c2961f8Sjose borrego 			mutex_exit(&node->n_mutex);
503da6c28aaSamw 
504da6c28aaSamw 			smb_llist_enter(node->n_hash_bucket, RW_WRITER);
505da6c28aaSamw 			smb_llist_remove(node->n_hash_bucket, node);
506da6c28aaSamw 			smb_llist_exit(node->n_hash_bucket);
507da6c28aaSamw 
508da6c28aaSamw 			/*
509da6c28aaSamw 			 * Check if the file was deleted
510da6c28aaSamw 			 */
511da6c28aaSamw 			smb_node_delete_on_close(node);
512da6c28aaSamw 
5131fcced4cSJordan Brown 			if (node->n_dnode) {
5141fcced4cSJordan Brown 				ASSERT(node->n_dnode->n_magic ==
515da6c28aaSamw 				    SMB_NODE_MAGIC);
5161fcced4cSJordan Brown 				smb_node_release(node->n_dnode);
517da6c28aaSamw 			}
518da6c28aaSamw 
5191fcced4cSJordan Brown 			if (node->n_unode) {
5201fcced4cSJordan Brown 				ASSERT(node->n_unode->n_magic ==
521da6c28aaSamw 				    SMB_NODE_MAGIC);
5221fcced4cSJordan Brown 				smb_node_release(node->n_unode);
523da6c28aaSamw 			}
524da6c28aaSamw 
5252c2961f8Sjose borrego 			smb_node_free(node);
526da6c28aaSamw 			return;
527da6c28aaSamw 
528da6c28aaSamw 		default:
5292c2961f8Sjose borrego 			SMB_PANIC();
530da6c28aaSamw 		}
531da6c28aaSamw 	}
5322c2961f8Sjose borrego 	smb_node_audit(node);
5332c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
534da6c28aaSamw }
535da6c28aaSamw 
536da6c28aaSamw static void
537da6c28aaSamw smb_node_delete_on_close(smb_node_t *node)
538da6c28aaSamw {
539da6c28aaSamw 	smb_node_t	*d_snode;
540da6c28aaSamw 	int		rc = 0;
5418b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 	uint32_t	flags = 0;
542da6c28aaSamw 
5431fcced4cSJordan Brown 	d_snode = node->n_dnode;
544da6c28aaSamw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
545da6c28aaSamw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
5468b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 		flags = node->n_delete_on_close_flags;
547da6c28aaSamw 		ASSERT(node->od_name != NULL);
5488b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 
549037cac00Sjoyce mcintosh 		if (node->vp->v_type == VDIR)
550da6c28aaSamw 			rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
5518b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 			    d_snode, node->od_name, flags);
552da6c28aaSamw 		else
553da6c28aaSamw 			rc = smb_fsop_remove(0, node->delete_on_close_cred,
5548b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 			    d_snode, node->od_name, flags);
555da6c28aaSamw 		smb_cred_rele(node->delete_on_close_cred);
556da6c28aaSamw 	}
557da6c28aaSamw 	if (rc != 0)
558da6c28aaSamw 		cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
559da6c28aaSamw 		    node->od_name, rc);
560da6c28aaSamw 	DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
561da6c28aaSamw }
562da6c28aaSamw 
563da6c28aaSamw /*
564da6c28aaSamw  * smb_node_rename()
565da6c28aaSamw  *
566da6c28aaSamw  */
5672c2961f8Sjose borrego void
568da6c28aaSamw smb_node_rename(
5692c2961f8Sjose borrego     smb_node_t	*from_dnode,
5702c2961f8Sjose borrego     smb_node_t	*ret_node,
5712c2961f8Sjose borrego     smb_node_t	*to_dnode,
572da6c28aaSamw     char	*to_name)
573da6c28aaSamw {
5742c2961f8Sjose borrego 	SMB_NODE_VALID(from_dnode);
5752c2961f8Sjose borrego 	SMB_NODE_VALID(to_dnode);
5762c2961f8Sjose borrego 	SMB_NODE_VALID(ret_node);
577da6c28aaSamw 
5782c2961f8Sjose borrego 	smb_node_ref(to_dnode);
5792c2961f8Sjose borrego 	mutex_enter(&ret_node->n_mutex);
5802c2961f8Sjose borrego 	switch (ret_node->n_state) {
5812c2961f8Sjose borrego 	case SMB_NODE_STATE_AVAILABLE:
5822c2961f8Sjose borrego 	case SMB_NODE_STATE_OPLOCK_GRANTED:
5832c2961f8Sjose borrego 	case SMB_NODE_STATE_OPLOCK_BREAKING:
5841fcced4cSJordan Brown 		ret_node->n_dnode = to_dnode;
5852c2961f8Sjose borrego 		mutex_exit(&ret_node->n_mutex);
5861fcced4cSJordan Brown 		ASSERT(to_dnode->n_dnode != ret_node);
5872c2961f8Sjose borrego 		ASSERT((to_dnode->vp->v_xattrdir) ||
5882c2961f8Sjose borrego 		    (to_dnode->vp->v_type == VDIR));
5892c2961f8Sjose borrego 		smb_node_release(from_dnode);
5902c2961f8Sjose borrego 		(void) strcpy(ret_node->od_name, to_name);
591da6c28aaSamw 		/*
592da6c28aaSamw 		 * XXX Need to update attributes?
593da6c28aaSamw 		 */
5942c2961f8Sjose borrego 		break;
5952c2961f8Sjose borrego 	default:
5962c2961f8Sjose borrego 		SMB_PANIC();
5972c2961f8Sjose borrego 	}
598da6c28aaSamw }
599da6c28aaSamw 
600da6c28aaSamw int
601faa1795aSjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root)
602da6c28aaSamw {
603037cac00Sjoyce mcintosh 	smb_attr_t	attr;
604faa1795aSjb150015 	int		error;
605faa1795aSjb150015 	uint32_t	hashkey;
606faa1795aSjb150015 	smb_llist_t	*node_hdr;
607faa1795aSjb150015 	smb_node_t	*node;
608da6c28aaSamw 
609037cac00Sjoyce mcintosh 	attr.sa_mask = SMB_AT_ALL;
610037cac00Sjoyce mcintosh 	error = smb_vop_getattr(vp, NULL, &attr, 0, kcred);
611faa1795aSjb150015 	if (error) {
612faa1795aSjb150015 		VN_RELE(vp);
613faa1795aSjb150015 		return (error);
614faa1795aSjb150015 	}
615da6c28aaSamw 
616037cac00Sjoyce mcintosh 	node_hdr = smb_node_get_hash(&vp->v_vfsp->vfs_fsid, &attr, &hashkey);
617faa1795aSjb150015 
618037cac00Sjoyce mcintosh 	node = smb_node_alloc(ROOTVOL, vp, node_hdr, hashkey);
619faa1795aSjb150015 
620faa1795aSjb150015 	sv->si_root_smb_node = node;
6212c2961f8Sjose borrego 	smb_node_audit(node);
622faa1795aSjb150015 	smb_llist_enter(node_hdr, RW_WRITER);
623faa1795aSjb150015 	smb_llist_insert_head(node_hdr, node);
624faa1795aSjb150015 	smb_llist_exit(node_hdr);
625faa1795aSjb150015 	*root = node;
626da6c28aaSamw 	return (0);
627da6c28aaSamw }
628da6c28aaSamw 
629da6c28aaSamw /*
6308b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * When DeleteOnClose is set on an smb_node, the common open code will
6318b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * reject subsequent open requests for the file. Observation of Windows
6328b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * 2000 indicates that subsequent opens should be allowed (assuming
6338b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * there would be no sharing violation) until the file is closed using
6348b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * the fid on which the DeleteOnClose was requested.
6358b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  *
6368b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * If there are multiple opens with delete-on-close create options,
6378b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * whichever the first file handle is closed will trigger the node to be
6388b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * marked as delete-on-close. The credentials of that ofile will be used
6398b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  * as the delete-on-close credentials of the node.
6408b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States  */
641da6c28aaSamw int
6428b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags)
643da6c28aaSamw {
644*a0aa776eSAlan Wright 	int rc = 0;
645037cac00Sjoyce mcintosh 	smb_attr_t attr;
646da6c28aaSamw 
647*a0aa776eSAlan Wright 	if (node->readonly_creator)
648037cac00Sjoyce mcintosh 		return (-1);
649037cac00Sjoyce mcintosh 
650037cac00Sjoyce mcintosh 	bzero(&attr, sizeof (smb_attr_t));
651037cac00Sjoyce mcintosh 	attr.sa_mask = SMB_AT_DOSATTR;
652037cac00Sjoyce mcintosh 	rc = smb_fsop_getattr(NULL, kcred, node, &attr);
653037cac00Sjoyce mcintosh 	if ((rc != 0) || (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) {
654037cac00Sjoyce mcintosh 		return (-1);
655037cac00Sjoyce mcintosh 	}
656037cac00Sjoyce mcintosh 
657*a0aa776eSAlan Wright 	mutex_enter(&node->n_mutex);
658*a0aa776eSAlan Wright 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
659*a0aa776eSAlan Wright 		rc = -1;
660*a0aa776eSAlan Wright 	} else {
661da6c28aaSamw 		crhold(cr);
662da6c28aaSamw 		node->delete_on_close_cred = cr;
6638b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 		node->n_delete_on_close_flags = flags;
664da6c28aaSamw 		node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
665*a0aa776eSAlan Wright 		rc = 0;
666*a0aa776eSAlan Wright 	}
6672c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
668*a0aa776eSAlan Wright 	return (rc);
669da6c28aaSamw }
670da6c28aaSamw 
671da6c28aaSamw void
672da6c28aaSamw smb_node_reset_delete_on_close(smb_node_t *node)
673da6c28aaSamw {
6742c2961f8Sjose borrego 	mutex_enter(&node->n_mutex);
675da6c28aaSamw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
676da6c28aaSamw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
677da6c28aaSamw 		crfree(node->delete_on_close_cred);
678da6c28aaSamw 		node->delete_on_close_cred = NULL;
6798b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 		node->n_delete_on_close_flags = 0;
680da6c28aaSamw 	}
6812c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
682da6c28aaSamw }
683dc20a302Sas200622 
684dc20a302Sas200622 /*
6853ad684d6Sjb150015  * smb_node_open_check
686dc20a302Sas200622  *
687dc20a302Sas200622  * check file sharing rules for current open request
688dc20a302Sas200622  * against all existing opens for a file.
689dc20a302Sas200622  *
690dc20a302Sas200622  * Returns NT_STATUS_SHARING_VIOLATION if there is any
691dc20a302Sas200622  * sharing conflict, otherwise returns NT_STATUS_SUCCESS.
692dc20a302Sas200622  */
693dc20a302Sas200622 uint32_t
6942c2961f8Sjose borrego smb_node_open_check(
6952c2961f8Sjose borrego     smb_node_t	*node,
6962c2961f8Sjose borrego     cred_t	*cr,
6972c2961f8Sjose borrego     uint32_t	desired_access,
6982c2961f8Sjose borrego     uint32_t	share_access)
699dc20a302Sas200622 {
700dc20a302Sas200622 	smb_ofile_t *of;
701dc20a302Sas200622 	uint32_t status;
702dc20a302Sas200622 
7032c2961f8Sjose borrego 	SMB_NODE_VALID(node);
704dc20a302Sas200622 
705dc20a302Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
706dc20a302Sas200622 	of = smb_llist_head(&node->n_ofile_list);
707dc20a302Sas200622 	while (of) {
7083ad684d6Sjb150015 		status = smb_ofile_open_check(of, cr, desired_access,
7093ad684d6Sjb150015 		    share_access);
7103ad684d6Sjb150015 
7113ad684d6Sjb150015 		switch (status) {
7123ad684d6Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7133ad684d6Sjb150015 		case NT_STATUS_SUCCESS:
7143ad684d6Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7153ad684d6Sjb150015 			break;
7163ad684d6Sjb150015 		default:
7173ad684d6Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
718dc20a302Sas200622 			smb_llist_exit(&node->n_ofile_list);
719dc20a302Sas200622 			return (status);
720dc20a302Sas200622 		}
721dc20a302Sas200622 	}
7223ad684d6Sjb150015 
723dc20a302Sas200622 	smb_llist_exit(&node->n_ofile_list);
724dc20a302Sas200622 	return (NT_STATUS_SUCCESS);
725dc20a302Sas200622 }
726dc20a302Sas200622 
727dc20a302Sas200622 uint32_t
7282c2961f8Sjose borrego smb_node_rename_check(smb_node_t *node)
729dc20a302Sas200622 {
7302c2961f8Sjose borrego 	smb_ofile_t	*of;
7313ad684d6Sjb150015 	uint32_t	status;
732dc20a302Sas200622 
7332c2961f8Sjose borrego 	SMB_NODE_VALID(node);
734dc20a302Sas200622 
735dc20a302Sas200622 	/*
736dc20a302Sas200622 	 * Intra-CIFS check
737dc20a302Sas200622 	 */
738dc20a302Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
7393ad684d6Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
7403ad684d6Sjb150015 	while (of) {
7413ad684d6Sjb150015 		status = smb_ofile_rename_check(of);
742dc20a302Sas200622 
7433ad684d6Sjb150015 		switch (status) {
7443ad684d6Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7453ad684d6Sjb150015 		case NT_STATUS_SUCCESS:
7463ad684d6Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7473ad684d6Sjb150015 			break;
7483ad684d6Sjb150015 		default:
7493ad684d6Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
750dc20a302Sas200622 			smb_llist_exit(&node->n_ofile_list);
7513ad684d6Sjb150015 			return (status);
752dc20a302Sas200622 		}
753dc20a302Sas200622 	}
754dc20a302Sas200622 	smb_llist_exit(&node->n_ofile_list);
755dc20a302Sas200622 
756dc20a302Sas200622 	/*
757dc20a302Sas200622 	 * system-wide share check
758dc20a302Sas200622 	 */
759dc20a302Sas200622 	if (nbl_share_conflict(node->vp, NBL_RENAME, NULL))
760dc20a302Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
761dc20a302Sas200622 	else
762dc20a302Sas200622 		return (NT_STATUS_SUCCESS);
763dc20a302Sas200622 }
764dc20a302Sas200622 
7653ad684d6Sjb150015 uint32_t
766dc20a302Sas200622 smb_node_delete_check(smb_node_t *node)
767dc20a302Sas200622 {
7683ad684d6Sjb150015 	smb_ofile_t	*of;
7693ad684d6Sjb150015 	uint32_t	status;
770dc20a302Sas200622 
7712c2961f8Sjose borrego 	SMB_NODE_VALID(node);
772dc20a302Sas200622 
773037cac00Sjoyce mcintosh 	if (node->vp->v_type == VDIR)
774dc20a302Sas200622 		return (NT_STATUS_SUCCESS);
775dc20a302Sas200622 
776dc20a302Sas200622 	/*
777dc20a302Sas200622 	 * intra-CIFS check
778dc20a302Sas200622 	 */
779dc20a302Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
7803ad684d6Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
7813ad684d6Sjb150015 	while (of) {
7823ad684d6Sjb150015 		status = smb_ofile_delete_check(of);
7833ad684d6Sjb150015 
7843ad684d6Sjb150015 		switch (status) {
7853ad684d6Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7863ad684d6Sjb150015 		case NT_STATUS_SUCCESS:
7873ad684d6Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7883ad684d6Sjb150015 			break;
7893ad684d6Sjb150015 		default:
7903ad684d6Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
791dc20a302Sas200622 			smb_llist_exit(&node->n_ofile_list);
7923ad684d6Sjb150015 			return (status);
793dc20a302Sas200622 		}
794dc20a302Sas200622 	}
795dc20a302Sas200622 	smb_llist_exit(&node->n_ofile_list);
796dc20a302Sas200622 
797dc20a302Sas200622 	/*
798dc20a302Sas200622 	 * system-wide share check
799dc20a302Sas200622 	 */
800dc20a302Sas200622 	if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL))
801dc20a302Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
802dc20a302Sas200622 	else
803dc20a302Sas200622 		return (NT_STATUS_SUCCESS);
804dc20a302Sas200622 }
805dc20a302Sas200622 
806b1352070SAlan Wright void
807b1352070SAlan Wright smb_node_notify_change(smb_node_t *node)
808b1352070SAlan Wright {
809b1352070SAlan Wright 	SMB_NODE_VALID(node);
810b1352070SAlan Wright 
811b1352070SAlan Wright 	if (node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
812b1352070SAlan Wright 		node->flags |= NODE_FLAGS_CHANGED;
813b1352070SAlan Wright 		smb_process_node_notify_change_queue(node);
814b1352070SAlan Wright 	}
815b1352070SAlan Wright }
816b1352070SAlan Wright 
817dc20a302Sas200622 /*
818dc20a302Sas200622  * smb_node_start_crit()
819dc20a302Sas200622  *
820dc20a302Sas200622  * Enter critical region for share reservations.
821dc20a302Sas200622  * See comments above smb_fsop_shrlock().
822dc20a302Sas200622  */
823dc20a302Sas200622 
824dc20a302Sas200622 void
825dc20a302Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode)
826dc20a302Sas200622 {
8272c2961f8Sjose borrego 	rw_enter(&node->n_lock, mode);
828dc20a302Sas200622 	nbl_start_crit(node->vp, mode);
829dc20a302Sas200622 }
830dc20a302Sas200622 
831dc20a302Sas200622 /*
832dc20a302Sas200622  * smb_node_end_crit()
833dc20a302Sas200622  *
834dc20a302Sas200622  * Exit critical region for share reservations.
835dc20a302Sas200622  */
836dc20a302Sas200622 
837dc20a302Sas200622 void
838dc20a302Sas200622 smb_node_end_crit(smb_node_t *node)
839dc20a302Sas200622 {
840dc20a302Sas200622 	nbl_end_crit(node->vp);
8412c2961f8Sjose borrego 	rw_exit(&node->n_lock);
842dc20a302Sas200622 }
843dc20a302Sas200622 
844dc20a302Sas200622 int
845dc20a302Sas200622 smb_node_in_crit(smb_node_t *node)
846dc20a302Sas200622 {
8472c2961f8Sjose borrego 	return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_lock));
8482c2961f8Sjose borrego }
8492c2961f8Sjose borrego 
8502c2961f8Sjose borrego void
8512c2961f8Sjose borrego smb_node_rdlock(smb_node_t *node)
8522c2961f8Sjose borrego {
8532c2961f8Sjose borrego 	rw_enter(&node->n_lock, RW_READER);
8542c2961f8Sjose borrego }
8552c2961f8Sjose borrego 
8562c2961f8Sjose borrego void
8572c2961f8Sjose borrego smb_node_wrlock(smb_node_t *node)
8582c2961f8Sjose borrego {
8592c2961f8Sjose borrego 	rw_enter(&node->n_lock, RW_WRITER);
8602c2961f8Sjose borrego }
8612c2961f8Sjose borrego 
8622c2961f8Sjose borrego void
8632c2961f8Sjose borrego smb_node_unlock(smb_node_t *node)
8642c2961f8Sjose borrego {
8652c2961f8Sjose borrego 	rw_exit(&node->n_lock);
8662c2961f8Sjose borrego }
8672c2961f8Sjose borrego 
8682c2961f8Sjose borrego uint32_t
8692c2961f8Sjose borrego smb_node_get_ofile_count(smb_node_t *node)
8702c2961f8Sjose borrego {
8712c2961f8Sjose borrego 	uint32_t	cntr;
8722c2961f8Sjose borrego 
8732c2961f8Sjose borrego 	SMB_NODE_VALID(node);
8742c2961f8Sjose borrego 
8752c2961f8Sjose borrego 	smb_llist_enter(&node->n_ofile_list, RW_READER);
8762c2961f8Sjose borrego 	cntr = smb_llist_get_count(&node->n_ofile_list);
8772c2961f8Sjose borrego 	smb_llist_exit(&node->n_ofile_list);
8782c2961f8Sjose borrego 	return (cntr);
8792c2961f8Sjose borrego }
8802c2961f8Sjose borrego 
8812c2961f8Sjose borrego void
8822c2961f8Sjose borrego smb_node_add_ofile(smb_node_t *node, smb_ofile_t *of)
8832c2961f8Sjose borrego {
8842c2961f8Sjose borrego 	SMB_NODE_VALID(node);
8852c2961f8Sjose borrego 
8862c2961f8Sjose borrego 	smb_llist_enter(&node->n_ofile_list, RW_WRITER);
8872c2961f8Sjose borrego 	smb_llist_insert_tail(&node->n_ofile_list, of);
8882c2961f8Sjose borrego 	smb_llist_exit(&node->n_ofile_list);
8892c2961f8Sjose borrego }
8902c2961f8Sjose borrego 
8912c2961f8Sjose borrego void
8922c2961f8Sjose borrego smb_node_rem_ofile(smb_node_t *node, smb_ofile_t *of)
8932c2961f8Sjose borrego {
8942c2961f8Sjose borrego 	SMB_NODE_VALID(node);
8952c2961f8Sjose borrego 
8962c2961f8Sjose borrego 	smb_llist_enter(&node->n_ofile_list, RW_WRITER);
8972c2961f8Sjose borrego 	smb_llist_remove(&node->n_ofile_list, of);
8982c2961f8Sjose borrego 	smb_llist_exit(&node->n_ofile_list);
8992c2961f8Sjose borrego }
9002c2961f8Sjose borrego 
901037cac00Sjoyce mcintosh /*
902037cac00Sjoyce mcintosh  * smb_node_inc_open_ofiles
903037cac00Sjoyce mcintosh  */
9042c2961f8Sjose borrego void
9052c2961f8Sjose borrego smb_node_inc_open_ofiles(smb_node_t *node)
9062c2961f8Sjose borrego {
9072c2961f8Sjose borrego 	SMB_NODE_VALID(node);
9082c2961f8Sjose borrego 
9092c2961f8Sjose borrego 	mutex_enter(&node->n_mutex);
9102c2961f8Sjose borrego 	node->n_open_count++;
9112c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
912037cac00Sjoyce mcintosh 
913e3f2c991SKeyur Desai 	smb_node_init_cached_data(node);
9142c2961f8Sjose borrego }
9152c2961f8Sjose borrego 
916037cac00Sjoyce mcintosh /*
917037cac00Sjoyce mcintosh  * smb_node_dec_open_ofiles
918037cac00Sjoyce mcintosh  */
9192c2961f8Sjose borrego void
9202c2961f8Sjose borrego smb_node_dec_open_ofiles(smb_node_t *node)
9212c2961f8Sjose borrego {
9222c2961f8Sjose borrego 	SMB_NODE_VALID(node);
9232c2961f8Sjose borrego 
9242c2961f8Sjose borrego 	mutex_enter(&node->n_mutex);
9252c2961f8Sjose borrego 	node->n_open_count--;
9262c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
927037cac00Sjoyce mcintosh 
928e3f2c991SKeyur Desai 	smb_node_clear_cached_data(node);
9292c2961f8Sjose borrego }
9302c2961f8Sjose borrego 
9312c2961f8Sjose borrego uint32_t
9322c2961f8Sjose borrego smb_node_get_open_ofiles(smb_node_t *node)
9332c2961f8Sjose borrego {
9342c2961f8Sjose borrego 	uint32_t	cnt;
9352c2961f8Sjose borrego 
9362c2961f8Sjose borrego 	SMB_NODE_VALID(node);
9372c2961f8Sjose borrego 
9382c2961f8Sjose borrego 	mutex_enter(&node->n_mutex);
9392c2961f8Sjose borrego 	cnt = node->n_open_count;
9402c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
9412c2961f8Sjose borrego 	return (cnt);
9422c2961f8Sjose borrego }
9432c2961f8Sjose borrego 
9442c2961f8Sjose borrego /*
9452c2961f8Sjose borrego  * smb_node_alloc
9462c2961f8Sjose borrego  */
9472c2961f8Sjose borrego static smb_node_t *
9482c2961f8Sjose borrego smb_node_alloc(
9492c2961f8Sjose borrego     char	*od_name,
9502c2961f8Sjose borrego     vnode_t	*vp,
9512c2961f8Sjose borrego     smb_llist_t	*bucket,
9522c2961f8Sjose borrego     uint32_t	hashkey)
9532c2961f8Sjose borrego {
9542c2961f8Sjose borrego 	smb_node_t	*node;
9552c2961f8Sjose borrego 
9562c2961f8Sjose borrego 	node = kmem_cache_alloc(smb_node_cache, KM_SLEEP);
9572c2961f8Sjose borrego 
9582c2961f8Sjose borrego 	if (node->n_audit_buf != NULL)
9592c2961f8Sjose borrego 		node->n_audit_buf->anb_index = 0;
9602c2961f8Sjose borrego 
961037cac00Sjoyce mcintosh 	node->flags = 0;
9622c2961f8Sjose borrego 	VN_HOLD(vp);
9632c2961f8Sjose borrego 	node->vp = vp;
9642c2961f8Sjose borrego 	node->n_refcnt = 1;
9652c2961f8Sjose borrego 	node->n_hash_bucket = bucket;
9662c2961f8Sjose borrego 	node->n_hashkey = hashkey;
9672c2961f8Sjose borrego 	node->n_orig_uid = 0;
9682c2961f8Sjose borrego 	node->readonly_creator = NULL;
9692c2961f8Sjose borrego 	node->waiting_event = 0;
9702c2961f8Sjose borrego 	node->n_open_count = 0;
9711fcced4cSJordan Brown 	node->n_dnode = NULL;
9721fcced4cSJordan Brown 	node->n_unode = NULL;
9732c2961f8Sjose borrego 	node->delete_on_close_cred = NULL;
9748b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 	node->n_delete_on_close_flags = 0;
9752c2961f8Sjose borrego 
9762c2961f8Sjose borrego 	(void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
9772c2961f8Sjose borrego 	if (strcmp(od_name, XATTR_DIR) == 0)
9782c2961f8Sjose borrego 		node->flags |= NODE_XATTR_DIR;
9792c2961f8Sjose borrego 
9802c2961f8Sjose borrego 	node->n_state = SMB_NODE_STATE_AVAILABLE;
9812c2961f8Sjose borrego 	node->n_magic = SMB_NODE_MAGIC;
9822c2961f8Sjose borrego 	return (node);
9832c2961f8Sjose borrego }
9842c2961f8Sjose borrego 
9852c2961f8Sjose borrego /*
9862c2961f8Sjose borrego  * smb_node_free
9872c2961f8Sjose borrego  */
9882c2961f8Sjose borrego static void
9892c2961f8Sjose borrego smb_node_free(smb_node_t *node)
9902c2961f8Sjose borrego {
9912c2961f8Sjose borrego 	SMB_NODE_VALID(node);
9922c2961f8Sjose borrego 
9932c2961f8Sjose borrego 	node->n_magic = 0;
9942c2961f8Sjose borrego 	VERIFY(!list_link_active(&node->n_lnd));
9952c2961f8Sjose borrego 	VERIFY(node->n_lock_list.ll_count == 0);
9962c2961f8Sjose borrego 	VERIFY(node->n_ofile_list.ll_count == 0);
9972c2961f8Sjose borrego 	VERIFY(node->n_oplock.ol_xthread == NULL);
998fc724630SAlan Wright 	VERIFY(mutex_owner(&node->n_mutex) == NULL);
999fc724630SAlan Wright 	VERIFY(!RW_LOCK_HELD(&node->n_lock));
10002c2961f8Sjose borrego 	VN_RELE(node->vp);
10012c2961f8Sjose borrego 	kmem_cache_free(smb_node_cache, node);
10022c2961f8Sjose borrego }
10032c2961f8Sjose borrego 
10042c2961f8Sjose borrego /*
10052c2961f8Sjose borrego  * smb_node_constructor
10062c2961f8Sjose borrego  */
10072c2961f8Sjose borrego static int
10082c2961f8Sjose borrego smb_node_constructor(void *buf, void *un, int kmflags)
10092c2961f8Sjose borrego {
10102c2961f8Sjose borrego 	_NOTE(ARGUNUSED(kmflags, un))
10112c2961f8Sjose borrego 
10122c2961f8Sjose borrego 	smb_node_t	*node = (smb_node_t *)buf;
10132c2961f8Sjose borrego 
10142c2961f8Sjose borrego 	bzero(node, sizeof (smb_node_t));
10152c2961f8Sjose borrego 
10162c2961f8Sjose borrego 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
10172c2961f8Sjose borrego 	    offsetof(smb_ofile_t, f_nnd));
10182c2961f8Sjose borrego 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
10192c2961f8Sjose borrego 	    offsetof(smb_lock_t, l_lnd));
10202c2961f8Sjose borrego 	cv_init(&node->n_oplock.ol_cv, NULL, CV_DEFAULT, NULL);
10212c2961f8Sjose borrego 	rw_init(&node->n_lock, NULL, RW_DEFAULT, NULL);
10222c2961f8Sjose borrego 	mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL);
10232c2961f8Sjose borrego 	smb_node_create_audit_buf(node, kmflags);
10242c2961f8Sjose borrego 	return (0);
10252c2961f8Sjose borrego }
10262c2961f8Sjose borrego 
10272c2961f8Sjose borrego /*
10282c2961f8Sjose borrego  * smb_node_destructor
10292c2961f8Sjose borrego  */
10302c2961f8Sjose borrego static void
10312c2961f8Sjose borrego smb_node_destructor(void *buf, void *un)
10322c2961f8Sjose borrego {
10332c2961f8Sjose borrego 	_NOTE(ARGUNUSED(un))
10342c2961f8Sjose borrego 
10352c2961f8Sjose borrego 	smb_node_t	*node = (smb_node_t *)buf;
10362c2961f8Sjose borrego 
10372c2961f8Sjose borrego 	smb_node_destroy_audit_buf(node);
10382c2961f8Sjose borrego 	mutex_destroy(&node->n_mutex);
10392c2961f8Sjose borrego 	rw_destroy(&node->n_lock);
10402c2961f8Sjose borrego 	cv_destroy(&node->n_oplock.ol_cv);
10412c2961f8Sjose borrego 	smb_llist_destructor(&node->n_lock_list);
10422c2961f8Sjose borrego 	smb_llist_destructor(&node->n_ofile_list);
10432c2961f8Sjose borrego }
10442c2961f8Sjose borrego 
10452c2961f8Sjose borrego /*
10462c2961f8Sjose borrego  * smb_node_create_audit_buf
10472c2961f8Sjose borrego  */
10482c2961f8Sjose borrego static void
10492c2961f8Sjose borrego smb_node_create_audit_buf(smb_node_t *node, int kmflags)
10502c2961f8Sjose borrego {
10512c2961f8Sjose borrego 	smb_audit_buf_node_t	*abn;
10522c2961f8Sjose borrego 
10532c2961f8Sjose borrego 	if (smb_audit_flags & SMB_AUDIT_NODE) {
10542c2961f8Sjose borrego 		abn = kmem_zalloc(sizeof (smb_audit_buf_node_t), kmflags);
10552c2961f8Sjose borrego 		abn->anb_max_index = SMB_AUDIT_BUF_MAX_REC - 1;
10562c2961f8Sjose borrego 		node->n_audit_buf = abn;
10572c2961f8Sjose borrego 	}
10582c2961f8Sjose borrego }
10592c2961f8Sjose borrego 
10602c2961f8Sjose borrego /*
10612c2961f8Sjose borrego  * smb_node_destroy_audit_buf
10622c2961f8Sjose borrego  */
10632c2961f8Sjose borrego static void
10642c2961f8Sjose borrego smb_node_destroy_audit_buf(smb_node_t *node)
10652c2961f8Sjose borrego {
10662c2961f8Sjose borrego 	if (node->n_audit_buf != NULL) {
10672c2961f8Sjose borrego 		kmem_free(node->n_audit_buf, sizeof (smb_audit_buf_node_t));
10682c2961f8Sjose borrego 		node->n_audit_buf = NULL;
10692c2961f8Sjose borrego 	}
10702c2961f8Sjose borrego }
10712c2961f8Sjose borrego 
10722c2961f8Sjose borrego /*
10732c2961f8Sjose borrego  * smb_node_audit
10742c2961f8Sjose borrego  *
10752c2961f8Sjose borrego  * This function saves the calling stack in the audit buffer of the node passed
10762c2961f8Sjose borrego  * in.
10772c2961f8Sjose borrego  */
10782c2961f8Sjose borrego static void
10792c2961f8Sjose borrego smb_node_audit(smb_node_t *node)
10802c2961f8Sjose borrego {
10812c2961f8Sjose borrego 	smb_audit_buf_node_t	*abn;
10822c2961f8Sjose borrego 	smb_audit_record_node_t	*anr;
10832c2961f8Sjose borrego 
10842c2961f8Sjose borrego 	if (node->n_audit_buf) {
10852c2961f8Sjose borrego 		abn = node->n_audit_buf;
10862c2961f8Sjose borrego 		anr = abn->anb_records;
10872c2961f8Sjose borrego 		anr += abn->anb_index;
10882c2961f8Sjose borrego 		abn->anb_index++;
10892c2961f8Sjose borrego 		abn->anb_index &= abn->anb_max_index;
10902c2961f8Sjose borrego 		anr->anr_refcnt = node->n_refcnt;
10912c2961f8Sjose borrego 		anr->anr_depth = getpcstack(anr->anr_stack,
10922c2961f8Sjose borrego 		    SMB_AUDIT_STACK_DEPTH);
10932c2961f8Sjose borrego 	}
10942c2961f8Sjose borrego }
10952c2961f8Sjose borrego 
10962c2961f8Sjose borrego static smb_llist_t *
10972c2961f8Sjose borrego smb_node_get_hash(fsid_t *fsid, smb_attr_t *attr, uint32_t *phashkey)
10982c2961f8Sjose borrego {
10992c2961f8Sjose borrego 	uint32_t	hashkey;
11002c2961f8Sjose borrego 
11012c2961f8Sjose borrego 	hashkey = fsid->val[0] + attr->sa_vattr.va_nodeid;
11022c2961f8Sjose borrego 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
11032c2961f8Sjose borrego 	*phashkey = hashkey;
11042c2961f8Sjose borrego 	return (&smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]);
1105dc20a302Sas200622 }
1106037cac00Sjoyce mcintosh 
1107037cac00Sjoyce mcintosh boolean_t
1108037cac00Sjoyce mcintosh smb_node_is_dir(smb_node_t *node)
1109037cac00Sjoyce mcintosh {
1110037cac00Sjoyce mcintosh 	SMB_NODE_VALID(node);
1111037cac00Sjoyce mcintosh 	return (node->vp->v_type == VDIR);
1112037cac00Sjoyce mcintosh }
1113037cac00Sjoyce mcintosh 
1114037cac00Sjoyce mcintosh boolean_t
1115037cac00Sjoyce mcintosh smb_node_is_link(smb_node_t *node)
1116037cac00Sjoyce mcintosh {
1117037cac00Sjoyce mcintosh 	SMB_NODE_VALID(node);
1118037cac00Sjoyce mcintosh 	return (node->vp->v_type == VLNK);
1119037cac00Sjoyce mcintosh }
1120037cac00Sjoyce mcintosh 
1121037cac00Sjoyce mcintosh /*
1122037cac00Sjoyce mcintosh  * smb_node_file_is_readonly
1123037cac00Sjoyce mcintosh  *
1124037cac00Sjoyce mcintosh  * Checks if the file (which node represents) is marked readonly
1125037cac00Sjoyce mcintosh  * in the filesystem. No account is taken of any pending readonly
1126037cac00Sjoyce mcintosh  * in the node, which must be handled by the callers.
1127037cac00Sjoyce mcintosh  * (See SMB_OFILE_IS_READONLY and SMB_PATHFILE_IS_READONLY)
1128037cac00Sjoyce mcintosh  */
1129037cac00Sjoyce mcintosh boolean_t
1130037cac00Sjoyce mcintosh smb_node_file_is_readonly(smb_node_t *node)
1131037cac00Sjoyce mcintosh {
1132037cac00Sjoyce mcintosh 	smb_attr_t attr;
1133037cac00Sjoyce mcintosh 
1134037cac00Sjoyce mcintosh 	if (node == NULL)
1135037cac00Sjoyce mcintosh 		return (B_FALSE);
1136037cac00Sjoyce mcintosh 
1137037cac00Sjoyce mcintosh 	bzero(&attr, sizeof (smb_attr_t));
1138037cac00Sjoyce mcintosh 	attr.sa_mask = SMB_AT_DOSATTR;
1139037cac00Sjoyce mcintosh 	(void) smb_fsop_getattr(NULL, kcred, node, &attr);
1140037cac00Sjoyce mcintosh 	return ((attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) != 0);
1141037cac00Sjoyce mcintosh }
1142037cac00Sjoyce mcintosh 
1143037cac00Sjoyce mcintosh /*
1144037cac00Sjoyce mcintosh  * smb_node_setattr
1145037cac00Sjoyce mcintosh  *
1146037cac00Sjoyce mcintosh  * The sr may be NULL, for example when closing an ofile.
1147037cac00Sjoyce mcintosh  * The ofile may be NULL, for example when a client request
1148037cac00Sjoyce mcintosh  * specifies the file by pathname.
1149037cac00Sjoyce mcintosh  *
1150e3f2c991SKeyur Desai  * Timestamps
1151037cac00Sjoyce mcintosh  * When attributes are set on an ofile, any pending timestamps
1152037cac00Sjoyce mcintosh  * from a write request on the ofile are implicitly set to "now".
1153037cac00Sjoyce mcintosh  * For compatibility with windows the following timestamps are
1154037cac00Sjoyce mcintosh  * also implicitly set to now:
1155037cac00Sjoyce mcintosh  * - if any attribute is being explicitly set, set ctime to now
1156037cac00Sjoyce mcintosh  * - if file size is being explicitly set, set atime & ctime to now
1157037cac00Sjoyce mcintosh  *
1158e3f2c991SKeyur Desai  * Any timestamp that is being explicitly set, or has previously
1159037cac00Sjoyce mcintosh  * been explicitly set on the ofile, is excluded from implicit
1160037cac00Sjoyce mcintosh  * (now) setting.
1161037cac00Sjoyce mcintosh  *
1162037cac00Sjoyce mcintosh  * Updates the node's cached timestamp values.
1163037cac00Sjoyce mcintosh  * Updates the ofile's explicit times flag.
1164037cac00Sjoyce mcintosh  *
1165e3f2c991SKeyur Desai  * File allocation size
1166e3f2c991SKeyur Desai  * When the file allocation size is set it is first rounded up
1167e3f2c991SKeyur Desai  * to block size. If the file size is smaller than the allocation
1168e3f2c991SKeyur Desai  * size the file is truncated by setting the filesize to allocsz.
1169e3f2c991SKeyur Desai  * If there are open ofiles, the allocsz is cached on the node.
1170e3f2c991SKeyur Desai  *
1171e3f2c991SKeyur Desai  * Updates the node's cached allocsz value.
1172e3f2c991SKeyur Desai  *
1173037cac00Sjoyce mcintosh  * Returns: errno
1174037cac00Sjoyce mcintosh  */
1175037cac00Sjoyce mcintosh int
1176037cac00Sjoyce mcintosh smb_node_setattr(smb_request_t *sr, smb_node_t *node,
1177037cac00Sjoyce mcintosh     cred_t *cr, smb_ofile_t *of, smb_attr_t *attr)
1178037cac00Sjoyce mcintosh {
1179037cac00Sjoyce mcintosh 	int rc;
1180e3f2c991SKeyur Desai 	uint32_t pending_times = 0;
1181e3f2c991SKeyur Desai 	uint32_t explicit_times = 0;
1182037cac00Sjoyce mcintosh 	timestruc_t now;
1183e3f2c991SKeyur Desai 	smb_attr_t tmp_attr;
1184037cac00Sjoyce mcintosh 
1185037cac00Sjoyce mcintosh 	ASSERT(attr);
1186037cac00Sjoyce mcintosh 	SMB_NODE_VALID(node);
1187037cac00Sjoyce mcintosh 
1188e3f2c991SKeyur Desai 	/* set attributes specified in attr */
1189e3f2c991SKeyur Desai 	if (attr->sa_mask != 0) {
1190e3f2c991SKeyur Desai 		/* if allocation size is < file size, truncate the file */
1191e3f2c991SKeyur Desai 		if (attr->sa_mask & SMB_AT_ALLOCSZ) {
1192e3f2c991SKeyur Desai 			attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_allocsz);
1193037cac00Sjoyce mcintosh 
1194e3f2c991SKeyur Desai 			bzero(&tmp_attr, sizeof (smb_attr_t));
1195e3f2c991SKeyur Desai 			tmp_attr.sa_mask = SMB_AT_SIZE;
1196e3f2c991SKeyur Desai 			(void) smb_fsop_getattr(NULL, kcred, node, &tmp_attr);
1197037cac00Sjoyce mcintosh 
1198e3f2c991SKeyur Desai 			if (tmp_attr.sa_vattr.va_size > attr->sa_allocsz) {
1199e3f2c991SKeyur Desai 				attr->sa_vattr.va_size = attr->sa_allocsz;
1200e3f2c991SKeyur Desai 				attr->sa_mask |= SMB_AT_SIZE;
1201037cac00Sjoyce mcintosh 			}
1202037cac00Sjoyce mcintosh 		}
1203037cac00Sjoyce mcintosh 
1204037cac00Sjoyce mcintosh 		rc = smb_fsop_setattr(sr, cr, node, attr);
1205037cac00Sjoyce mcintosh 		if (rc != 0)
1206037cac00Sjoyce mcintosh 			return (rc);
1207037cac00Sjoyce mcintosh 
1208e3f2c991SKeyur Desai 		smb_node_set_cached_allocsz(node, attr);
1209037cac00Sjoyce mcintosh 		smb_node_set_cached_timestamps(node, attr);
1210e3f2c991SKeyur Desai 		if (of) {
1211e3f2c991SKeyur Desai 			smb_ofile_set_explicit_times(of,
1212e3f2c991SKeyur Desai 			    (attr->sa_mask & SMB_AT_TIMES));
1213e3f2c991SKeyur Desai 		}
1214e3f2c991SKeyur Desai 	}
1215037cac00Sjoyce mcintosh 
1216e3f2c991SKeyur Desai 	/*
1217e3f2c991SKeyur Desai 	 * Determine which timestamps to implicitly set to "now".
1218e3f2c991SKeyur Desai 	 * Don't overwrite timestamps already explicitly set.
1219e3f2c991SKeyur Desai 	 */
1220e3f2c991SKeyur Desai 	bzero(&tmp_attr, sizeof (smb_attr_t));
1221e3f2c991SKeyur Desai 	gethrestime(&now);
1222e3f2c991SKeyur Desai 	tmp_attr.sa_vattr.va_atime = now;
1223e3f2c991SKeyur Desai 	tmp_attr.sa_vattr.va_mtime = now;
1224e3f2c991SKeyur Desai 	tmp_attr.sa_vattr.va_ctime = now;
1225e3f2c991SKeyur Desai 
1226e3f2c991SKeyur Desai 	/* pending write timestamps */
1227e3f2c991SKeyur Desai 	if (of) {
1228e3f2c991SKeyur Desai 		if (smb_ofile_write_time_pending(of)) {
1229e3f2c991SKeyur Desai 			pending_times |=
1230e3f2c991SKeyur Desai 			    (SMB_AT_MTIME | SMB_AT_CTIME | SMB_AT_ATIME);
1231e3f2c991SKeyur Desai 		}
1232e3f2c991SKeyur Desai 		explicit_times |= (smb_ofile_explicit_times(of));
1233e3f2c991SKeyur Desai 	}
1234e3f2c991SKeyur Desai 	explicit_times |= (attr->sa_mask & SMB_AT_TIMES);
1235e3f2c991SKeyur Desai 	pending_times &= ~explicit_times;
1236e3f2c991SKeyur Desai 
1237e3f2c991SKeyur Desai 	if (pending_times) {
1238e3f2c991SKeyur Desai 		tmp_attr.sa_mask = pending_times;
1239e3f2c991SKeyur Desai 		(void) smb_fsop_setattr(NULL, kcred, node, &tmp_attr);
1240e3f2c991SKeyur Desai 	}
1241e3f2c991SKeyur Desai 
1242e3f2c991SKeyur Desai 	/* additional timestamps to update in cache */
1243e3f2c991SKeyur Desai 	if (attr->sa_mask)
1244e3f2c991SKeyur Desai 		tmp_attr.sa_mask |= SMB_AT_CTIME;
1245e3f2c991SKeyur Desai 	if (attr->sa_mask & (SMB_AT_SIZE | SMB_AT_ALLOCSZ))
1246e3f2c991SKeyur Desai 		tmp_attr.sa_mask |= SMB_AT_MTIME;
1247e3f2c991SKeyur Desai 	tmp_attr.sa_mask &= ~explicit_times;
1248e3f2c991SKeyur Desai 
1249e3f2c991SKeyur Desai 	if (tmp_attr.sa_mask)
1250e3f2c991SKeyur Desai 		smb_node_set_cached_timestamps(node, &tmp_attr);
1251037cac00Sjoyce mcintosh 
1252037cac00Sjoyce mcintosh 	return (0);
1253037cac00Sjoyce mcintosh }
1254037cac00Sjoyce mcintosh 
1255037cac00Sjoyce mcintosh /*
1256037cac00Sjoyce mcintosh  * smb_node_getattr
1257037cac00Sjoyce mcintosh  *
1258037cac00Sjoyce mcintosh  * Get attributes from the file system and apply any smb-specific
1259037cac00Sjoyce mcintosh  * overrides for size, dos attributes and timestamps
1260037cac00Sjoyce mcintosh  *
1261037cac00Sjoyce mcintosh  * node->readonly_creator reflects whether a readonly set is pending
1262037cac00Sjoyce mcintosh  * from a readonly create. The readonly attribute should be visible to
1263037cac00Sjoyce mcintosh  * all clients even though the readonly creator fid is immune to the
1264037cac00Sjoyce mcintosh  * readonly bit until close.
1265037cac00Sjoyce mcintosh  *
1266037cac00Sjoyce mcintosh  * Returns: errno
1267037cac00Sjoyce mcintosh  */
1268037cac00Sjoyce mcintosh int
1269037cac00Sjoyce mcintosh smb_node_getattr(smb_request_t *sr, smb_node_t *node, smb_attr_t *attr)
1270037cac00Sjoyce mcintosh {
1271037cac00Sjoyce mcintosh 	int rc;
1272037cac00Sjoyce mcintosh 
1273037cac00Sjoyce mcintosh 	SMB_NODE_VALID(node);
1274037cac00Sjoyce mcintosh 
1275037cac00Sjoyce mcintosh 	bzero(attr, sizeof (smb_attr_t));
1276037cac00Sjoyce mcintosh 	attr->sa_mask = SMB_AT_ALL;
1277037cac00Sjoyce mcintosh 	rc = smb_fsop_getattr(sr, kcred, node, attr);
1278037cac00Sjoyce mcintosh 	if (rc != 0)
1279037cac00Sjoyce mcintosh 		return (rc);
1280037cac00Sjoyce mcintosh 
1281037cac00Sjoyce mcintosh 	mutex_enter(&node->n_mutex);
1282037cac00Sjoyce mcintosh 
1283e3f2c991SKeyur Desai 	if (node->vp->v_type == VDIR) {
1284037cac00Sjoyce mcintosh 		attr->sa_vattr.va_size = 0;
1285e3f2c991SKeyur Desai 		attr->sa_allocsz = 0;
1286e3f2c991SKeyur Desai 	}
1287037cac00Sjoyce mcintosh 
1288037cac00Sjoyce mcintosh 	if (node->readonly_creator)
1289037cac00Sjoyce mcintosh 		attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY;
1290037cac00Sjoyce mcintosh 	if (attr->sa_dosattr == 0)
1291037cac00Sjoyce mcintosh 		attr->sa_dosattr = FILE_ATTRIBUTE_NORMAL;
1292037cac00Sjoyce mcintosh 
1293e3f2c991SKeyur Desai 
1294037cac00Sjoyce mcintosh 	mutex_exit(&node->n_mutex);
1295037cac00Sjoyce mcintosh 
1296e3f2c991SKeyur Desai 	smb_node_get_cached_allocsz(node, attr);
1297037cac00Sjoyce mcintosh 	smb_node_get_cached_timestamps(node, attr);
1298e3f2c991SKeyur Desai 
1299037cac00Sjoyce mcintosh 	return (0);
1300037cac00Sjoyce mcintosh }
1301037cac00Sjoyce mcintosh 
1302037cac00Sjoyce mcintosh /*
1303e3f2c991SKeyur Desai  * smb_node_init_cached_data
1304e3f2c991SKeyur Desai  */
1305e3f2c991SKeyur Desai static void
1306e3f2c991SKeyur Desai smb_node_init_cached_data(smb_node_t *node)
1307e3f2c991SKeyur Desai {
1308e3f2c991SKeyur Desai 	smb_attr_t attr;
1309e3f2c991SKeyur Desai 
1310e3f2c991SKeyur Desai 	bzero(&attr, sizeof (smb_attr_t));
1311e3f2c991SKeyur Desai 	attr.sa_mask = SMB_AT_ALL;
1312e3f2c991SKeyur Desai 	(void) smb_fsop_getattr(NULL, kcred, node, &attr);
1313e3f2c991SKeyur Desai 
1314e3f2c991SKeyur Desai 	smb_node_init_cached_allocsz(node, &attr);
1315e3f2c991SKeyur Desai 	smb_node_init_cached_timestamps(node, &attr);
1316e3f2c991SKeyur Desai }
1317e3f2c991SKeyur Desai 
1318e3f2c991SKeyur Desai /*
1319e3f2c991SKeyur Desai  * smb_node_clear_cached_data
1320e3f2c991SKeyur Desai  */
1321e3f2c991SKeyur Desai static void
1322e3f2c991SKeyur Desai smb_node_clear_cached_data(smb_node_t *node)
1323e3f2c991SKeyur Desai {
1324e3f2c991SKeyur Desai 	smb_node_clear_cached_allocsz(node);
1325e3f2c991SKeyur Desai 	smb_node_clear_cached_timestamps(node);
1326e3f2c991SKeyur Desai }
1327e3f2c991SKeyur Desai 
1328e3f2c991SKeyur Desai /*
1329e3f2c991SKeyur Desai  * File allocation size (allocsz) caching
1330e3f2c991SKeyur Desai  *
1331e3f2c991SKeyur Desai  * When there are open ofiles on the node, the file allocsz is cached.
1332e3f2c991SKeyur Desai  * The cached value (n_allocsz) is initialized when the first ofile is
1333e3f2c991SKeyur Desai  * opened and cleared when the last is closed. Allocsz calculated from
1334e3f2c991SKeyur Desai  * the filesize (rounded up to block size).
1335e3f2c991SKeyur Desai  * When the allocation size is queried, if the cached allocsz is less
1336e3f2c991SKeyur Desai  * than the filesize, it is recalculated from the filesize.
1337e3f2c991SKeyur Desai  */
1338e3f2c991SKeyur Desai 
1339e3f2c991SKeyur Desai /*
1340e3f2c991SKeyur Desai  * smb_node_init_cached_allocsz
1341e3f2c991SKeyur Desai  *
1342e3f2c991SKeyur Desai  * If there are open ofiles, cache the allocsz in the node.
1343e3f2c991SKeyur Desai  * Calculate the allocsz from the filesizes.
1344e3f2c991SKeyur Desai  * block size).
1345e3f2c991SKeyur Desai  */
1346e3f2c991SKeyur Desai static void
1347e3f2c991SKeyur Desai smb_node_init_cached_allocsz(smb_node_t *node, smb_attr_t *attr)
1348e3f2c991SKeyur Desai {
1349e3f2c991SKeyur Desai 	mutex_enter(&node->n_mutex);
1350e3f2c991SKeyur Desai 	if (node->n_open_count == 1)
1351e3f2c991SKeyur Desai 		node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size);
1352e3f2c991SKeyur Desai 	mutex_exit(&node->n_mutex);
1353e3f2c991SKeyur Desai }
1354e3f2c991SKeyur Desai 
1355e3f2c991SKeyur Desai /*
1356e3f2c991SKeyur Desai  * smb_node_clear_cached_allocsz
1357e3f2c991SKeyur Desai  */
1358e3f2c991SKeyur Desai static void
1359e3f2c991SKeyur Desai smb_node_clear_cached_allocsz(smb_node_t *node)
1360e3f2c991SKeyur Desai {
1361e3f2c991SKeyur Desai 	mutex_enter(&node->n_mutex);
1362e3f2c991SKeyur Desai 	if (node->n_open_count == 0)
1363e3f2c991SKeyur Desai 		node->n_allocsz = 0;
1364e3f2c991SKeyur Desai 	mutex_exit(&node->n_mutex);
1365e3f2c991SKeyur Desai }
1366e3f2c991SKeyur Desai 
1367e3f2c991SKeyur Desai /*
1368e3f2c991SKeyur Desai  * smb_node_get_cached_allocsz
1369e3f2c991SKeyur Desai  *
1370e3f2c991SKeyur Desai  * If there is no cached allocsz (no open files), calculate
1371e3f2c991SKeyur Desai  * allocsz from the filesize.
1372e3f2c991SKeyur Desai  * If the allocsz is cached but is smaller than the filesize
1373e3f2c991SKeyur Desai  * recalculate the cached allocsz from the filesize.
1374e3f2c991SKeyur Desai  *
1375e3f2c991SKeyur Desai  * Return allocs in attr->sa_allocsz.
1376e3f2c991SKeyur Desai  */
1377e3f2c991SKeyur Desai static void
1378e3f2c991SKeyur Desai smb_node_get_cached_allocsz(smb_node_t *node, smb_attr_t *attr)
1379e3f2c991SKeyur Desai {
1380e3f2c991SKeyur Desai 	if (node->vp->v_type == VDIR)
1381e3f2c991SKeyur Desai 		return;
1382e3f2c991SKeyur Desai 
1383e3f2c991SKeyur Desai 	mutex_enter(&node->n_mutex);
1384e3f2c991SKeyur Desai 	if (node->n_open_count == 0) {
1385e3f2c991SKeyur Desai 		attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size);
1386e3f2c991SKeyur Desai 	} else {
1387e3f2c991SKeyur Desai 		if (node->n_allocsz < attr->sa_vattr.va_size)
1388e3f2c991SKeyur Desai 			node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size);
1389e3f2c991SKeyur Desai 		attr->sa_allocsz = node->n_allocsz;
1390e3f2c991SKeyur Desai 	}
1391e3f2c991SKeyur Desai 	mutex_exit(&node->n_mutex);
1392e3f2c991SKeyur Desai }
1393e3f2c991SKeyur Desai 
1394e3f2c991SKeyur Desai /*
1395e3f2c991SKeyur Desai  * smb_node_set_cached_allocsz
1396e3f2c991SKeyur Desai  *
1397e3f2c991SKeyur Desai  * attr->sa_allocsz has already been rounded to block size by
1398e3f2c991SKeyur Desai  * the caller.
1399e3f2c991SKeyur Desai  */
1400e3f2c991SKeyur Desai static void
1401e3f2c991SKeyur Desai smb_node_set_cached_allocsz(smb_node_t *node, smb_attr_t *attr)
1402e3f2c991SKeyur Desai {
1403e3f2c991SKeyur Desai 	mutex_enter(&node->n_mutex);
1404e3f2c991SKeyur Desai 	if (attr->sa_mask & SMB_AT_ALLOCSZ) {
1405e3f2c991SKeyur Desai 		if (node->n_open_count > 0)
1406e3f2c991SKeyur Desai 			node->n_allocsz = attr->sa_allocsz;
1407e3f2c991SKeyur Desai 	}
1408e3f2c991SKeyur Desai 	mutex_exit(&node->n_mutex);
1409e3f2c991SKeyur Desai }
1410e3f2c991SKeyur Desai 
1411e3f2c991SKeyur Desai 
1412e3f2c991SKeyur Desai /*
1413037cac00Sjoyce mcintosh  * Timestamp caching
1414037cac00Sjoyce mcintosh  *
1415037cac00Sjoyce mcintosh  * Solaris file systems handle timestamps different from NTFS. For
1416037cac00Sjoyce mcintosh  * example when file data is written NTFS doesn't update the timestamps
1417037cac00Sjoyce mcintosh  * until the file is closed, and then only if they haven't been explicity
1418037cac00Sjoyce mcintosh  * set via a set attribute request. In order to provide a more similar
1419037cac00Sjoyce mcintosh  * view of an open file's timestamps, we cache the timestamps in the
1420037cac00Sjoyce mcintosh  * node and manipulate them in a manner more consistent with windows.
1421037cac00Sjoyce mcintosh  * (See handling of explicit times and pending timestamps from a write
1422037cac00Sjoyce mcintosh  * request in smb_node_getattr and smb_node_setattr above.)
1423037cac00Sjoyce mcintosh  * Timestamps remain cached while there are open ofiles for the node.
1424037cac00Sjoyce mcintosh  * This includes open ofiles for named streams.
1425037cac00Sjoyce mcintosh  * n_open_ofiles cannot be used as this doesn't include ofiles opened
1426037cac00Sjoyce mcintosh  * for the node's named streams. Thus n_timestamps contains a count
1427037cac00Sjoyce mcintosh  * of open ofiles (t_open_ofiles), including named streams' ofiles,
1428037cac00Sjoyce mcintosh  * to be used to control timestamp caching.
1429037cac00Sjoyce mcintosh  *
1430037cac00Sjoyce mcintosh  * If a node represents a named stream the associated unnamed streams
1431037cac00Sjoyce mcintosh  * cached timestamps are used instead.
1432037cac00Sjoyce mcintosh  */
1433037cac00Sjoyce mcintosh 
1434037cac00Sjoyce mcintosh /*
1435037cac00Sjoyce mcintosh  * smb_node_init_cached_timestamps
1436037cac00Sjoyce mcintosh  *
1437037cac00Sjoyce mcintosh  * Increment count of open ofiles which are using the cached timestamps.
1438037cac00Sjoyce mcintosh  * If this is the first open ofile, init the cached timestamps from the
1439037cac00Sjoyce mcintosh  * file system values.
1440037cac00Sjoyce mcintosh  */
1441037cac00Sjoyce mcintosh static void
1442e3f2c991SKeyur Desai smb_node_init_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
1443037cac00Sjoyce mcintosh {
1444037cac00Sjoyce mcintosh 	smb_node_t *unode;
1445037cac00Sjoyce mcintosh 
1446037cac00Sjoyce mcintosh 	if ((unode = SMB_IS_STREAM(node)) != NULL)
1447037cac00Sjoyce mcintosh 		node = unode;
1448037cac00Sjoyce mcintosh 
1449037cac00Sjoyce mcintosh 	mutex_enter(&node->n_mutex);
1450037cac00Sjoyce mcintosh 	++(node->n_timestamps.t_open_ofiles);
1451037cac00Sjoyce mcintosh 	if (node->n_timestamps.t_open_ofiles == 1) {
1452e3f2c991SKeyur Desai 		node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime;
1453e3f2c991SKeyur Desai 		node->n_timestamps.t_atime = attr->sa_vattr.va_atime;
1454e3f2c991SKeyur Desai 		node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime;
1455e3f2c991SKeyur Desai 		node->n_timestamps.t_crtime = attr->sa_crtime;
1456037cac00Sjoyce mcintosh 		node->n_timestamps.t_cached = B_TRUE;
1457037cac00Sjoyce mcintosh 	}
1458037cac00Sjoyce mcintosh 	mutex_exit(&node->n_mutex);
1459037cac00Sjoyce mcintosh }
1460037cac00Sjoyce mcintosh 
1461037cac00Sjoyce mcintosh /*
1462037cac00Sjoyce mcintosh  * smb_node_clear_cached_timestamps
1463037cac00Sjoyce mcintosh  *
1464037cac00Sjoyce mcintosh  * Decrement count of open ofiles using the cached timestamps.
1465037cac00Sjoyce mcintosh  * If the decremented count is zero, clear the cached timestamps.
1466037cac00Sjoyce mcintosh  */
1467037cac00Sjoyce mcintosh static void
1468037cac00Sjoyce mcintosh smb_node_clear_cached_timestamps(smb_node_t *node)
1469037cac00Sjoyce mcintosh {
1470037cac00Sjoyce mcintosh 	smb_node_t *unode;
1471037cac00Sjoyce mcintosh 
1472037cac00Sjoyce mcintosh 	if ((unode = SMB_IS_STREAM(node)) != NULL)
1473037cac00Sjoyce mcintosh 		node = unode;
1474037cac00Sjoyce mcintosh 
1475037cac00Sjoyce mcintosh 	mutex_enter(&node->n_mutex);
1476037cac00Sjoyce mcintosh 	ASSERT(node->n_timestamps.t_open_ofiles > 0);
1477037cac00Sjoyce mcintosh 	--(node->n_timestamps.t_open_ofiles);
1478037cac00Sjoyce mcintosh 	if (node->n_timestamps.t_open_ofiles == 0)
1479037cac00Sjoyce mcintosh 		bzero(&node->n_timestamps, sizeof (smb_times_t));
1480037cac00Sjoyce mcintosh 	mutex_exit(&node->n_mutex);
1481037cac00Sjoyce mcintosh }
1482037cac00Sjoyce mcintosh 
1483037cac00Sjoyce mcintosh /*
1484037cac00Sjoyce mcintosh  * smb_node_get_cached_timestamps
1485037cac00Sjoyce mcintosh  *
1486037cac00Sjoyce mcintosh  * Overwrite timestamps in attr with those cached in node.
1487037cac00Sjoyce mcintosh  */
1488037cac00Sjoyce mcintosh static void
1489037cac00Sjoyce mcintosh smb_node_get_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
1490037cac00Sjoyce mcintosh {
1491037cac00Sjoyce mcintosh 	smb_node_t *unode;
1492037cac00Sjoyce mcintosh 
1493037cac00Sjoyce mcintosh 	if ((unode = SMB_IS_STREAM(node)) != NULL)
1494037cac00Sjoyce mcintosh 		node = unode;
1495037cac00Sjoyce mcintosh 
1496037cac00Sjoyce mcintosh 	mutex_enter(&node->n_mutex);
1497037cac00Sjoyce mcintosh 	if (node->n_timestamps.t_cached) {
1498037cac00Sjoyce mcintosh 		attr->sa_vattr.va_mtime = node->n_timestamps.t_mtime;
1499037cac00Sjoyce mcintosh 		attr->sa_vattr.va_atime = node->n_timestamps.t_atime;
1500037cac00Sjoyce mcintosh 		attr->sa_vattr.va_ctime = node->n_timestamps.t_ctime;
1501037cac00Sjoyce mcintosh 		attr->sa_crtime = node->n_timestamps.t_crtime;
1502037cac00Sjoyce mcintosh 	}
1503037cac00Sjoyce mcintosh 	mutex_exit(&node->n_mutex);
1504037cac00Sjoyce mcintosh }
1505037cac00Sjoyce mcintosh 
1506037cac00Sjoyce mcintosh /*
1507037cac00Sjoyce mcintosh  * smb_node_set_cached_timestamps
1508037cac00Sjoyce mcintosh  *
1509037cac00Sjoyce mcintosh  * Update the node's cached timestamps with values from attr.
1510037cac00Sjoyce mcintosh  */
1511037cac00Sjoyce mcintosh static void
1512037cac00Sjoyce mcintosh smb_node_set_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
1513037cac00Sjoyce mcintosh {
1514037cac00Sjoyce mcintosh 	smb_node_t *unode;
1515037cac00Sjoyce mcintosh 
1516037cac00Sjoyce mcintosh 	if ((unode = SMB_IS_STREAM(node)) != NULL)
1517037cac00Sjoyce mcintosh 		node = unode;
1518037cac00Sjoyce mcintosh 
1519037cac00Sjoyce mcintosh 	mutex_enter(&node->n_mutex);
1520037cac00Sjoyce mcintosh 	if (node->n_timestamps.t_cached) {
1521037cac00Sjoyce mcintosh 		if (attr->sa_mask & SMB_AT_MTIME)
1522037cac00Sjoyce mcintosh 			node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime;
1523037cac00Sjoyce mcintosh 		if (attr->sa_mask & SMB_AT_ATIME)
1524037cac00Sjoyce mcintosh 			node->n_timestamps.t_atime = attr->sa_vattr.va_atime;
1525037cac00Sjoyce mcintosh 		if (attr->sa_mask & SMB_AT_CTIME)
1526037cac00Sjoyce mcintosh 			node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime;
1527037cac00Sjoyce mcintosh 		if (attr->sa_mask & SMB_AT_CRTIME)
1528037cac00Sjoyce mcintosh 			node->n_timestamps.t_crtime = attr->sa_crtime;
1529037cac00Sjoyce mcintosh 	}
1530037cac00Sjoyce mcintosh 	mutex_exit(&node->n_mutex);
1531037cac00Sjoyce mcintosh }
1532