xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_node.c (revision 10122:96eda55bfd54)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
228670SJose.Borrego@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw /*
265331Samw  * SMB Node State Machine
275331Samw  * ----------------------
285331Samw  *
299021Samw@Sun.COM  *
309021Samw@Sun.COM  *		    +----------- Creation/Allocation
315331Samw  *		    |
329021Samw@Sun.COM  *		    | T0
335331Samw  *		    |
345331Samw  *		    v
359021Samw@Sun.COM  *    +----------------------------+        T1
369021Samw@Sun.COM  *    |  SMB_NODE_STATE_AVAILABLE  |--------------------+
379021Samw@Sun.COM  *    +----------------------------+			|
389021Samw@Sun.COM  *		    |	     ^				|
399021Samw@Sun.COM  *		    |	     |				v
409021Samw@Sun.COM  *		    |	     |	  T2	+-------------------------------+
419021Samw@Sun.COM  *		    |	     |<---------| SMB_NODE_STATE_OPLOCK_GRANTED |
429021Samw@Sun.COM  *		    |	     |		+-------------------------------+
439021Samw@Sun.COM  *		    | T5     |				|
449021Samw@Sun.COM  *		    |	     |				| T3
459021Samw@Sun.COM  *		    |	     |				v
469021Samw@Sun.COM  *		    |	     |	  T4	+--------------------------------+
479021Samw@Sun.COM  *		    |	     +----------| SMB_NODE_STATE_OPLOCK_BREAKING |
489021Samw@Sun.COM  *		    |			+--------------------------------+
499021Samw@Sun.COM  *		    |
509021Samw@Sun.COM  *		    v
515331Samw  *    +-----------------------------+
529021Samw@Sun.COM  *    |  SMB_NODE_STATE_DESTROYING  |
539021Samw@Sun.COM  *    +-----------------------------+
549021Samw@Sun.COM  *		    |
559021Samw@Sun.COM  *		    |
569021Samw@Sun.COM  *		    | T6
579021Samw@Sun.COM  *		    |
589021Samw@Sun.COM  *		    +----------> Deletion/Free
595331Samw  *
605331Samw  * Transition T0
615331Samw  *
625331Samw  *    This transition occurs in smb_node_lookup(). If the node looked for is
635331Samw  *    not found in the has table a new node is created. The reference count is
645331Samw  *    initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE.
655331Samw  *
665331Samw  * Transition T1
675331Samw  *
689021Samw@Sun.COM  *    This transition occurs smb_oplock_acquire() during an OPEN.
699021Samw@Sun.COM  *
709021Samw@Sun.COM  * Transition T2
719021Samw@Sun.COM  *
729021Samw@Sun.COM  *    This transition occurs in smb_oplock_release(). The events triggering
739021Samw@Sun.COM  *    it are:
749021Samw@Sun.COM  *
759021Samw@Sun.COM  *	- LockingAndX sent by the client that was granted the oplock.
769021Samw@Sun.COM  *	- Closing of the file.
779021Samw@Sun.COM  *
789021Samw@Sun.COM  * Transition T3
799021Samw@Sun.COM  *
809021Samw@Sun.COM  *    This transition occurs in smb_oplock_break(). The events triggering
819021Samw@Sun.COM  *    it are:
829021Samw@Sun.COM  *
839021Samw@Sun.COM  *	- Another client wants to open the file.
849021Samw@Sun.COM  *	- A client is trying to delete the file.
859021Samw@Sun.COM  *	- A client is trying to rename the file.
869021Samw@Sun.COM  *	- A client is trying to set/modify  the file attributes.
879021Samw@Sun.COM  *
889021Samw@Sun.COM  * Transition T4
899021Samw@Sun.COM  *
909021Samw@Sun.COM  *    This transition occurs in smb_oplock_release or smb_oplock_break(). The
919021Samw@Sun.COM  *    events triggering it are:
929021Samw@Sun.COM  *
939021Samw@Sun.COM  *	- The client that was granting the oplock releases it (close or
949021Samw@Sun.COM  *	  LockingAndx).
959021Samw@Sun.COM  *	- The time alloted to release the oplock expired.
969021Samw@Sun.COM  *
979021Samw@Sun.COM  * Transition T5
989021Samw@Sun.COM  *
995331Samw  *    This transition occurs in smb_node_release(). If the reference count
1005331Samw  *    drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more
1015331Samw  *    reference count will be given out for that node.
1025331Samw  *
1039021Samw@Sun.COM  * Transition T6
1045331Samw  *
1055331Samw  *    This transition occurs in smb_node_release(). The structure is deleted.
1065331Samw  *
1075331Samw  * Comments
1085331Samw  * --------
1095331Samw  *
1105331Samw  *    The reason the smb node has 2 states is the following synchronization
1115331Samw  *    rule:
1125331Samw  *
1135331Samw  *    There's a mutex embedded in the node used to protect its fields and
1145331Samw  *    there's a lock embedded in the bucket of the hash table the node belongs
1155331Samw  *    to. To increment or to decrement the reference count the mutex must be
1165331Samw  *    entered. To insert the node into the bucket and to remove it from the
1175331Samw  *    bucket the lock must be entered in RW_WRITER mode. When both (mutex and
1185331Samw  *    lock) have to be entered, the lock has always to be entered first then
1195331Samw  *    the mutex. This prevents a deadlock between smb_node_lookup() and
1205331Samw  *    smb_node_release() from occurring. However, in smb_node_release() when the
1215331Samw  *    reference count drops to zero and triggers the deletion of the node, the
1225331Samw  *    mutex has to be released before entering the lock of the bucket (to
1235331Samw  *    remove the node). This creates a window during which the node that is
1245331Samw  *    about to be freed could be given out by smb_node_lookup(). To close that
1255331Samw  *    window the node is moved to the state SMB_NODE_STATE_DESTROYING before
1265331Samw  *    releasing the mutex. That way, even if smb_node_lookup() finds it, the
1275331Samw  *    state will indicate that the node should be treated as non existent (of
1285331Samw  *    course the state of the node should be tested/updated under the
1295331Samw  *    protection of the mutex).
1305331Samw  */
1315331Samw #include <smbsrv/smb_incl.h>
1325331Samw #include <smbsrv/smb_fsops.h>
1338934SJose.Borrego@Sun.COM #include <smbsrv/smb_kstat.h>
1345331Samw #include <sys/pathname.h>
1355331Samw #include <sys/sdt.h>
1365772Sas200622 #include <sys/nbmlock.h>
1375331Samw 
1388934SJose.Borrego@Sun.COM uint32_t smb_is_executable(char *);
1398934SJose.Borrego@Sun.COM static void smb_node_delete_on_close(smb_node_t *);
1408934SJose.Borrego@Sun.COM static void smb_node_create_audit_buf(smb_node_t *, int);
1418934SJose.Borrego@Sun.COM static void smb_node_destroy_audit_buf(smb_node_t *);
1428934SJose.Borrego@Sun.COM static void smb_node_audit(smb_node_t *);
14310001SJoyce.McIntosh@Sun.COM static smb_node_t *smb_node_alloc(char *, vnode_t *, smb_llist_t *, uint32_t);
1448934SJose.Borrego@Sun.COM static void smb_node_free(smb_node_t *);
1458934SJose.Borrego@Sun.COM static int smb_node_constructor(void *, void *, int);
1468934SJose.Borrego@Sun.COM static void smb_node_destructor(void *, void *);
1478934SJose.Borrego@Sun.COM static smb_llist_t *smb_node_get_hash(fsid_t *, smb_attr_t *, uint32_t *);
14810001SJoyce.McIntosh@Sun.COM static void smb_node_init_cached_timestamps(smb_node_t *);
14910001SJoyce.McIntosh@Sun.COM static void smb_node_clear_cached_timestamps(smb_node_t *);
15010001SJoyce.McIntosh@Sun.COM static void smb_node_get_cached_timestamps(smb_node_t *, smb_attr_t *);
15110001SJoyce.McIntosh@Sun.COM static void smb_node_set_cached_timestamps(smb_node_t *, smb_attr_t *);
1525331Samw 
1535331Samw #define	VALIDATE_DIR_NODE(_dir_, _node_) \
1545331Samw     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
1555331Samw     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
156*10122SJordan.Brown@Sun.COM     ASSERT((_dir_)->n_dnode != (_node_));
1575331Samw 
1588934SJose.Borrego@Sun.COM static kmem_cache_t	*smb_node_cache = NULL;
1596139Sjb150015 static boolean_t	smb_node_initialized = B_FALSE;
1606139Sjb150015 static smb_llist_t	smb_node_hash_table[SMBND_HASH_MASK+1];
1616139Sjb150015 
1626139Sjb150015 /*
1636139Sjb150015  * smb_node_init
1646139Sjb150015  *
1656139Sjb150015  * Initialization of the SMB node layer.
1666139Sjb150015  *
1676139Sjb150015  * This function is not multi-thread safe. The caller must make sure only one
1686139Sjb150015  * thread makes the call.
1696139Sjb150015  */
1706139Sjb150015 int
1716139Sjb150015 smb_node_init(void)
1726139Sjb150015 {
1736139Sjb150015 	int	i;
1746139Sjb150015 
1756139Sjb150015 	if (smb_node_initialized)
1766139Sjb150015 		return (0);
1778934SJose.Borrego@Sun.COM 	smb_node_cache = kmem_cache_create(SMBSRV_KSTAT_NODE_CACHE,
1788934SJose.Borrego@Sun.COM 	    sizeof (smb_node_t), 8, smb_node_constructor, smb_node_destructor,
1798934SJose.Borrego@Sun.COM 	    NULL, NULL, NULL, 0);
1806139Sjb150015 
1816139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
1826139Sjb150015 		smb_llist_constructor(&smb_node_hash_table[i],
1836139Sjb150015 		    sizeof (smb_node_t), offsetof(smb_node_t, n_lnd));
1846139Sjb150015 	}
1856139Sjb150015 	smb_node_initialized = B_TRUE;
1866139Sjb150015 	return (0);
1876139Sjb150015 }
1886139Sjb150015 
1896139Sjb150015 /*
1906139Sjb150015  * smb_node_fini
1916139Sjb150015  *
1926139Sjb150015  * This function is not multi-thread safe. The caller must make sure only one
1936139Sjb150015  * thread makes the call.
1946139Sjb150015  */
1956139Sjb150015 void
1966139Sjb150015 smb_node_fini(void)
1976139Sjb150015 {
1986139Sjb150015 	int	i;
1996139Sjb150015 
2006139Sjb150015 	if (!smb_node_initialized)
2016139Sjb150015 		return;
2026139Sjb150015 
2036139Sjb150015 #ifdef DEBUG
2046139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
2056139Sjb150015 		smb_node_t	*node;
2066139Sjb150015 
2076139Sjb150015 		/*
2086139Sjb150015 		 * The following sequence is just intended for sanity check.
2096139Sjb150015 		 * This will have to be modified when the code goes into
2106139Sjb150015 		 * production.
2116139Sjb150015 		 *
2126139Sjb150015 		 * The SMB node hash table should be emtpy at this point. If the
2136139Sjb150015 		 * hash table is not empty a panic will be triggered.
2146139Sjb150015 		 *
2156139Sjb150015 		 * The reason why SMB nodes are still remaining in the hash
2166139Sjb150015 		 * table is problably due to a mismatch between calls to
2176139Sjb150015 		 * smb_node_lookup() and smb_node_release(). You must track that
2186139Sjb150015 		 * down.
2196139Sjb150015 		 */
2206139Sjb150015 		node = smb_llist_head(&smb_node_hash_table[i]);
2216139Sjb150015 		ASSERT(node == NULL);
2226139Sjb150015 	}
2236139Sjb150015 #endif
2246139Sjb150015 
2256139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
2266139Sjb150015 		smb_llist_destructor(&smb_node_hash_table[i]);
2276139Sjb150015 	}
2288934SJose.Borrego@Sun.COM 	kmem_cache_destroy(smb_node_cache);
2298934SJose.Borrego@Sun.COM 	smb_node_cache = NULL;
2306139Sjb150015 	smb_node_initialized = B_FALSE;
2316139Sjb150015 }
2326139Sjb150015 
2335331Samw /*
2345331Samw  * smb_node_lookup()
2355331Samw  *
2365331Samw  * NOTE: This routine should only be called by the file system interface layer,
2375331Samw  * and not by SMB.
2385331Samw  *
2395331Samw  * smb_node_lookup() is called upon successful lookup, mkdir, and create
2405331Samw  * (for both non-streams and streams).  In each of these cases, a held vnode is
2418670SJose.Borrego@Sun.COM  * passed into this routine.  If a new smb_node is created it will take its
2428670SJose.Borrego@Sun.COM  * own hold on the vnode.  The caller's hold therefore still belongs to, and
2438670SJose.Borrego@Sun.COM  * should be released by, the caller.
2445331Samw  *
2455331Samw  * A reference is taken on the smb_node whether found in the hash table
2465331Samw  * or newly created.
2475331Samw  *
2485331Samw  * If an smb_node needs to be created, a reference is also taken on the
249*10122SJordan.Brown@Sun.COM  * dnode (if passed in).
2505331Samw  *
2515331Samw  * See smb_node_release() for details on the release of these references.
2525331Samw  */
2535331Samw 
2545331Samw /*ARGSUSED*/
2555331Samw smb_node_t *
2565331Samw smb_node_lookup(
2575331Samw     struct smb_request	*sr,
2585331Samw     struct open_param	*op,
2595331Samw     cred_t		*cred,
2605331Samw     vnode_t		*vp,
2615331Samw     char		*od_name,
262*10122SJordan.Brown@Sun.COM     smb_node_t		*dnode,
263*10122SJordan.Brown@Sun.COM     smb_node_t		*unode)
2645331Samw {
2655331Samw 	smb_llist_t		*node_hdr;
2665331Samw 	smb_node_t		*node;
26710001SJoyce.McIntosh@Sun.COM 	smb_attr_t		attr;
2685331Samw 	uint32_t		hashkey = 0;
2697348SJose.Borrego@Sun.COM 	fsid_t			fsid;
2705331Samw 	int			error;
2715331Samw 	krw_t			lock_mode;
2725331Samw 	vnode_t			*unnamed_vp = NULL;
2735331Samw 
2745331Samw 	/*
2755331Samw 	 * smb_vop_getattr() is called here instead of smb_fsop_getattr(),
2765331Samw 	 * because the node may not yet exist.  We also do not want to call
2775331Samw 	 * it with the list lock held.
2785331Samw 	 */
2795331Samw 
280*10122SJordan.Brown@Sun.COM 	if (unode)
281*10122SJordan.Brown@Sun.COM 		unnamed_vp = unode->vp;
2825331Samw 
2835331Samw 	/*
2845331Samw 	 * This getattr is performed on behalf of the server
2855331Samw 	 * that's why kcred is used not the user's cred
2865331Samw 	 */
28710001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_ALL;
28810001SJoyce.McIntosh@Sun.COM 	error = smb_vop_getattr(vp, unnamed_vp, &attr, 0, kcred);
2895331Samw 	if (error)
2905331Samw 		return (NULL);
2915331Samw 
2927348SJose.Borrego@Sun.COM 	if (sr && sr->tid_tree) {
2937348SJose.Borrego@Sun.COM 		/*
2947348SJose.Borrego@Sun.COM 		 * The fsid for a file is that of the tree, even
2957348SJose.Borrego@Sun.COM 		 * if the file resides in a different mountpoint
2967348SJose.Borrego@Sun.COM 		 * under the share.
2977348SJose.Borrego@Sun.COM 		 */
2987348SJose.Borrego@Sun.COM 		fsid = SMB_TREE_FSID(sr->tid_tree);
2995331Samw 	} else {
3007348SJose.Borrego@Sun.COM 		/*
3017348SJose.Borrego@Sun.COM 		 * This should be getting executed only for the
3027348SJose.Borrego@Sun.COM 		 * tree root smb_node.
3037348SJose.Borrego@Sun.COM 		 */
3047348SJose.Borrego@Sun.COM 		fsid = vp->v_vfsp->vfs_fsid;
3055331Samw 	}
3065331Samw 
30710001SJoyce.McIntosh@Sun.COM 	node_hdr = smb_node_get_hash(&fsid, &attr, &hashkey);
3085331Samw 	lock_mode = RW_READER;
3095331Samw 
3105331Samw 	smb_llist_enter(node_hdr, lock_mode);
3115331Samw 	for (;;) {
3125331Samw 		node = list_head(&node_hdr->ll_list);
3135331Samw 		while (node) {
3145331Samw 			ASSERT(node->n_magic == SMB_NODE_MAGIC);
3155331Samw 			ASSERT(node->n_hash_bucket == node_hdr);
3165331Samw 			if ((node->n_hashkey == hashkey) && (node->vp == vp)) {
3178934SJose.Borrego@Sun.COM 				mutex_enter(&node->n_mutex);
3185331Samw 				DTRACE_PROBE1(smb_node_lookup_hit,
3195331Samw 				    smb_node_t *, node);
3205331Samw 				switch (node->n_state) {
3218934SJose.Borrego@Sun.COM 				case SMB_NODE_STATE_OPLOCK_GRANTED:
3228934SJose.Borrego@Sun.COM 				case SMB_NODE_STATE_OPLOCK_BREAKING:
3235331Samw 				case SMB_NODE_STATE_AVAILABLE:
3245331Samw 					/* The node was found. */
3255331Samw 					node->n_refcnt++;
326*10122SJordan.Brown@Sun.COM 					if ((node->n_dnode == NULL) &&
327*10122SJordan.Brown@Sun.COM 					    (dnode != NULL) &&
3285331Samw 					    (strcmp(od_name, "..") != 0) &&
3295331Samw 					    (strcmp(od_name, ".") != 0)) {
330*10122SJordan.Brown@Sun.COM 						VALIDATE_DIR_NODE(dnode, node);
331*10122SJordan.Brown@Sun.COM 						node->n_dnode = dnode;
332*10122SJordan.Brown@Sun.COM 						smb_node_ref(dnode);
3335331Samw 					}
3345331Samw 
3358934SJose.Borrego@Sun.COM 					smb_node_audit(node);
3368934SJose.Borrego@Sun.COM 					mutex_exit(&node->n_mutex);
3375331Samw 					smb_llist_exit(node_hdr);
3385331Samw 					return (node);
3395331Samw 
3405331Samw 				case SMB_NODE_STATE_DESTROYING:
3415331Samw 					/*
3425331Samw 					 * Although the node exists it is about
3435331Samw 					 * to be destroyed. We act as it hasn't
3445331Samw 					 * been found.
3455331Samw 					 */
3468934SJose.Borrego@Sun.COM 					mutex_exit(&node->n_mutex);
3475331Samw 					break;
3485331Samw 				default:
3495331Samw 					/*
3505331Samw 					 * Although the node exists it is in an
3515331Samw 					 * unknown state. We act as it hasn't
3525331Samw 					 * been found.
3535331Samw 					 */
3545331Samw 					ASSERT(0);
3558934SJose.Borrego@Sun.COM 					mutex_exit(&node->n_mutex);
3565331Samw 					break;
3575331Samw 				}
3585331Samw 			}
3595331Samw 			node = smb_llist_next(node_hdr, node);
3605331Samw 		}
3615331Samw 		if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) {
3625331Samw 			lock_mode = RW_WRITER;
3635331Samw 			continue;
3645331Samw 		}
3655331Samw 		break;
3665331Samw 	}
36710001SJoyce.McIntosh@Sun.COM 	node = smb_node_alloc(od_name, vp, node_hdr, hashkey);
3688934SJose.Borrego@Sun.COM 	node->n_orig_uid = crgetuid(sr->user_cr);
3695331Samw 
3705331Samw 	if (op)
3719343SAfshin.Ardakani@Sun.COM 		node->flags |= smb_is_executable(op->fqi.fq_last_comp);
3725331Samw 
373*10122SJordan.Brown@Sun.COM 	if (dnode) {
374*10122SJordan.Brown@Sun.COM 		smb_node_ref(dnode);
375*10122SJordan.Brown@Sun.COM 		node->n_dnode = dnode;
376*10122SJordan.Brown@Sun.COM 		ASSERT(dnode->n_dnode != node);
377*10122SJordan.Brown@Sun.COM 		ASSERT((dnode->vp->v_xattrdir) ||
378*10122SJordan.Brown@Sun.COM 		    (dnode->vp->v_type == VDIR));
3795331Samw 	}
3805331Samw 
381*10122SJordan.Brown@Sun.COM 	if (unode) {
382*10122SJordan.Brown@Sun.COM 		smb_node_ref(unode);
383*10122SJordan.Brown@Sun.COM 		node->n_unode = unode;
3845331Samw 	}
3855331Samw 
3865331Samw 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
3878934SJose.Borrego@Sun.COM 	smb_node_audit(node);
3885331Samw 	smb_llist_insert_head(node_hdr, node);
3895331Samw 	smb_llist_exit(node_hdr);
3905331Samw 	return (node);
3915331Samw }
3925331Samw 
3935331Samw /*
3945331Samw  * smb_stream_node_lookup()
3955331Samw  *
3965331Samw  * Note: stream_name (the name that will be stored in the "od_name" field
3975331Samw  * of a stream's smb_node) is the same as the on-disk name for the stream
3985331Samw  * except that it does not have SMB_STREAM_PREFIX prepended.
3995331Samw  */
4005331Samw 
4015331Samw smb_node_t *
4028934SJose.Borrego@Sun.COM smb_stream_node_lookup(smb_request_t *sr, cred_t *cr, smb_node_t *fnode,
40310001SJoyce.McIntosh@Sun.COM     vnode_t *xattrdirvp, vnode_t *vp, char *stream_name)
4045331Samw {
4055331Samw 	smb_node_t	*xattrdir_node;
4065331Samw 	smb_node_t	*snode;
4075331Samw 
4085331Samw 	xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR,
40910001SJoyce.McIntosh@Sun.COM 	    fnode, NULL);
4105331Samw 
4115331Samw 	if (xattrdir_node == NULL)
4125331Samw 		return (NULL);
4135331Samw 
4145331Samw 	snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node,
41510001SJoyce.McIntosh@Sun.COM 	    fnode);
4165331Samw 
4175331Samw 	(void) smb_node_release(xattrdir_node);
4185331Samw 	return (snode);
4195331Samw }
4205331Samw 
4215331Samw 
4225331Samw /*
4235331Samw  * This function should be called whenever a reference is needed on an
4245331Samw  * smb_node pointer.  The copy of an smb_node pointer from one non-local
4255331Samw  * data structure to another requires a reference to be taken on the smb_node
4265331Samw  * (unless the usage is localized).  Each data structure deallocation routine
4275331Samw  * will call smb_node_release() on its smb_node pointers.
4285331Samw  *
4295331Samw  * In general, an smb_node pointer residing in a structure should never be
4305331Samw  * stale.  A node pointer may be NULL, however, and care should be taken
4315331Samw  * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid.
4325331Samw  * Care also needs to be taken with respect to racing deallocations of a
4335331Samw  * structure.
4345331Samw  */
4355331Samw void
4365331Samw smb_node_ref(smb_node_t *node)
4375331Samw {
4388934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
4395331Samw 
4408934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
4418934SJose.Borrego@Sun.COM 	switch (node->n_state) {
4428934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_AVAILABLE:
4438934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_GRANTED:
4448934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_BREAKING:
4458934SJose.Borrego@Sun.COM 		node->n_refcnt++;
4468934SJose.Borrego@Sun.COM 		ASSERT(node->n_refcnt);
4478934SJose.Borrego@Sun.COM 		DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node);
4488934SJose.Borrego@Sun.COM 		smb_node_audit(node);
4498934SJose.Borrego@Sun.COM 		break;
4508934SJose.Borrego@Sun.COM 	default:
4518934SJose.Borrego@Sun.COM 		SMB_PANIC();
4528934SJose.Borrego@Sun.COM 	}
4538934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
4545331Samw }
4555331Samw 
4565331Samw /*
4575331Samw  * smb_node_lookup() takes a hold on an smb_node, whether found in the
4585331Samw  * hash table or newly created.  This hold is expected to be released
4595331Samw  * in the following manner.
4605331Samw  *
4615331Samw  * smb_node_lookup() takes an address of an smb_node pointer.  This should
4625331Samw  * be getting passed down via a lookup (whether path name or component), mkdir,
4635331Samw  * create.  If the original smb_node pointer resides in a data structure, then
4645331Samw  * the deallocation routine for the data structure is responsible for calling
4655331Samw  * smb_node_release() on the smb_node pointer.  Alternatively,
4665331Samw  * smb_node_release() can be called as soon as the smb_node pointer is no longer
4675331Samw  * needed.  In this case, callers are responsible for setting an embedded
4685331Samw  * pointer to NULL if it is known that the last reference is being released.
4695331Samw  *
4705331Samw  * If the passed-in address of the smb_node pointer belongs to a local variable,
4715331Samw  * then the caller with the local variable should call smb_node_release()
4725331Samw  * directly.
4735331Samw  *
474*10122SJordan.Brown@Sun.COM  * smb_node_release() itself will call smb_node_release() on a node's n_dnode,
475*10122SJordan.Brown@Sun.COM  * as smb_node_lookup() takes a hold on dnode.
4765331Samw  */
4775331Samw void
4785331Samw smb_node_release(smb_node_t *node)
4795331Samw {
4808934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
4815331Samw 
4828934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
4835331Samw 	ASSERT(node->n_refcnt);
4845331Samw 	DTRACE_PROBE1(smb_node_release, smb_node_t *, node);
4855331Samw 	if (--node->n_refcnt == 0) {
4865331Samw 		switch (node->n_state) {
4875331Samw 
4885331Samw 		case SMB_NODE_STATE_AVAILABLE:
4895331Samw 			node->n_state = SMB_NODE_STATE_DESTROYING;
4908934SJose.Borrego@Sun.COM 			mutex_exit(&node->n_mutex);
4915331Samw 
4925331Samw 			smb_llist_enter(node->n_hash_bucket, RW_WRITER);
4935331Samw 			smb_llist_remove(node->n_hash_bucket, node);
4945331Samw 			smb_llist_exit(node->n_hash_bucket);
4955331Samw 
4965331Samw 			/*
4975331Samw 			 * Check if the file was deleted
4985331Samw 			 */
4995331Samw 			smb_node_delete_on_close(node);
5005331Samw 
501*10122SJordan.Brown@Sun.COM 			if (node->n_dnode) {
502*10122SJordan.Brown@Sun.COM 				ASSERT(node->n_dnode->n_magic ==
5035331Samw 				    SMB_NODE_MAGIC);
504*10122SJordan.Brown@Sun.COM 				smb_node_release(node->n_dnode);
5055331Samw 			}
5065331Samw 
507*10122SJordan.Brown@Sun.COM 			if (node->n_unode) {
508*10122SJordan.Brown@Sun.COM 				ASSERT(node->n_unode->n_magic ==
5095331Samw 				    SMB_NODE_MAGIC);
510*10122SJordan.Brown@Sun.COM 				smb_node_release(node->n_unode);
5115331Samw 			}
5125331Samw 
5138934SJose.Borrego@Sun.COM 			smb_node_free(node);
5145331Samw 			return;
5155331Samw 
5165331Samw 		default:
5178934SJose.Borrego@Sun.COM 			SMB_PANIC();
5185331Samw 		}
5195331Samw 	}
5208934SJose.Borrego@Sun.COM 	smb_node_audit(node);
5218934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
5225331Samw }
5235331Samw 
5245331Samw static void
5255331Samw smb_node_delete_on_close(smb_node_t *node)
5265331Samw {
5275331Samw 	smb_node_t	*d_snode;
5285331Samw 	int		rc = 0;
5299231SAfshin.Ardakani@Sun.COM 	uint32_t	flags = 0;
5305331Samw 
531*10122SJordan.Brown@Sun.COM 	d_snode = node->n_dnode;
5325331Samw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
5339231SAfshin.Ardakani@Sun.COM 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
5349231SAfshin.Ardakani@Sun.COM 		flags = node->n_delete_on_close_flags;
5359231SAfshin.Ardakani@Sun.COM 		ASSERT(node->od_name != NULL);
5365331Samw 
53710001SJoyce.McIntosh@Sun.COM 		if (node->vp->v_type == VDIR)
5385331Samw 			rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
5399231SAfshin.Ardakani@Sun.COM 			    d_snode, node->od_name, flags);
5405331Samw 		else
5415331Samw 			rc = smb_fsop_remove(0, node->delete_on_close_cred,
5429231SAfshin.Ardakani@Sun.COM 			    d_snode, node->od_name, flags);
5435331Samw 		smb_cred_rele(node->delete_on_close_cred);
5445331Samw 	}
5455331Samw 	if (rc != 0)
5465331Samw 		cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
5475331Samw 		    node->od_name, rc);
5485331Samw 	DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
5495331Samw }
5505331Samw 
5515331Samw /*
5525331Samw  * smb_node_rename()
5535331Samw  *
5545331Samw  */
5558934SJose.Borrego@Sun.COM void
5565331Samw smb_node_rename(
5578934SJose.Borrego@Sun.COM     smb_node_t	*from_dnode,
5588934SJose.Borrego@Sun.COM     smb_node_t	*ret_node,
5598934SJose.Borrego@Sun.COM     smb_node_t	*to_dnode,
5605331Samw     char	*to_name)
5615331Samw {
5628934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(from_dnode);
5638934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(to_dnode);
5648934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(ret_node);
5655331Samw 
5668934SJose.Borrego@Sun.COM 	smb_node_ref(to_dnode);
5678934SJose.Borrego@Sun.COM 	mutex_enter(&ret_node->n_mutex);
5688934SJose.Borrego@Sun.COM 	switch (ret_node->n_state) {
5698934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_AVAILABLE:
5708934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_GRANTED:
5718934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_BREAKING:
572*10122SJordan.Brown@Sun.COM 		ret_node->n_dnode = to_dnode;
5738934SJose.Borrego@Sun.COM 		mutex_exit(&ret_node->n_mutex);
574*10122SJordan.Brown@Sun.COM 		ASSERT(to_dnode->n_dnode != ret_node);
5758934SJose.Borrego@Sun.COM 		ASSERT((to_dnode->vp->v_xattrdir) ||
5768934SJose.Borrego@Sun.COM 		    (to_dnode->vp->v_type == VDIR));
5778934SJose.Borrego@Sun.COM 		smb_node_release(from_dnode);
5788934SJose.Borrego@Sun.COM 		(void) strcpy(ret_node->od_name, to_name);
5798934SJose.Borrego@Sun.COM 		/*
5808934SJose.Borrego@Sun.COM 		 * XXX Need to update attributes?
5818934SJose.Borrego@Sun.COM 		 */
5828934SJose.Borrego@Sun.COM 		break;
5838934SJose.Borrego@Sun.COM 	default:
5848934SJose.Borrego@Sun.COM 		SMB_PANIC();
5858934SJose.Borrego@Sun.COM 	}
5865331Samw }
5875331Samw 
5885331Samw int
5896139Sjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root)
5905331Samw {
59110001SJoyce.McIntosh@Sun.COM 	smb_attr_t	attr;
5926139Sjb150015 	int		error;
5936139Sjb150015 	uint32_t	hashkey;
5946139Sjb150015 	smb_llist_t	*node_hdr;
5956139Sjb150015 	smb_node_t	*node;
5965331Samw 
59710001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_ALL;
59810001SJoyce.McIntosh@Sun.COM 	error = smb_vop_getattr(vp, NULL, &attr, 0, kcred);
5996139Sjb150015 	if (error) {
6006139Sjb150015 		VN_RELE(vp);
6016139Sjb150015 		return (error);
6026139Sjb150015 	}
6036139Sjb150015 
60410001SJoyce.McIntosh@Sun.COM 	node_hdr = smb_node_get_hash(&vp->v_vfsp->vfs_fsid, &attr, &hashkey);
6055331Samw 
60610001SJoyce.McIntosh@Sun.COM 	node = smb_node_alloc(ROOTVOL, vp, node_hdr, hashkey);
6076139Sjb150015 
6086139Sjb150015 	sv->si_root_smb_node = node;
6098934SJose.Borrego@Sun.COM 	smb_node_audit(node);
6106139Sjb150015 	smb_llist_enter(node_hdr, RW_WRITER);
6116139Sjb150015 	smb_llist_insert_head(node_hdr, node);
6126139Sjb150015 	smb_llist_exit(node_hdr);
6136139Sjb150015 	*root = node;
6146139Sjb150015 	return (0);
6155331Samw }
6165331Samw 
6175331Samw /*
6189231SAfshin.Ardakani@Sun.COM  * When DeleteOnClose is set on an smb_node, the common open code will
6199231SAfshin.Ardakani@Sun.COM  * reject subsequent open requests for the file. Observation of Windows
6209231SAfshin.Ardakani@Sun.COM  * 2000 indicates that subsequent opens should be allowed (assuming
6219231SAfshin.Ardakani@Sun.COM  * there would be no sharing violation) until the file is closed using
6229231SAfshin.Ardakani@Sun.COM  * the fid on which the DeleteOnClose was requested.
6239231SAfshin.Ardakani@Sun.COM  *
6249231SAfshin.Ardakani@Sun.COM  * If there are multiple opens with delete-on-close create options,
6259231SAfshin.Ardakani@Sun.COM  * whichever the first file handle is closed will trigger the node to be
6269231SAfshin.Ardakani@Sun.COM  * marked as delete-on-close. The credentials of that ofile will be used
6279231SAfshin.Ardakani@Sun.COM  * as the delete-on-close credentials of the node.
6289231SAfshin.Ardakani@Sun.COM  */
6295331Samw int
6309231SAfshin.Ardakani@Sun.COM smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags)
6315331Samw {
63210001SJoyce.McIntosh@Sun.COM 	int rc;
63310001SJoyce.McIntosh@Sun.COM 	smb_attr_t attr;
6345331Samw 
6358934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
63610001SJoyce.McIntosh@Sun.COM 
63710001SJoyce.McIntosh@Sun.COM 	if ((node->flags & NODE_FLAGS_DELETE_ON_CLOSE) ||
63810001SJoyce.McIntosh@Sun.COM 	    (node->readonly_creator)) {
63910001SJoyce.McIntosh@Sun.COM 		mutex_exit(&node->n_mutex);
64010001SJoyce.McIntosh@Sun.COM 		return (-1);
6415331Samw 	}
64210001SJoyce.McIntosh@Sun.COM 
64310001SJoyce.McIntosh@Sun.COM 	bzero(&attr, sizeof (smb_attr_t));
64410001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_DOSATTR;
64510001SJoyce.McIntosh@Sun.COM 	rc = smb_fsop_getattr(NULL, kcred, node, &attr);
64610001SJoyce.McIntosh@Sun.COM 	if ((rc != 0) || (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) {
64710001SJoyce.McIntosh@Sun.COM 		mutex_exit(&node->n_mutex);
64810001SJoyce.McIntosh@Sun.COM 		return (-1);
64910001SJoyce.McIntosh@Sun.COM 	}
65010001SJoyce.McIntosh@Sun.COM 
65110001SJoyce.McIntosh@Sun.COM 	crhold(cr);
65210001SJoyce.McIntosh@Sun.COM 	node->delete_on_close_cred = cr;
65310001SJoyce.McIntosh@Sun.COM 	node->n_delete_on_close_flags = flags;
65410001SJoyce.McIntosh@Sun.COM 	node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
6558934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
65610001SJoyce.McIntosh@Sun.COM 	return (0);
6575331Samw }
6585331Samw 
6595331Samw void
6605331Samw smb_node_reset_delete_on_close(smb_node_t *node)
6615331Samw {
6628934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
6635331Samw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
6645331Samw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
6655331Samw 		crfree(node->delete_on_close_cred);
6665331Samw 		node->delete_on_close_cred = NULL;
6679231SAfshin.Ardakani@Sun.COM 		node->n_delete_on_close_flags = 0;
6685331Samw 	}
6698934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
6705331Samw }
6715772Sas200622 
6725772Sas200622 /*
6736771Sjb150015  * smb_node_open_check
6745772Sas200622  *
6755772Sas200622  * check file sharing rules for current open request
6765772Sas200622  * against all existing opens for a file.
6775772Sas200622  *
6785772Sas200622  * Returns NT_STATUS_SHARING_VIOLATION if there is any
6795772Sas200622  * sharing conflict, otherwise returns NT_STATUS_SUCCESS.
6805772Sas200622  */
6815772Sas200622 uint32_t
6828934SJose.Borrego@Sun.COM smb_node_open_check(
6838934SJose.Borrego@Sun.COM     smb_node_t	*node,
6848934SJose.Borrego@Sun.COM     cred_t	*cr,
6858934SJose.Borrego@Sun.COM     uint32_t	desired_access,
6868934SJose.Borrego@Sun.COM     uint32_t	share_access)
6875772Sas200622 {
6885772Sas200622 	smb_ofile_t *of;
6895772Sas200622 	uint32_t status;
6905772Sas200622 
6918934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
6925772Sas200622 
6935772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
6945772Sas200622 	of = smb_llist_head(&node->n_ofile_list);
6955772Sas200622 	while (of) {
6966771Sjb150015 		status = smb_ofile_open_check(of, cr, desired_access,
6976771Sjb150015 		    share_access);
6986771Sjb150015 
6996771Sjb150015 		switch (status) {
7006771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7016771Sjb150015 		case NT_STATUS_SUCCESS:
7026771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7036771Sjb150015 			break;
7046771Sjb150015 		default:
7056771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
7065772Sas200622 			smb_llist_exit(&node->n_ofile_list);
7075772Sas200622 			return (status);
7085772Sas200622 		}
7095772Sas200622 	}
7106771Sjb150015 
7115772Sas200622 	smb_llist_exit(&node->n_ofile_list);
7125772Sas200622 	return (NT_STATUS_SUCCESS);
7135772Sas200622 }
7145772Sas200622 
7155772Sas200622 uint32_t
7168934SJose.Borrego@Sun.COM smb_node_rename_check(smb_node_t *node)
7175772Sas200622 {
7188934SJose.Borrego@Sun.COM 	smb_ofile_t	*of;
7198934SJose.Borrego@Sun.COM 	uint32_t	status;
7205772Sas200622 
7218934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
7225772Sas200622 
7235772Sas200622 	/*
7245772Sas200622 	 * Intra-CIFS check
7255772Sas200622 	 */
7265772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
7276771Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
7286771Sjb150015 	while (of) {
7296771Sjb150015 		status = smb_ofile_rename_check(of);
7305772Sas200622 
7316771Sjb150015 		switch (status) {
7326771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7336771Sjb150015 		case NT_STATUS_SUCCESS:
7346771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7356771Sjb150015 			break;
7366771Sjb150015 		default:
7376771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
7386771Sjb150015 			smb_llist_exit(&node->n_ofile_list);
7396771Sjb150015 			return (status);
7405772Sas200622 		}
7415772Sas200622 	}
7425772Sas200622 	smb_llist_exit(&node->n_ofile_list);
7435772Sas200622 
7445772Sas200622 	/*
7455772Sas200622 	 * system-wide share check
7465772Sas200622 	 */
7475772Sas200622 	if (nbl_share_conflict(node->vp, NBL_RENAME, NULL))
7485772Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
7495772Sas200622 	else
7505772Sas200622 		return (NT_STATUS_SUCCESS);
7515772Sas200622 }
7525772Sas200622 
7536771Sjb150015 uint32_t
7545772Sas200622 smb_node_delete_check(smb_node_t *node)
7555772Sas200622 {
7568934SJose.Borrego@Sun.COM 	smb_ofile_t	*of;
7578934SJose.Borrego@Sun.COM 	uint32_t	status;
7585772Sas200622 
7598934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
7605772Sas200622 
76110001SJoyce.McIntosh@Sun.COM 	if (node->vp->v_type == VDIR)
7625772Sas200622 		return (NT_STATUS_SUCCESS);
7635772Sas200622 
7645772Sas200622 	/*
7655772Sas200622 	 * intra-CIFS check
7665772Sas200622 	 */
7675772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
7686771Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
7696771Sjb150015 	while (of) {
7706771Sjb150015 		status = smb_ofile_delete_check(of);
7716771Sjb150015 
7726771Sjb150015 		switch (status) {
7736771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7746771Sjb150015 		case NT_STATUS_SUCCESS:
7756771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7766771Sjb150015 			break;
7776771Sjb150015 		default:
7786771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
7795772Sas200622 			smb_llist_exit(&node->n_ofile_list);
7806771Sjb150015 			return (status);
7815772Sas200622 		}
7825772Sas200622 	}
7835772Sas200622 	smb_llist_exit(&node->n_ofile_list);
7845772Sas200622 
7855772Sas200622 	/*
7865772Sas200622 	 * system-wide share check
7875772Sas200622 	 */
7885772Sas200622 	if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL))
7895772Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
7905772Sas200622 	else
7915772Sas200622 		return (NT_STATUS_SUCCESS);
7925772Sas200622 }
7935772Sas200622 
7949914Samw@Sun.COM void
7959914Samw@Sun.COM smb_node_notify_change(smb_node_t *node)
7969914Samw@Sun.COM {
7979914Samw@Sun.COM 	SMB_NODE_VALID(node);
7989914Samw@Sun.COM 
7999914Samw@Sun.COM 	if (node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
8009914Samw@Sun.COM 		node->flags |= NODE_FLAGS_CHANGED;
8019914Samw@Sun.COM 		smb_process_node_notify_change_queue(node);
8029914Samw@Sun.COM 	}
8039914Samw@Sun.COM }
8049914Samw@Sun.COM 
8055772Sas200622 /*
8065772Sas200622  * smb_node_start_crit()
8075772Sas200622  *
8085772Sas200622  * Enter critical region for share reservations.
8095772Sas200622  * See comments above smb_fsop_shrlock().
8105772Sas200622  */
8115772Sas200622 
8125772Sas200622 void
8135772Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode)
8145772Sas200622 {
8158934SJose.Borrego@Sun.COM 	rw_enter(&node->n_lock, mode);
8165772Sas200622 	nbl_start_crit(node->vp, mode);
8175772Sas200622 }
8185772Sas200622 
8195772Sas200622 /*
8205772Sas200622  * smb_node_end_crit()
8215772Sas200622  *
8225772Sas200622  * Exit critical region for share reservations.
8235772Sas200622  */
8245772Sas200622 
8255772Sas200622 void
8265772Sas200622 smb_node_end_crit(smb_node_t *node)
8275772Sas200622 {
8285772Sas200622 	nbl_end_crit(node->vp);
8298934SJose.Borrego@Sun.COM 	rw_exit(&node->n_lock);
8305772Sas200622 }
8315772Sas200622 
8325772Sas200622 int
8335772Sas200622 smb_node_in_crit(smb_node_t *node)
8345772Sas200622 {
8358934SJose.Borrego@Sun.COM 	return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_lock));
8368934SJose.Borrego@Sun.COM }
8378934SJose.Borrego@Sun.COM 
8388934SJose.Borrego@Sun.COM void
8398934SJose.Borrego@Sun.COM smb_node_rdlock(smb_node_t *node)
8408934SJose.Borrego@Sun.COM {
8418934SJose.Borrego@Sun.COM 	rw_enter(&node->n_lock, RW_READER);
8428934SJose.Borrego@Sun.COM }
8438934SJose.Borrego@Sun.COM 
8448934SJose.Borrego@Sun.COM void
8458934SJose.Borrego@Sun.COM smb_node_wrlock(smb_node_t *node)
8468934SJose.Borrego@Sun.COM {
8478934SJose.Borrego@Sun.COM 	rw_enter(&node->n_lock, RW_WRITER);
8488934SJose.Borrego@Sun.COM }
8498934SJose.Borrego@Sun.COM 
8508934SJose.Borrego@Sun.COM void
8518934SJose.Borrego@Sun.COM smb_node_unlock(smb_node_t *node)
8528934SJose.Borrego@Sun.COM {
8538934SJose.Borrego@Sun.COM 	rw_exit(&node->n_lock);
8548934SJose.Borrego@Sun.COM }
8558934SJose.Borrego@Sun.COM 
8568934SJose.Borrego@Sun.COM uint32_t
8578934SJose.Borrego@Sun.COM smb_node_get_ofile_count(smb_node_t *node)
8588934SJose.Borrego@Sun.COM {
8598934SJose.Borrego@Sun.COM 	uint32_t	cntr;
8608934SJose.Borrego@Sun.COM 
8618934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
8628934SJose.Borrego@Sun.COM 
8638934SJose.Borrego@Sun.COM 	smb_llist_enter(&node->n_ofile_list, RW_READER);
8648934SJose.Borrego@Sun.COM 	cntr = smb_llist_get_count(&node->n_ofile_list);
8658934SJose.Borrego@Sun.COM 	smb_llist_exit(&node->n_ofile_list);
8668934SJose.Borrego@Sun.COM 	return (cntr);
8678934SJose.Borrego@Sun.COM }
8688934SJose.Borrego@Sun.COM 
8698934SJose.Borrego@Sun.COM void
8708934SJose.Borrego@Sun.COM smb_node_add_ofile(smb_node_t *node, smb_ofile_t *of)
8718934SJose.Borrego@Sun.COM {
8728934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
8738934SJose.Borrego@Sun.COM 
8748934SJose.Borrego@Sun.COM 	smb_llist_enter(&node->n_ofile_list, RW_WRITER);
8758934SJose.Borrego@Sun.COM 	smb_llist_insert_tail(&node->n_ofile_list, of);
8768934SJose.Borrego@Sun.COM 	smb_llist_exit(&node->n_ofile_list);
8778934SJose.Borrego@Sun.COM }
8788934SJose.Borrego@Sun.COM 
8798934SJose.Borrego@Sun.COM void
8808934SJose.Borrego@Sun.COM smb_node_rem_ofile(smb_node_t *node, smb_ofile_t *of)
8818934SJose.Borrego@Sun.COM {
8828934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
8838934SJose.Borrego@Sun.COM 
8848934SJose.Borrego@Sun.COM 	smb_llist_enter(&node->n_ofile_list, RW_WRITER);
8858934SJose.Borrego@Sun.COM 	smb_llist_remove(&node->n_ofile_list, of);
8868934SJose.Borrego@Sun.COM 	smb_llist_exit(&node->n_ofile_list);
8878934SJose.Borrego@Sun.COM }
8888934SJose.Borrego@Sun.COM 
88910001SJoyce.McIntosh@Sun.COM /*
89010001SJoyce.McIntosh@Sun.COM  * smb_node_inc_open_ofiles
89110001SJoyce.McIntosh@Sun.COM  */
8928934SJose.Borrego@Sun.COM void
8938934SJose.Borrego@Sun.COM smb_node_inc_open_ofiles(smb_node_t *node)
8948934SJose.Borrego@Sun.COM {
8958934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
8968934SJose.Borrego@Sun.COM 
8978934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
8988934SJose.Borrego@Sun.COM 	node->n_open_count++;
8998934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
90010001SJoyce.McIntosh@Sun.COM 
90110001SJoyce.McIntosh@Sun.COM 	smb_node_init_cached_timestamps(node);
9028934SJose.Borrego@Sun.COM }
9038934SJose.Borrego@Sun.COM 
90410001SJoyce.McIntosh@Sun.COM /*
90510001SJoyce.McIntosh@Sun.COM  * smb_node_dec_open_ofiles
90610001SJoyce.McIntosh@Sun.COM  */
9078934SJose.Borrego@Sun.COM void
9088934SJose.Borrego@Sun.COM smb_node_dec_open_ofiles(smb_node_t *node)
9098934SJose.Borrego@Sun.COM {
9108934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9118934SJose.Borrego@Sun.COM 
9128934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
9138934SJose.Borrego@Sun.COM 	node->n_open_count--;
9148934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
91510001SJoyce.McIntosh@Sun.COM 
91610001SJoyce.McIntosh@Sun.COM 	smb_node_clear_cached_timestamps(node);
9178934SJose.Borrego@Sun.COM }
9188934SJose.Borrego@Sun.COM 
9198934SJose.Borrego@Sun.COM uint32_t
9208934SJose.Borrego@Sun.COM smb_node_get_open_ofiles(smb_node_t *node)
9218934SJose.Borrego@Sun.COM {
9228934SJose.Borrego@Sun.COM 	uint32_t	cnt;
9238934SJose.Borrego@Sun.COM 
9248934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9258934SJose.Borrego@Sun.COM 
9268934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
9278934SJose.Borrego@Sun.COM 	cnt = node->n_open_count;
9288934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
9298934SJose.Borrego@Sun.COM 	return (cnt);
9305772Sas200622 }
9318934SJose.Borrego@Sun.COM 
9328934SJose.Borrego@Sun.COM /*
9338934SJose.Borrego@Sun.COM  * smb_node_alloc
9348934SJose.Borrego@Sun.COM  */
9358934SJose.Borrego@Sun.COM static smb_node_t *
9368934SJose.Borrego@Sun.COM smb_node_alloc(
9378934SJose.Borrego@Sun.COM     char	*od_name,
9388934SJose.Borrego@Sun.COM     vnode_t	*vp,
9398934SJose.Borrego@Sun.COM     smb_llist_t	*bucket,
9408934SJose.Borrego@Sun.COM     uint32_t	hashkey)
9418934SJose.Borrego@Sun.COM {
9428934SJose.Borrego@Sun.COM 	smb_node_t	*node;
9438934SJose.Borrego@Sun.COM 
9448934SJose.Borrego@Sun.COM 	node = kmem_cache_alloc(smb_node_cache, KM_SLEEP);
9458934SJose.Borrego@Sun.COM 
9468934SJose.Borrego@Sun.COM 	if (node->n_audit_buf != NULL)
9478934SJose.Borrego@Sun.COM 		node->n_audit_buf->anb_index = 0;
9488934SJose.Borrego@Sun.COM 
94910001SJoyce.McIntosh@Sun.COM 	node->flags = 0;
9508934SJose.Borrego@Sun.COM 	VN_HOLD(vp);
9518934SJose.Borrego@Sun.COM 	node->vp = vp;
9528934SJose.Borrego@Sun.COM 	node->n_refcnt = 1;
9538934SJose.Borrego@Sun.COM 	node->n_hash_bucket = bucket;
9548934SJose.Borrego@Sun.COM 	node->n_hashkey = hashkey;
9558934SJose.Borrego@Sun.COM 	node->n_orig_uid = 0;
9568934SJose.Borrego@Sun.COM 	node->readonly_creator = NULL;
9578934SJose.Borrego@Sun.COM 	node->waiting_event = 0;
9588934SJose.Borrego@Sun.COM 	node->n_open_count = 0;
959*10122SJordan.Brown@Sun.COM 	node->n_dnode = NULL;
960*10122SJordan.Brown@Sun.COM 	node->n_unode = NULL;
9618934SJose.Borrego@Sun.COM 	node->delete_on_close_cred = NULL;
9629231SAfshin.Ardakani@Sun.COM 	node->n_delete_on_close_flags = 0;
9638934SJose.Borrego@Sun.COM 
9648934SJose.Borrego@Sun.COM 	(void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
9658934SJose.Borrego@Sun.COM 	if (strcmp(od_name, XATTR_DIR) == 0)
9668934SJose.Borrego@Sun.COM 		node->flags |= NODE_XATTR_DIR;
9678934SJose.Borrego@Sun.COM 
9688934SJose.Borrego@Sun.COM 	node->n_state = SMB_NODE_STATE_AVAILABLE;
9698934SJose.Borrego@Sun.COM 	node->n_magic = SMB_NODE_MAGIC;
9708934SJose.Borrego@Sun.COM 	return (node);
9718934SJose.Borrego@Sun.COM }
9728934SJose.Borrego@Sun.COM 
9738934SJose.Borrego@Sun.COM /*
9748934SJose.Borrego@Sun.COM  * smb_node_free
9758934SJose.Borrego@Sun.COM  */
9768934SJose.Borrego@Sun.COM static void
9778934SJose.Borrego@Sun.COM smb_node_free(smb_node_t *node)
9788934SJose.Borrego@Sun.COM {
9798934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9808934SJose.Borrego@Sun.COM 
9818934SJose.Borrego@Sun.COM 	node->n_magic = 0;
9828934SJose.Borrego@Sun.COM 	VERIFY(!list_link_active(&node->n_lnd));
9838934SJose.Borrego@Sun.COM 	VERIFY(node->n_lock_list.ll_count == 0);
9848934SJose.Borrego@Sun.COM 	VERIFY(node->n_ofile_list.ll_count == 0);
9858934SJose.Borrego@Sun.COM 	VERIFY(node->n_oplock.ol_xthread == NULL);
9869021Samw@Sun.COM 	VERIFY(mutex_owner(&node->n_mutex) == NULL);
9879021Samw@Sun.COM 	VERIFY(!RW_LOCK_HELD(&node->n_lock));
9888934SJose.Borrego@Sun.COM 	VN_RELE(node->vp);
9898934SJose.Borrego@Sun.COM 	kmem_cache_free(smb_node_cache, node);
9908934SJose.Borrego@Sun.COM }
9918934SJose.Borrego@Sun.COM 
9928934SJose.Borrego@Sun.COM /*
9938934SJose.Borrego@Sun.COM  * smb_node_constructor
9948934SJose.Borrego@Sun.COM  */
9958934SJose.Borrego@Sun.COM static int
9968934SJose.Borrego@Sun.COM smb_node_constructor(void *buf, void *un, int kmflags)
9978934SJose.Borrego@Sun.COM {
9988934SJose.Borrego@Sun.COM 	_NOTE(ARGUNUSED(kmflags, un))
9998934SJose.Borrego@Sun.COM 
10008934SJose.Borrego@Sun.COM 	smb_node_t	*node = (smb_node_t *)buf;
10018934SJose.Borrego@Sun.COM 
10028934SJose.Borrego@Sun.COM 	bzero(node, sizeof (smb_node_t));
10038934SJose.Borrego@Sun.COM 
10048934SJose.Borrego@Sun.COM 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
10058934SJose.Borrego@Sun.COM 	    offsetof(smb_ofile_t, f_nnd));
10068934SJose.Borrego@Sun.COM 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
10078934SJose.Borrego@Sun.COM 	    offsetof(smb_lock_t, l_lnd));
10088934SJose.Borrego@Sun.COM 	cv_init(&node->n_oplock.ol_cv, NULL, CV_DEFAULT, NULL);
10098934SJose.Borrego@Sun.COM 	rw_init(&node->n_lock, NULL, RW_DEFAULT, NULL);
10108934SJose.Borrego@Sun.COM 	mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL);
10118934SJose.Borrego@Sun.COM 	smb_node_create_audit_buf(node, kmflags);
10128934SJose.Borrego@Sun.COM 	return (0);
10138934SJose.Borrego@Sun.COM }
10148934SJose.Borrego@Sun.COM 
10158934SJose.Borrego@Sun.COM /*
10168934SJose.Borrego@Sun.COM  * smb_node_destructor
10178934SJose.Borrego@Sun.COM  */
10188934SJose.Borrego@Sun.COM static void
10198934SJose.Borrego@Sun.COM smb_node_destructor(void *buf, void *un)
10208934SJose.Borrego@Sun.COM {
10218934SJose.Borrego@Sun.COM 	_NOTE(ARGUNUSED(un))
10228934SJose.Borrego@Sun.COM 
10238934SJose.Borrego@Sun.COM 	smb_node_t	*node = (smb_node_t *)buf;
10248934SJose.Borrego@Sun.COM 
10258934SJose.Borrego@Sun.COM 	smb_node_destroy_audit_buf(node);
10268934SJose.Borrego@Sun.COM 	mutex_destroy(&node->n_mutex);
10278934SJose.Borrego@Sun.COM 	rw_destroy(&node->n_lock);
10288934SJose.Borrego@Sun.COM 	cv_destroy(&node->n_oplock.ol_cv);
10298934SJose.Borrego@Sun.COM 	smb_llist_destructor(&node->n_lock_list);
10308934SJose.Borrego@Sun.COM 	smb_llist_destructor(&node->n_ofile_list);
10318934SJose.Borrego@Sun.COM }
10328934SJose.Borrego@Sun.COM 
10338934SJose.Borrego@Sun.COM /*
10348934SJose.Borrego@Sun.COM  * smb_node_create_audit_buf
10358934SJose.Borrego@Sun.COM  */
10368934SJose.Borrego@Sun.COM static void
10378934SJose.Borrego@Sun.COM smb_node_create_audit_buf(smb_node_t *node, int kmflags)
10388934SJose.Borrego@Sun.COM {
10398934SJose.Borrego@Sun.COM 	smb_audit_buf_node_t	*abn;
10408934SJose.Borrego@Sun.COM 
10418934SJose.Borrego@Sun.COM 	if (smb_audit_flags & SMB_AUDIT_NODE) {
10428934SJose.Borrego@Sun.COM 		abn = kmem_zalloc(sizeof (smb_audit_buf_node_t), kmflags);
10438934SJose.Borrego@Sun.COM 		abn->anb_max_index = SMB_AUDIT_BUF_MAX_REC - 1;
10448934SJose.Borrego@Sun.COM 		node->n_audit_buf = abn;
10458934SJose.Borrego@Sun.COM 	}
10468934SJose.Borrego@Sun.COM }
10478934SJose.Borrego@Sun.COM 
10488934SJose.Borrego@Sun.COM /*
10498934SJose.Borrego@Sun.COM  * smb_node_destroy_audit_buf
10508934SJose.Borrego@Sun.COM  */
10518934SJose.Borrego@Sun.COM static void
10528934SJose.Borrego@Sun.COM smb_node_destroy_audit_buf(smb_node_t *node)
10538934SJose.Borrego@Sun.COM {
10548934SJose.Borrego@Sun.COM 	if (node->n_audit_buf != NULL) {
10558934SJose.Borrego@Sun.COM 		kmem_free(node->n_audit_buf, sizeof (smb_audit_buf_node_t));
10568934SJose.Borrego@Sun.COM 		node->n_audit_buf = NULL;
10578934SJose.Borrego@Sun.COM 	}
10588934SJose.Borrego@Sun.COM }
10598934SJose.Borrego@Sun.COM 
10608934SJose.Borrego@Sun.COM /*
10618934SJose.Borrego@Sun.COM  * smb_node_audit
10628934SJose.Borrego@Sun.COM  *
10638934SJose.Borrego@Sun.COM  * This function saves the calling stack in the audit buffer of the node passed
10648934SJose.Borrego@Sun.COM  * in.
10658934SJose.Borrego@Sun.COM  */
10668934SJose.Borrego@Sun.COM static void
10678934SJose.Borrego@Sun.COM smb_node_audit(smb_node_t *node)
10688934SJose.Borrego@Sun.COM {
10698934SJose.Borrego@Sun.COM 	smb_audit_buf_node_t	*abn;
10708934SJose.Borrego@Sun.COM 	smb_audit_record_node_t	*anr;
10718934SJose.Borrego@Sun.COM 
10728934SJose.Borrego@Sun.COM 	if (node->n_audit_buf) {
10738934SJose.Borrego@Sun.COM 		abn = node->n_audit_buf;
10748934SJose.Borrego@Sun.COM 		anr = abn->anb_records;
10758934SJose.Borrego@Sun.COM 		anr += abn->anb_index;
10768934SJose.Borrego@Sun.COM 		abn->anb_index++;
10778934SJose.Borrego@Sun.COM 		abn->anb_index &= abn->anb_max_index;
10788934SJose.Borrego@Sun.COM 		anr->anr_refcnt = node->n_refcnt;
10798934SJose.Borrego@Sun.COM 		anr->anr_depth = getpcstack(anr->anr_stack,
10808934SJose.Borrego@Sun.COM 		    SMB_AUDIT_STACK_DEPTH);
10818934SJose.Borrego@Sun.COM 	}
10828934SJose.Borrego@Sun.COM }
10838934SJose.Borrego@Sun.COM 
10848934SJose.Borrego@Sun.COM static smb_llist_t *
10858934SJose.Borrego@Sun.COM smb_node_get_hash(fsid_t *fsid, smb_attr_t *attr, uint32_t *phashkey)
10868934SJose.Borrego@Sun.COM {
10878934SJose.Borrego@Sun.COM 	uint32_t	hashkey;
10888934SJose.Borrego@Sun.COM 
10898934SJose.Borrego@Sun.COM 	hashkey = fsid->val[0] + attr->sa_vattr.va_nodeid;
10908934SJose.Borrego@Sun.COM 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
10918934SJose.Borrego@Sun.COM 	*phashkey = hashkey;
10928934SJose.Borrego@Sun.COM 	return (&smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]);
10938934SJose.Borrego@Sun.COM }
109410001SJoyce.McIntosh@Sun.COM 
109510001SJoyce.McIntosh@Sun.COM boolean_t
109610001SJoyce.McIntosh@Sun.COM smb_node_is_dir(smb_node_t *node)
109710001SJoyce.McIntosh@Sun.COM {
109810001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
109910001SJoyce.McIntosh@Sun.COM 	return (node->vp->v_type == VDIR);
110010001SJoyce.McIntosh@Sun.COM }
110110001SJoyce.McIntosh@Sun.COM 
110210001SJoyce.McIntosh@Sun.COM boolean_t
110310001SJoyce.McIntosh@Sun.COM smb_node_is_link(smb_node_t *node)
110410001SJoyce.McIntosh@Sun.COM {
110510001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
110610001SJoyce.McIntosh@Sun.COM 	return (node->vp->v_type == VLNK);
110710001SJoyce.McIntosh@Sun.COM }
110810001SJoyce.McIntosh@Sun.COM 
110910001SJoyce.McIntosh@Sun.COM /*
111010001SJoyce.McIntosh@Sun.COM  * smb_node_file_is_readonly
111110001SJoyce.McIntosh@Sun.COM  *
111210001SJoyce.McIntosh@Sun.COM  * Checks if the file (which node represents) is marked readonly
111310001SJoyce.McIntosh@Sun.COM  * in the filesystem. No account is taken of any pending readonly
111410001SJoyce.McIntosh@Sun.COM  * in the node, which must be handled by the callers.
111510001SJoyce.McIntosh@Sun.COM  * (See SMB_OFILE_IS_READONLY and SMB_PATHFILE_IS_READONLY)
111610001SJoyce.McIntosh@Sun.COM  */
111710001SJoyce.McIntosh@Sun.COM boolean_t
111810001SJoyce.McIntosh@Sun.COM smb_node_file_is_readonly(smb_node_t *node)
111910001SJoyce.McIntosh@Sun.COM {
112010001SJoyce.McIntosh@Sun.COM 	smb_attr_t attr;
112110001SJoyce.McIntosh@Sun.COM 
112210001SJoyce.McIntosh@Sun.COM 	if (node == NULL)
112310001SJoyce.McIntosh@Sun.COM 		return (B_FALSE);
112410001SJoyce.McIntosh@Sun.COM 
112510001SJoyce.McIntosh@Sun.COM 	bzero(&attr, sizeof (smb_attr_t));
112610001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_DOSATTR;
112710001SJoyce.McIntosh@Sun.COM 	(void) smb_fsop_getattr(NULL, kcred, node, &attr);
112810001SJoyce.McIntosh@Sun.COM 	return ((attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) != 0);
112910001SJoyce.McIntosh@Sun.COM }
113010001SJoyce.McIntosh@Sun.COM 
113110001SJoyce.McIntosh@Sun.COM /*
113210001SJoyce.McIntosh@Sun.COM  * smb_node_setattr
113310001SJoyce.McIntosh@Sun.COM  *
113410001SJoyce.McIntosh@Sun.COM  * The sr may be NULL, for example when closing an ofile.
113510001SJoyce.McIntosh@Sun.COM  * The ofile may be NULL, for example when a client request
113610001SJoyce.McIntosh@Sun.COM  * specifies the file by pathname.
113710001SJoyce.McIntosh@Sun.COM  *
113810001SJoyce.McIntosh@Sun.COM  * When attributes are set on an ofile, any pending timestamps
113910001SJoyce.McIntosh@Sun.COM  * from a write request on the ofile are implicitly set to "now".
114010001SJoyce.McIntosh@Sun.COM  * For compatibility with windows the following timestamps are
114110001SJoyce.McIntosh@Sun.COM  * also implicitly set to now:
114210001SJoyce.McIntosh@Sun.COM  * - if any attribute is being explicitly set, set ctime to now
114310001SJoyce.McIntosh@Sun.COM  * - if file size is being explicitly set, set atime & ctime to now
114410001SJoyce.McIntosh@Sun.COM  *
114510001SJoyce.McIntosh@Sun.COM  * Any attribute that is being explicitly set, or has previously
114610001SJoyce.McIntosh@Sun.COM  * been explicitly set on the ofile, is excluded from implicit
114710001SJoyce.McIntosh@Sun.COM  * (now) setting.
114810001SJoyce.McIntosh@Sun.COM  *
114910001SJoyce.McIntosh@Sun.COM  * Updates the node's cached timestamp values.
115010001SJoyce.McIntosh@Sun.COM  * Updates the ofile's explicit times flag.
115110001SJoyce.McIntosh@Sun.COM  *
115210001SJoyce.McIntosh@Sun.COM  * Returns: errno
115310001SJoyce.McIntosh@Sun.COM  */
115410001SJoyce.McIntosh@Sun.COM int
115510001SJoyce.McIntosh@Sun.COM smb_node_setattr(smb_request_t *sr, smb_node_t *node,
115610001SJoyce.McIntosh@Sun.COM     cred_t *cr, smb_ofile_t *of, smb_attr_t *attr)
115710001SJoyce.McIntosh@Sun.COM {
115810001SJoyce.McIntosh@Sun.COM 	int rc;
115910001SJoyce.McIntosh@Sun.COM 	uint32_t what;
116010001SJoyce.McIntosh@Sun.COM 	uint32_t now_times = 0;
116110001SJoyce.McIntosh@Sun.COM 	timestruc_t now;
116210001SJoyce.McIntosh@Sun.COM 
116310001SJoyce.McIntosh@Sun.COM 	ASSERT(attr);
116410001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
116510001SJoyce.McIntosh@Sun.COM 
116610001SJoyce.McIntosh@Sun.COM 	what = attr->sa_mask;
116710001SJoyce.McIntosh@Sun.COM 
116810001SJoyce.McIntosh@Sun.COM 	/* determine which timestamps to implicitly set to "now" */
116910001SJoyce.McIntosh@Sun.COM 	if (what)
117010001SJoyce.McIntosh@Sun.COM 		now_times |= SMB_AT_CTIME;
117110001SJoyce.McIntosh@Sun.COM 	if (what & SMB_AT_SIZE)
117210001SJoyce.McIntosh@Sun.COM 		now_times |= (SMB_AT_MTIME | SMB_AT_CTIME);
117310001SJoyce.McIntosh@Sun.COM 	if (of) {
117410001SJoyce.McIntosh@Sun.COM 		if (smb_ofile_write_time_pending(of))
117510001SJoyce.McIntosh@Sun.COM 			now_times |=
117610001SJoyce.McIntosh@Sun.COM 			    (SMB_AT_MTIME | SMB_AT_CTIME | SMB_AT_ATIME);
117710001SJoyce.McIntosh@Sun.COM 		now_times &= ~(smb_ofile_explicit_times(of));
117810001SJoyce.McIntosh@Sun.COM 	}
117910001SJoyce.McIntosh@Sun.COM 	now_times &= ~what;
118010001SJoyce.McIntosh@Sun.COM 
118110001SJoyce.McIntosh@Sun.COM 	if (now_times) {
118210001SJoyce.McIntosh@Sun.COM 		gethrestime(&now);
118310001SJoyce.McIntosh@Sun.COM 
118410001SJoyce.McIntosh@Sun.COM 		if (now_times & SMB_AT_ATIME) {
118510001SJoyce.McIntosh@Sun.COM 			attr->sa_vattr.va_atime = now;
118610001SJoyce.McIntosh@Sun.COM 			attr->sa_mask |= SMB_AT_ATIME;
118710001SJoyce.McIntosh@Sun.COM 		}
118810001SJoyce.McIntosh@Sun.COM 		if (now_times & SMB_AT_MTIME) {
118910001SJoyce.McIntosh@Sun.COM 			attr->sa_vattr.va_mtime = now;
119010001SJoyce.McIntosh@Sun.COM 			attr->sa_mask |= SMB_AT_MTIME;
119110001SJoyce.McIntosh@Sun.COM 		}
119210001SJoyce.McIntosh@Sun.COM 		if (now_times & SMB_AT_CTIME) {
119310001SJoyce.McIntosh@Sun.COM 			attr->sa_vattr.va_ctime = now;
119410001SJoyce.McIntosh@Sun.COM 			attr->sa_mask |= SMB_AT_CTIME;
119510001SJoyce.McIntosh@Sun.COM 		}
119610001SJoyce.McIntosh@Sun.COM 	}
119710001SJoyce.McIntosh@Sun.COM 
119810001SJoyce.McIntosh@Sun.COM 	if (attr->sa_mask == 0)
119910001SJoyce.McIntosh@Sun.COM 		return (0);
120010001SJoyce.McIntosh@Sun.COM 
120110001SJoyce.McIntosh@Sun.COM 	rc = smb_fsop_setattr(sr, cr, node, attr);
120210001SJoyce.McIntosh@Sun.COM 	if (rc != 0)
120310001SJoyce.McIntosh@Sun.COM 		return (rc);
120410001SJoyce.McIntosh@Sun.COM 
120510001SJoyce.McIntosh@Sun.COM 	smb_node_set_cached_timestamps(node, attr);
120610001SJoyce.McIntosh@Sun.COM 
120710001SJoyce.McIntosh@Sun.COM 	if (of)
120810001SJoyce.McIntosh@Sun.COM 		smb_ofile_set_explicit_times(of, (what & SMB_AT_TIMES));
120910001SJoyce.McIntosh@Sun.COM 
121010001SJoyce.McIntosh@Sun.COM 	return (0);
121110001SJoyce.McIntosh@Sun.COM }
121210001SJoyce.McIntosh@Sun.COM 
121310001SJoyce.McIntosh@Sun.COM /*
121410001SJoyce.McIntosh@Sun.COM  * smb_node_getattr
121510001SJoyce.McIntosh@Sun.COM  *
121610001SJoyce.McIntosh@Sun.COM  * Get attributes from the file system and apply any smb-specific
121710001SJoyce.McIntosh@Sun.COM  * overrides for size, dos attributes and timestamps
121810001SJoyce.McIntosh@Sun.COM  *
121910001SJoyce.McIntosh@Sun.COM  * node->readonly_creator reflects whether a readonly set is pending
122010001SJoyce.McIntosh@Sun.COM  * from a readonly create. The readonly attribute should be visible to
122110001SJoyce.McIntosh@Sun.COM  * all clients even though the readonly creator fid is immune to the
122210001SJoyce.McIntosh@Sun.COM  * readonly bit until close.
122310001SJoyce.McIntosh@Sun.COM  *
122410001SJoyce.McIntosh@Sun.COM  * Returns: errno
122510001SJoyce.McIntosh@Sun.COM  */
122610001SJoyce.McIntosh@Sun.COM int
122710001SJoyce.McIntosh@Sun.COM smb_node_getattr(smb_request_t *sr, smb_node_t *node, smb_attr_t *attr)
122810001SJoyce.McIntosh@Sun.COM {
122910001SJoyce.McIntosh@Sun.COM 	int rc;
123010001SJoyce.McIntosh@Sun.COM 
123110001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
123210001SJoyce.McIntosh@Sun.COM 
123310001SJoyce.McIntosh@Sun.COM 	bzero(attr, sizeof (smb_attr_t));
123410001SJoyce.McIntosh@Sun.COM 	attr->sa_mask = SMB_AT_ALL;
123510001SJoyce.McIntosh@Sun.COM 	rc = smb_fsop_getattr(sr, kcred, node, attr);
123610001SJoyce.McIntosh@Sun.COM 	if (rc != 0)
123710001SJoyce.McIntosh@Sun.COM 		return (rc);
123810001SJoyce.McIntosh@Sun.COM 
123910001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
124010001SJoyce.McIntosh@Sun.COM 
124110001SJoyce.McIntosh@Sun.COM 	if (node->vp->v_type == VDIR)
124210001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_size = 0;
124310001SJoyce.McIntosh@Sun.COM 
124410001SJoyce.McIntosh@Sun.COM 	if (node->readonly_creator)
124510001SJoyce.McIntosh@Sun.COM 		attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY;
124610001SJoyce.McIntosh@Sun.COM 	if (attr->sa_dosattr == 0)
124710001SJoyce.McIntosh@Sun.COM 		attr->sa_dosattr = FILE_ATTRIBUTE_NORMAL;
124810001SJoyce.McIntosh@Sun.COM 
124910001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
125010001SJoyce.McIntosh@Sun.COM 
125110001SJoyce.McIntosh@Sun.COM 	smb_node_get_cached_timestamps(node, attr);
125210001SJoyce.McIntosh@Sun.COM 	return (0);
125310001SJoyce.McIntosh@Sun.COM }
125410001SJoyce.McIntosh@Sun.COM 
125510001SJoyce.McIntosh@Sun.COM /*
125610001SJoyce.McIntosh@Sun.COM  * Timestamp caching
125710001SJoyce.McIntosh@Sun.COM  *
125810001SJoyce.McIntosh@Sun.COM  * Solaris file systems handle timestamps different from NTFS. For
125910001SJoyce.McIntosh@Sun.COM  * example when file data is written NTFS doesn't update the timestamps
126010001SJoyce.McIntosh@Sun.COM  * until the file is closed, and then only if they haven't been explicity
126110001SJoyce.McIntosh@Sun.COM  * set via a set attribute request. In order to provide a more similar
126210001SJoyce.McIntosh@Sun.COM  * view of an open file's timestamps, we cache the timestamps in the
126310001SJoyce.McIntosh@Sun.COM  * node and manipulate them in a manner more consistent with windows.
126410001SJoyce.McIntosh@Sun.COM  * (See handling of explicit times and pending timestamps from a write
126510001SJoyce.McIntosh@Sun.COM  * request in smb_node_getattr and smb_node_setattr above.)
126610001SJoyce.McIntosh@Sun.COM  * Timestamps remain cached while there are open ofiles for the node.
126710001SJoyce.McIntosh@Sun.COM  * This includes open ofiles for named streams.
126810001SJoyce.McIntosh@Sun.COM  * n_open_ofiles cannot be used as this doesn't include ofiles opened
126910001SJoyce.McIntosh@Sun.COM  * for the node's named streams. Thus n_timestamps contains a count
127010001SJoyce.McIntosh@Sun.COM  * of open ofiles (t_open_ofiles), including named streams' ofiles,
127110001SJoyce.McIntosh@Sun.COM  * to be used to control timestamp caching.
127210001SJoyce.McIntosh@Sun.COM  *
127310001SJoyce.McIntosh@Sun.COM  * If a node represents a named stream the associated unnamed streams
127410001SJoyce.McIntosh@Sun.COM  * cached timestamps are used instead.
127510001SJoyce.McIntosh@Sun.COM  */
127610001SJoyce.McIntosh@Sun.COM 
127710001SJoyce.McIntosh@Sun.COM /*
127810001SJoyce.McIntosh@Sun.COM  * smb_node_init_cached_timestamps
127910001SJoyce.McIntosh@Sun.COM  *
128010001SJoyce.McIntosh@Sun.COM  * Increment count of open ofiles which are using the cached timestamps.
128110001SJoyce.McIntosh@Sun.COM  * If this is the first open ofile, init the cached timestamps from the
128210001SJoyce.McIntosh@Sun.COM  * file system values.
128310001SJoyce.McIntosh@Sun.COM  */
128410001SJoyce.McIntosh@Sun.COM static void
128510001SJoyce.McIntosh@Sun.COM smb_node_init_cached_timestamps(smb_node_t *node)
128610001SJoyce.McIntosh@Sun.COM {
128710001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
128810001SJoyce.McIntosh@Sun.COM 	smb_attr_t attr;
128910001SJoyce.McIntosh@Sun.COM 
129010001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
129110001SJoyce.McIntosh@Sun.COM 		node = unode;
129210001SJoyce.McIntosh@Sun.COM 
129310001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
129410001SJoyce.McIntosh@Sun.COM 	++(node->n_timestamps.t_open_ofiles);
129510001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_open_ofiles == 1) {
129610001SJoyce.McIntosh@Sun.COM 		bzero(&attr, sizeof (smb_attr_t));
129710001SJoyce.McIntosh@Sun.COM 		attr.sa_mask = SMB_AT_TIMES;
129810001SJoyce.McIntosh@Sun.COM 		(void) smb_fsop_getattr(NULL, kcred, node, &attr);
129910001SJoyce.McIntosh@Sun.COM 		node->n_timestamps.t_mtime = attr.sa_vattr.va_mtime;
130010001SJoyce.McIntosh@Sun.COM 		node->n_timestamps.t_atime = attr.sa_vattr.va_atime;
130110001SJoyce.McIntosh@Sun.COM 		node->n_timestamps.t_ctime = attr.sa_vattr.va_ctime;
130210001SJoyce.McIntosh@Sun.COM 		node->n_timestamps.t_crtime = attr.sa_crtime;
130310001SJoyce.McIntosh@Sun.COM 		node->n_timestamps.t_cached = B_TRUE;
130410001SJoyce.McIntosh@Sun.COM 	}
130510001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
130610001SJoyce.McIntosh@Sun.COM }
130710001SJoyce.McIntosh@Sun.COM 
130810001SJoyce.McIntosh@Sun.COM /*
130910001SJoyce.McIntosh@Sun.COM  * smb_node_clear_cached_timestamps
131010001SJoyce.McIntosh@Sun.COM  *
131110001SJoyce.McIntosh@Sun.COM  * Decrement count of open ofiles using the cached timestamps.
131210001SJoyce.McIntosh@Sun.COM  * If the decremented count is zero, clear the cached timestamps.
131310001SJoyce.McIntosh@Sun.COM  */
131410001SJoyce.McIntosh@Sun.COM static void
131510001SJoyce.McIntosh@Sun.COM smb_node_clear_cached_timestamps(smb_node_t *node)
131610001SJoyce.McIntosh@Sun.COM {
131710001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
131810001SJoyce.McIntosh@Sun.COM 
131910001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
132010001SJoyce.McIntosh@Sun.COM 		node = unode;
132110001SJoyce.McIntosh@Sun.COM 
132210001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
132310001SJoyce.McIntosh@Sun.COM 	ASSERT(node->n_timestamps.t_open_ofiles > 0);
132410001SJoyce.McIntosh@Sun.COM 	--(node->n_timestamps.t_open_ofiles);
132510001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_open_ofiles == 0)
132610001SJoyce.McIntosh@Sun.COM 		bzero(&node->n_timestamps, sizeof (smb_times_t));
132710001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
132810001SJoyce.McIntosh@Sun.COM }
132910001SJoyce.McIntosh@Sun.COM 
133010001SJoyce.McIntosh@Sun.COM /*
133110001SJoyce.McIntosh@Sun.COM  * smb_node_get_cached_timestamps
133210001SJoyce.McIntosh@Sun.COM  *
133310001SJoyce.McIntosh@Sun.COM  * Overwrite timestamps in attr with those cached in node.
133410001SJoyce.McIntosh@Sun.COM  */
133510001SJoyce.McIntosh@Sun.COM static void
133610001SJoyce.McIntosh@Sun.COM smb_node_get_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
133710001SJoyce.McIntosh@Sun.COM {
133810001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
133910001SJoyce.McIntosh@Sun.COM 
134010001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
134110001SJoyce.McIntosh@Sun.COM 		node = unode;
134210001SJoyce.McIntosh@Sun.COM 
134310001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
134410001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_cached) {
134510001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_mtime = node->n_timestamps.t_mtime;
134610001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_atime = node->n_timestamps.t_atime;
134710001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_ctime = node->n_timestamps.t_ctime;
134810001SJoyce.McIntosh@Sun.COM 		attr->sa_crtime = node->n_timestamps.t_crtime;
134910001SJoyce.McIntosh@Sun.COM 	}
135010001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
135110001SJoyce.McIntosh@Sun.COM }
135210001SJoyce.McIntosh@Sun.COM 
135310001SJoyce.McIntosh@Sun.COM /*
135410001SJoyce.McIntosh@Sun.COM  * smb_node_set_cached_timestamps
135510001SJoyce.McIntosh@Sun.COM  *
135610001SJoyce.McIntosh@Sun.COM  * Update the node's cached timestamps with values from attr.
135710001SJoyce.McIntosh@Sun.COM  */
135810001SJoyce.McIntosh@Sun.COM static void
135910001SJoyce.McIntosh@Sun.COM smb_node_set_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
136010001SJoyce.McIntosh@Sun.COM {
136110001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
136210001SJoyce.McIntosh@Sun.COM 
136310001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
136410001SJoyce.McIntosh@Sun.COM 		node = unode;
136510001SJoyce.McIntosh@Sun.COM 
136610001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
136710001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_cached) {
136810001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_MTIME)
136910001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime;
137010001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_ATIME)
137110001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_atime = attr->sa_vattr.va_atime;
137210001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_CTIME)
137310001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime;
137410001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_CRTIME)
137510001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_crtime = attr->sa_crtime;
137610001SJoyce.McIntosh@Sun.COM 	}
137710001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
137810001SJoyce.McIntosh@Sun.COM }
1379