xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_node.c (revision 11963:061945695ce1)
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 /*
2211633SJoyce.McIntosh@Sun.COM  * Copyright 2010 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  */
13110966SJordan.Brown@Sun.COM #include <smbsrv/smb_kproto.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>
137*11963SAfshin.Ardakani@Sun.COM #include <fs/fs_reparse.h>
1385331Samw 
1398934SJose.Borrego@Sun.COM uint32_t smb_is_executable(char *);
1408934SJose.Borrego@Sun.COM static void smb_node_delete_on_close(smb_node_t *);
1418934SJose.Borrego@Sun.COM static void smb_node_create_audit_buf(smb_node_t *, int);
1428934SJose.Borrego@Sun.COM static void smb_node_destroy_audit_buf(smb_node_t *);
1438934SJose.Borrego@Sun.COM static void smb_node_audit(smb_node_t *);
14410001SJoyce.McIntosh@Sun.COM static smb_node_t *smb_node_alloc(char *, vnode_t *, smb_llist_t *, uint32_t);
1458934SJose.Borrego@Sun.COM static void smb_node_free(smb_node_t *);
1468934SJose.Borrego@Sun.COM static int smb_node_constructor(void *, void *, int);
1478934SJose.Borrego@Sun.COM static void smb_node_destructor(void *, void *);
1488934SJose.Borrego@Sun.COM static smb_llist_t *smb_node_get_hash(fsid_t *, smb_attr_t *, uint32_t *);
14910504SKeyur.Desai@Sun.COM 
15010504SKeyur.Desai@Sun.COM static void smb_node_init_cached_data(smb_node_t *);
15110504SKeyur.Desai@Sun.COM static void smb_node_clear_cached_data(smb_node_t *);
15210504SKeyur.Desai@Sun.COM 
15310504SKeyur.Desai@Sun.COM static void smb_node_init_cached_timestamps(smb_node_t *, smb_attr_t *);
15410001SJoyce.McIntosh@Sun.COM static void smb_node_clear_cached_timestamps(smb_node_t *);
15510001SJoyce.McIntosh@Sun.COM static void smb_node_get_cached_timestamps(smb_node_t *, smb_attr_t *);
15610001SJoyce.McIntosh@Sun.COM static void smb_node_set_cached_timestamps(smb_node_t *, smb_attr_t *);
1575331Samw 
15810504SKeyur.Desai@Sun.COM static void smb_node_init_cached_allocsz(smb_node_t *, smb_attr_t *);
15910504SKeyur.Desai@Sun.COM static void smb_node_clear_cached_allocsz(smb_node_t *);
16010504SKeyur.Desai@Sun.COM static void smb_node_get_cached_allocsz(smb_node_t *, smb_attr_t *);
16110504SKeyur.Desai@Sun.COM static void smb_node_set_cached_allocsz(smb_node_t *, smb_attr_t *);
162*11963SAfshin.Ardakani@Sun.COM static void smb_node_init_reparse(smb_node_t *, smb_attr_t *);
163*11963SAfshin.Ardakani@Sun.COM static void smb_node_init_system(smb_node_t *);
16410504SKeyur.Desai@Sun.COM 
1655331Samw #define	VALIDATE_DIR_NODE(_dir_, _node_) \
1665331Samw     ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \
1675331Samw     ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \
16810122SJordan.Brown@Sun.COM     ASSERT((_dir_)->n_dnode != (_node_));
1695331Samw 
17010504SKeyur.Desai@Sun.COM /* round sz to DEV_BSIZE block */
17110504SKeyur.Desai@Sun.COM #define	SMB_ALLOCSZ(sz)	(((sz) + DEV_BSIZE-1) & ~(DEV_BSIZE-1))
17210504SKeyur.Desai@Sun.COM 
1738934SJose.Borrego@Sun.COM static kmem_cache_t	*smb_node_cache = NULL;
1746139Sjb150015 static boolean_t	smb_node_initialized = B_FALSE;
1756139Sjb150015 static smb_llist_t	smb_node_hash_table[SMBND_HASH_MASK+1];
1766139Sjb150015 
1776139Sjb150015 /*
1786139Sjb150015  * smb_node_init
1796139Sjb150015  *
1806139Sjb150015  * Initialization of the SMB node layer.
1816139Sjb150015  *
1826139Sjb150015  * This function is not multi-thread safe. The caller must make sure only one
1836139Sjb150015  * thread makes the call.
1846139Sjb150015  */
1856139Sjb150015 int
1866139Sjb150015 smb_node_init(void)
1876139Sjb150015 {
1886139Sjb150015 	int	i;
1896139Sjb150015 
1906139Sjb150015 	if (smb_node_initialized)
1916139Sjb150015 		return (0);
1928934SJose.Borrego@Sun.COM 	smb_node_cache = kmem_cache_create(SMBSRV_KSTAT_NODE_CACHE,
1938934SJose.Borrego@Sun.COM 	    sizeof (smb_node_t), 8, smb_node_constructor, smb_node_destructor,
1948934SJose.Borrego@Sun.COM 	    NULL, NULL, NULL, 0);
1956139Sjb150015 
1966139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
1976139Sjb150015 		smb_llist_constructor(&smb_node_hash_table[i],
1986139Sjb150015 		    sizeof (smb_node_t), offsetof(smb_node_t, n_lnd));
1996139Sjb150015 	}
2006139Sjb150015 	smb_node_initialized = B_TRUE;
2016139Sjb150015 	return (0);
2026139Sjb150015 }
2036139Sjb150015 
2046139Sjb150015 /*
2056139Sjb150015  * smb_node_fini
2066139Sjb150015  *
2076139Sjb150015  * This function is not multi-thread safe. The caller must make sure only one
2086139Sjb150015  * thread makes the call.
2096139Sjb150015  */
2106139Sjb150015 void
2116139Sjb150015 smb_node_fini(void)
2126139Sjb150015 {
2136139Sjb150015 	int	i;
2146139Sjb150015 
2156139Sjb150015 	if (!smb_node_initialized)
2166139Sjb150015 		return;
2176139Sjb150015 
2186139Sjb150015 #ifdef DEBUG
2196139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
2206139Sjb150015 		smb_node_t	*node;
2216139Sjb150015 
2226139Sjb150015 		/*
2236139Sjb150015 		 * The following sequence is just intended for sanity check.
2246139Sjb150015 		 * This will have to be modified when the code goes into
2256139Sjb150015 		 * production.
2266139Sjb150015 		 *
2276139Sjb150015 		 * The SMB node hash table should be emtpy at this point. If the
2286139Sjb150015 		 * hash table is not empty a panic will be triggered.
2296139Sjb150015 		 *
2306139Sjb150015 		 * The reason why SMB nodes are still remaining in the hash
2316139Sjb150015 		 * table is problably due to a mismatch between calls to
2326139Sjb150015 		 * smb_node_lookup() and smb_node_release(). You must track that
2336139Sjb150015 		 * down.
2346139Sjb150015 		 */
2356139Sjb150015 		node = smb_llist_head(&smb_node_hash_table[i]);
2366139Sjb150015 		ASSERT(node == NULL);
2376139Sjb150015 	}
2386139Sjb150015 #endif
2396139Sjb150015 
2406139Sjb150015 	for (i = 0; i <= SMBND_HASH_MASK; i++) {
2416139Sjb150015 		smb_llist_destructor(&smb_node_hash_table[i]);
2426139Sjb150015 	}
2438934SJose.Borrego@Sun.COM 	kmem_cache_destroy(smb_node_cache);
2448934SJose.Borrego@Sun.COM 	smb_node_cache = NULL;
2456139Sjb150015 	smb_node_initialized = B_FALSE;
2466139Sjb150015 }
2476139Sjb150015 
2485331Samw /*
2495331Samw  * smb_node_lookup()
2505331Samw  *
2515331Samw  * NOTE: This routine should only be called by the file system interface layer,
2525331Samw  * and not by SMB.
2535331Samw  *
2545331Samw  * smb_node_lookup() is called upon successful lookup, mkdir, and create
2555331Samw  * (for both non-streams and streams).  In each of these cases, a held vnode is
2568670SJose.Borrego@Sun.COM  * passed into this routine.  If a new smb_node is created it will take its
2578670SJose.Borrego@Sun.COM  * own hold on the vnode.  The caller's hold therefore still belongs to, and
2588670SJose.Borrego@Sun.COM  * should be released by, the caller.
2595331Samw  *
2605331Samw  * A reference is taken on the smb_node whether found in the hash table
2615331Samw  * or newly created.
2625331Samw  *
2635331Samw  * If an smb_node needs to be created, a reference is also taken on the
26410122SJordan.Brown@Sun.COM  * dnode (if passed in).
2655331Samw  *
2665331Samw  * See smb_node_release() for details on the release of these references.
2675331Samw  */
2685331Samw 
2695331Samw /*ARGSUSED*/
2705331Samw smb_node_t *
2715331Samw smb_node_lookup(
2725331Samw     struct smb_request	*sr,
2735331Samw     struct open_param	*op,
2745331Samw     cred_t		*cred,
2755331Samw     vnode_t		*vp,
2765331Samw     char		*od_name,
27710122SJordan.Brown@Sun.COM     smb_node_t		*dnode,
27810122SJordan.Brown@Sun.COM     smb_node_t		*unode)
2795331Samw {
2805331Samw 	smb_llist_t		*node_hdr;
2815331Samw 	smb_node_t		*node;
28210001SJoyce.McIntosh@Sun.COM 	smb_attr_t		attr;
2835331Samw 	uint32_t		hashkey = 0;
2847348SJose.Borrego@Sun.COM 	fsid_t			fsid;
2855331Samw 	int			error;
2865331Samw 	krw_t			lock_mode;
2875331Samw 	vnode_t			*unnamed_vp = NULL;
2885331Samw 
2895331Samw 	/*
2905331Samw 	 * smb_vop_getattr() is called here instead of smb_fsop_getattr(),
2915331Samw 	 * because the node may not yet exist.  We also do not want to call
2925331Samw 	 * it with the list lock held.
2935331Samw 	 */
2945331Samw 
29510122SJordan.Brown@Sun.COM 	if (unode)
29610122SJordan.Brown@Sun.COM 		unnamed_vp = unode->vp;
2975331Samw 
2985331Samw 	/*
2995331Samw 	 * This getattr is performed on behalf of the server
3005331Samw 	 * that's why kcred is used not the user's cred
3015331Samw 	 */
30210001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_ALL;
30310001SJoyce.McIntosh@Sun.COM 	error = smb_vop_getattr(vp, unnamed_vp, &attr, 0, kcred);
3045331Samw 	if (error)
3055331Samw 		return (NULL);
3065331Samw 
3077348SJose.Borrego@Sun.COM 	if (sr && sr->tid_tree) {
3087348SJose.Borrego@Sun.COM 		/*
3097348SJose.Borrego@Sun.COM 		 * The fsid for a file is that of the tree, even
3107348SJose.Borrego@Sun.COM 		 * if the file resides in a different mountpoint
3117348SJose.Borrego@Sun.COM 		 * under the share.
3127348SJose.Borrego@Sun.COM 		 */
3137348SJose.Borrego@Sun.COM 		fsid = SMB_TREE_FSID(sr->tid_tree);
3145331Samw 	} else {
3157348SJose.Borrego@Sun.COM 		/*
3167348SJose.Borrego@Sun.COM 		 * This should be getting executed only for the
3177348SJose.Borrego@Sun.COM 		 * tree root smb_node.
3187348SJose.Borrego@Sun.COM 		 */
3197348SJose.Borrego@Sun.COM 		fsid = vp->v_vfsp->vfs_fsid;
3205331Samw 	}
3215331Samw 
32210001SJoyce.McIntosh@Sun.COM 	node_hdr = smb_node_get_hash(&fsid, &attr, &hashkey);
3235331Samw 	lock_mode = RW_READER;
3245331Samw 
3255331Samw 	smb_llist_enter(node_hdr, lock_mode);
3265331Samw 	for (;;) {
3275331Samw 		node = list_head(&node_hdr->ll_list);
3285331Samw 		while (node) {
3295331Samw 			ASSERT(node->n_magic == SMB_NODE_MAGIC);
3305331Samw 			ASSERT(node->n_hash_bucket == node_hdr);
3315331Samw 			if ((node->n_hashkey == hashkey) && (node->vp == vp)) {
3328934SJose.Borrego@Sun.COM 				mutex_enter(&node->n_mutex);
3335331Samw 				DTRACE_PROBE1(smb_node_lookup_hit,
3345331Samw 				    smb_node_t *, node);
3355331Samw 				switch (node->n_state) {
3368934SJose.Borrego@Sun.COM 				case SMB_NODE_STATE_OPLOCK_GRANTED:
3378934SJose.Borrego@Sun.COM 				case SMB_NODE_STATE_OPLOCK_BREAKING:
3385331Samw 				case SMB_NODE_STATE_AVAILABLE:
3395331Samw 					/* The node was found. */
3405331Samw 					node->n_refcnt++;
34110122SJordan.Brown@Sun.COM 					if ((node->n_dnode == NULL) &&
34210122SJordan.Brown@Sun.COM 					    (dnode != NULL) &&
34311633SJoyce.McIntosh@Sun.COM 					    (node != dnode) &&
3445331Samw 					    (strcmp(od_name, "..") != 0) &&
3455331Samw 					    (strcmp(od_name, ".") != 0)) {
34610122SJordan.Brown@Sun.COM 						VALIDATE_DIR_NODE(dnode, node);
34710122SJordan.Brown@Sun.COM 						node->n_dnode = dnode;
34810122SJordan.Brown@Sun.COM 						smb_node_ref(dnode);
3495331Samw 					}
3505331Samw 
3518934SJose.Borrego@Sun.COM 					smb_node_audit(node);
3528934SJose.Borrego@Sun.COM 					mutex_exit(&node->n_mutex);
3535331Samw 					smb_llist_exit(node_hdr);
3545331Samw 					return (node);
3555331Samw 
3565331Samw 				case SMB_NODE_STATE_DESTROYING:
3575331Samw 					/*
3585331Samw 					 * Although the node exists it is about
3595331Samw 					 * to be destroyed. We act as it hasn't
3605331Samw 					 * been found.
3615331Samw 					 */
3628934SJose.Borrego@Sun.COM 					mutex_exit(&node->n_mutex);
3635331Samw 					break;
3645331Samw 				default:
3655331Samw 					/*
3665331Samw 					 * Although the node exists it is in an
3675331Samw 					 * unknown state. We act as it hasn't
3685331Samw 					 * been found.
3695331Samw 					 */
3705331Samw 					ASSERT(0);
3718934SJose.Borrego@Sun.COM 					mutex_exit(&node->n_mutex);
3725331Samw 					break;
3735331Samw 				}
3745331Samw 			}
3755331Samw 			node = smb_llist_next(node_hdr, node);
3765331Samw 		}
3775331Samw 		if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) {
3785331Samw 			lock_mode = RW_WRITER;
3795331Samw 			continue;
3805331Samw 		}
3815331Samw 		break;
3825331Samw 	}
38310001SJoyce.McIntosh@Sun.COM 	node = smb_node_alloc(od_name, vp, node_hdr, hashkey);
384*11963SAfshin.Ardakani@Sun.COM 	smb_node_init_reparse(node, &attr);
3855331Samw 
3865331Samw 	if (op)
3879343SAfshin.Ardakani@Sun.COM 		node->flags |= smb_is_executable(op->fqi.fq_last_comp);
3885331Samw 
38910122SJordan.Brown@Sun.COM 	if (dnode) {
39010122SJordan.Brown@Sun.COM 		smb_node_ref(dnode);
39110122SJordan.Brown@Sun.COM 		node->n_dnode = dnode;
39210122SJordan.Brown@Sun.COM 		ASSERT(dnode->n_dnode != node);
39310122SJordan.Brown@Sun.COM 		ASSERT((dnode->vp->v_xattrdir) ||
39410122SJordan.Brown@Sun.COM 		    (dnode->vp->v_type == VDIR));
3955331Samw 	}
3965331Samw 
39710122SJordan.Brown@Sun.COM 	if (unode) {
39810122SJordan.Brown@Sun.COM 		smb_node_ref(unode);
39910122SJordan.Brown@Sun.COM 		node->n_unode = unode;
4005331Samw 	}
4015331Samw 
402*11963SAfshin.Ardakani@Sun.COM 	smb_node_init_system(node);
403*11963SAfshin.Ardakani@Sun.COM 
4045331Samw 	DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node);
4058934SJose.Borrego@Sun.COM 	smb_node_audit(node);
4065331Samw 	smb_llist_insert_head(node_hdr, node);
4075331Samw 	smb_llist_exit(node_hdr);
4085331Samw 	return (node);
4095331Samw }
4105331Samw 
4115331Samw /*
4125331Samw  * smb_stream_node_lookup()
4135331Samw  *
4145331Samw  * Note: stream_name (the name that will be stored in the "od_name" field
4155331Samw  * of a stream's smb_node) is the same as the on-disk name for the stream
4165331Samw  * except that it does not have SMB_STREAM_PREFIX prepended.
4175331Samw  */
4185331Samw 
4195331Samw smb_node_t *
4208934SJose.Borrego@Sun.COM smb_stream_node_lookup(smb_request_t *sr, cred_t *cr, smb_node_t *fnode,
42110001SJoyce.McIntosh@Sun.COM     vnode_t *xattrdirvp, vnode_t *vp, char *stream_name)
4225331Samw {
4235331Samw 	smb_node_t	*xattrdir_node;
4245331Samw 	smb_node_t	*snode;
4255331Samw 
4265331Samw 	xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR,
42710001SJoyce.McIntosh@Sun.COM 	    fnode, NULL);
4285331Samw 
4295331Samw 	if (xattrdir_node == NULL)
4305331Samw 		return (NULL);
4315331Samw 
4325331Samw 	snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node,
43310001SJoyce.McIntosh@Sun.COM 	    fnode);
4345331Samw 
4355331Samw 	(void) smb_node_release(xattrdir_node);
4365331Samw 	return (snode);
4375331Samw }
4385331Samw 
4395331Samw 
4405331Samw /*
4415331Samw  * This function should be called whenever a reference is needed on an
4425331Samw  * smb_node pointer.  The copy of an smb_node pointer from one non-local
4435331Samw  * data structure to another requires a reference to be taken on the smb_node
4445331Samw  * (unless the usage is localized).  Each data structure deallocation routine
4455331Samw  * will call smb_node_release() on its smb_node pointers.
4465331Samw  *
4475331Samw  * In general, an smb_node pointer residing in a structure should never be
4485331Samw  * stale.  A node pointer may be NULL, however, and care should be taken
4495331Samw  * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid.
4505331Samw  * Care also needs to be taken with respect to racing deallocations of a
4515331Samw  * structure.
4525331Samw  */
4535331Samw void
4545331Samw smb_node_ref(smb_node_t *node)
4555331Samw {
4568934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
4575331Samw 
4588934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
4598934SJose.Borrego@Sun.COM 	switch (node->n_state) {
4608934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_AVAILABLE:
4618934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_GRANTED:
4628934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_BREAKING:
4638934SJose.Borrego@Sun.COM 		node->n_refcnt++;
4648934SJose.Borrego@Sun.COM 		ASSERT(node->n_refcnt);
4658934SJose.Borrego@Sun.COM 		DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node);
4668934SJose.Borrego@Sun.COM 		smb_node_audit(node);
4678934SJose.Borrego@Sun.COM 		break;
4688934SJose.Borrego@Sun.COM 	default:
4698934SJose.Borrego@Sun.COM 		SMB_PANIC();
4708934SJose.Borrego@Sun.COM 	}
4718934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
4725331Samw }
4735331Samw 
4745331Samw /*
4755331Samw  * smb_node_lookup() takes a hold on an smb_node, whether found in the
4765331Samw  * hash table or newly created.  This hold is expected to be released
4775331Samw  * in the following manner.
4785331Samw  *
4795331Samw  * smb_node_lookup() takes an address of an smb_node pointer.  This should
4805331Samw  * be getting passed down via a lookup (whether path name or component), mkdir,
4815331Samw  * create.  If the original smb_node pointer resides in a data structure, then
4825331Samw  * the deallocation routine for the data structure is responsible for calling
4835331Samw  * smb_node_release() on the smb_node pointer.  Alternatively,
4845331Samw  * smb_node_release() can be called as soon as the smb_node pointer is no longer
4855331Samw  * needed.  In this case, callers are responsible for setting an embedded
4865331Samw  * pointer to NULL if it is known that the last reference is being released.
4875331Samw  *
4885331Samw  * If the passed-in address of the smb_node pointer belongs to a local variable,
4895331Samw  * then the caller with the local variable should call smb_node_release()
4905331Samw  * directly.
4915331Samw  *
49210122SJordan.Brown@Sun.COM  * smb_node_release() itself will call smb_node_release() on a node's n_dnode,
49310122SJordan.Brown@Sun.COM  * as smb_node_lookup() takes a hold on dnode.
4945331Samw  */
4955331Samw void
4965331Samw smb_node_release(smb_node_t *node)
4975331Samw {
4988934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
4995331Samw 
5008934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
5015331Samw 	ASSERT(node->n_refcnt);
5025331Samw 	DTRACE_PROBE1(smb_node_release, smb_node_t *, node);
5035331Samw 	if (--node->n_refcnt == 0) {
5045331Samw 		switch (node->n_state) {
5055331Samw 
5065331Samw 		case SMB_NODE_STATE_AVAILABLE:
5075331Samw 			node->n_state = SMB_NODE_STATE_DESTROYING;
5088934SJose.Borrego@Sun.COM 			mutex_exit(&node->n_mutex);
5095331Samw 
5105331Samw 			smb_llist_enter(node->n_hash_bucket, RW_WRITER);
5115331Samw 			smb_llist_remove(node->n_hash_bucket, node);
5125331Samw 			smb_llist_exit(node->n_hash_bucket);
5135331Samw 
5145331Samw 			/*
5155331Samw 			 * Check if the file was deleted
5165331Samw 			 */
5175331Samw 			smb_node_delete_on_close(node);
5185331Samw 
51910122SJordan.Brown@Sun.COM 			if (node->n_dnode) {
52010122SJordan.Brown@Sun.COM 				ASSERT(node->n_dnode->n_magic ==
5215331Samw 				    SMB_NODE_MAGIC);
52210122SJordan.Brown@Sun.COM 				smb_node_release(node->n_dnode);
5235331Samw 			}
5245331Samw 
52510122SJordan.Brown@Sun.COM 			if (node->n_unode) {
52610122SJordan.Brown@Sun.COM 				ASSERT(node->n_unode->n_magic ==
5275331Samw 				    SMB_NODE_MAGIC);
52810122SJordan.Brown@Sun.COM 				smb_node_release(node->n_unode);
5295331Samw 			}
5305331Samw 
5318934SJose.Borrego@Sun.COM 			smb_node_free(node);
5325331Samw 			return;
5335331Samw 
5345331Samw 		default:
5358934SJose.Borrego@Sun.COM 			SMB_PANIC();
5365331Samw 		}
5375331Samw 	}
5388934SJose.Borrego@Sun.COM 	smb_node_audit(node);
5398934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
5405331Samw }
5415331Samw 
5425331Samw static void
5435331Samw smb_node_delete_on_close(smb_node_t *node)
5445331Samw {
5455331Samw 	smb_node_t	*d_snode;
5465331Samw 	int		rc = 0;
5479231SAfshin.Ardakani@Sun.COM 	uint32_t	flags = 0;
5485331Samw 
54910122SJordan.Brown@Sun.COM 	d_snode = node->n_dnode;
5505331Samw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
5519231SAfshin.Ardakani@Sun.COM 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
5529231SAfshin.Ardakani@Sun.COM 		flags = node->n_delete_on_close_flags;
5539231SAfshin.Ardakani@Sun.COM 		ASSERT(node->od_name != NULL);
5545331Samw 
555*11963SAfshin.Ardakani@Sun.COM 		if (smb_node_is_dir(node))
5565331Samw 			rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
5579231SAfshin.Ardakani@Sun.COM 			    d_snode, node->od_name, flags);
5585331Samw 		else
5595331Samw 			rc = smb_fsop_remove(0, node->delete_on_close_cred,
5609231SAfshin.Ardakani@Sun.COM 			    d_snode, node->od_name, flags);
5615331Samw 		smb_cred_rele(node->delete_on_close_cred);
5625331Samw 	}
5635331Samw 	if (rc != 0)
5645331Samw 		cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
5655331Samw 		    node->od_name, rc);
5665331Samw 	DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
5675331Samw }
5685331Samw 
5695331Samw /*
5705331Samw  * smb_node_rename()
5715331Samw  *
5725331Samw  */
5738934SJose.Borrego@Sun.COM void
5745331Samw smb_node_rename(
5758934SJose.Borrego@Sun.COM     smb_node_t	*from_dnode,
5768934SJose.Borrego@Sun.COM     smb_node_t	*ret_node,
5778934SJose.Borrego@Sun.COM     smb_node_t	*to_dnode,
5785331Samw     char	*to_name)
5795331Samw {
5808934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(from_dnode);
5818934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(to_dnode);
5828934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(ret_node);
5835331Samw 
5848934SJose.Borrego@Sun.COM 	smb_node_ref(to_dnode);
5858934SJose.Borrego@Sun.COM 	mutex_enter(&ret_node->n_mutex);
5868934SJose.Borrego@Sun.COM 	switch (ret_node->n_state) {
5878934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_AVAILABLE:
5888934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_GRANTED:
5898934SJose.Borrego@Sun.COM 	case SMB_NODE_STATE_OPLOCK_BREAKING:
59010122SJordan.Brown@Sun.COM 		ret_node->n_dnode = to_dnode;
5918934SJose.Borrego@Sun.COM 		mutex_exit(&ret_node->n_mutex);
59210122SJordan.Brown@Sun.COM 		ASSERT(to_dnode->n_dnode != ret_node);
5938934SJose.Borrego@Sun.COM 		ASSERT((to_dnode->vp->v_xattrdir) ||
5948934SJose.Borrego@Sun.COM 		    (to_dnode->vp->v_type == VDIR));
5958934SJose.Borrego@Sun.COM 		smb_node_release(from_dnode);
5968934SJose.Borrego@Sun.COM 		(void) strcpy(ret_node->od_name, to_name);
5978934SJose.Borrego@Sun.COM 		/*
5988934SJose.Borrego@Sun.COM 		 * XXX Need to update attributes?
5998934SJose.Borrego@Sun.COM 		 */
6008934SJose.Borrego@Sun.COM 		break;
6018934SJose.Borrego@Sun.COM 	default:
6028934SJose.Borrego@Sun.COM 		SMB_PANIC();
6038934SJose.Borrego@Sun.COM 	}
6045331Samw }
6055331Samw 
6065331Samw int
6076139Sjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root)
6085331Samw {
60910001SJoyce.McIntosh@Sun.COM 	smb_attr_t	attr;
6106139Sjb150015 	int		error;
6116139Sjb150015 	uint32_t	hashkey;
6126139Sjb150015 	smb_llist_t	*node_hdr;
6136139Sjb150015 	smb_node_t	*node;
6145331Samw 
61510001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_ALL;
61610001SJoyce.McIntosh@Sun.COM 	error = smb_vop_getattr(vp, NULL, &attr, 0, kcred);
6176139Sjb150015 	if (error) {
6186139Sjb150015 		VN_RELE(vp);
6196139Sjb150015 		return (error);
6206139Sjb150015 	}
6216139Sjb150015 
62210001SJoyce.McIntosh@Sun.COM 	node_hdr = smb_node_get_hash(&vp->v_vfsp->vfs_fsid, &attr, &hashkey);
6235331Samw 
62410001SJoyce.McIntosh@Sun.COM 	node = smb_node_alloc(ROOTVOL, vp, node_hdr, hashkey);
6256139Sjb150015 
6266139Sjb150015 	sv->si_root_smb_node = node;
6278934SJose.Borrego@Sun.COM 	smb_node_audit(node);
6286139Sjb150015 	smb_llist_enter(node_hdr, RW_WRITER);
6296139Sjb150015 	smb_llist_insert_head(node_hdr, node);
6306139Sjb150015 	smb_llist_exit(node_hdr);
6316139Sjb150015 	*root = node;
6326139Sjb150015 	return (0);
6335331Samw }
6345331Samw 
6355331Samw /*
6369231SAfshin.Ardakani@Sun.COM  * When DeleteOnClose is set on an smb_node, the common open code will
6379231SAfshin.Ardakani@Sun.COM  * reject subsequent open requests for the file. Observation of Windows
6389231SAfshin.Ardakani@Sun.COM  * 2000 indicates that subsequent opens should be allowed (assuming
6399231SAfshin.Ardakani@Sun.COM  * there would be no sharing violation) until the file is closed using
6409231SAfshin.Ardakani@Sun.COM  * the fid on which the DeleteOnClose was requested.
6419231SAfshin.Ardakani@Sun.COM  *
6429231SAfshin.Ardakani@Sun.COM  * If there are multiple opens with delete-on-close create options,
6439231SAfshin.Ardakani@Sun.COM  * whichever the first file handle is closed will trigger the node to be
6449231SAfshin.Ardakani@Sun.COM  * marked as delete-on-close. The credentials of that ofile will be used
6459231SAfshin.Ardakani@Sun.COM  * as the delete-on-close credentials of the node.
6469231SAfshin.Ardakani@Sun.COM  */
6475331Samw int
6489231SAfshin.Ardakani@Sun.COM smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags)
6495331Samw {
65010717Samw@Sun.COM 	int rc = 0;
65110001SJoyce.McIntosh@Sun.COM 	smb_attr_t attr;
6525331Samw 
65310717Samw@Sun.COM 	if (node->readonly_creator)
65410001SJoyce.McIntosh@Sun.COM 		return (-1);
65510001SJoyce.McIntosh@Sun.COM 
65610001SJoyce.McIntosh@Sun.COM 	bzero(&attr, sizeof (smb_attr_t));
65710001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_DOSATTR;
65810001SJoyce.McIntosh@Sun.COM 	rc = smb_fsop_getattr(NULL, kcred, node, &attr);
65910001SJoyce.McIntosh@Sun.COM 	if ((rc != 0) || (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) {
66010001SJoyce.McIntosh@Sun.COM 		return (-1);
66110001SJoyce.McIntosh@Sun.COM 	}
66210001SJoyce.McIntosh@Sun.COM 
66310717Samw@Sun.COM 	mutex_enter(&node->n_mutex);
66410717Samw@Sun.COM 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
66510717Samw@Sun.COM 		rc = -1;
66610717Samw@Sun.COM 	} else {
66710717Samw@Sun.COM 		crhold(cr);
66810717Samw@Sun.COM 		node->delete_on_close_cred = cr;
66910717Samw@Sun.COM 		node->n_delete_on_close_flags = flags;
67010717Samw@Sun.COM 		node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
67110717Samw@Sun.COM 		rc = 0;
67210717Samw@Sun.COM 	}
6738934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
67410717Samw@Sun.COM 	return (rc);
6755331Samw }
6765331Samw 
6775331Samw void
6785331Samw smb_node_reset_delete_on_close(smb_node_t *node)
6795331Samw {
6808934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
6815331Samw 	if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
6825331Samw 		node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
6835331Samw 		crfree(node->delete_on_close_cred);
6845331Samw 		node->delete_on_close_cred = NULL;
6859231SAfshin.Ardakani@Sun.COM 		node->n_delete_on_close_flags = 0;
6865331Samw 	}
6878934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
6885331Samw }
6895772Sas200622 
6905772Sas200622 /*
6916771Sjb150015  * smb_node_open_check
6925772Sas200622  *
6935772Sas200622  * check file sharing rules for current open request
6945772Sas200622  * against all existing opens for a file.
6955772Sas200622  *
6965772Sas200622  * Returns NT_STATUS_SHARING_VIOLATION if there is any
6975772Sas200622  * sharing conflict, otherwise returns NT_STATUS_SUCCESS.
6985772Sas200622  */
6995772Sas200622 uint32_t
700*11963SAfshin.Ardakani@Sun.COM smb_node_open_check(smb_node_t *node, uint32_t desired_access,
701*11963SAfshin.Ardakani@Sun.COM     uint32_t share_access)
7025772Sas200622 {
7035772Sas200622 	smb_ofile_t *of;
7045772Sas200622 	uint32_t status;
7055772Sas200622 
7068934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
7075772Sas200622 
7085772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
7095772Sas200622 	of = smb_llist_head(&node->n_ofile_list);
7105772Sas200622 	while (of) {
711*11963SAfshin.Ardakani@Sun.COM 		status = smb_ofile_open_check(of, desired_access, share_access);
7126771Sjb150015 
7136771Sjb150015 		switch (status) {
7146771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7156771Sjb150015 		case NT_STATUS_SUCCESS:
7166771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7176771Sjb150015 			break;
7186771Sjb150015 		default:
7196771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
7205772Sas200622 			smb_llist_exit(&node->n_ofile_list);
7215772Sas200622 			return (status);
7225772Sas200622 		}
7235772Sas200622 	}
7246771Sjb150015 
7255772Sas200622 	smb_llist_exit(&node->n_ofile_list);
7265772Sas200622 	return (NT_STATUS_SUCCESS);
7275772Sas200622 }
7285772Sas200622 
7295772Sas200622 uint32_t
7308934SJose.Borrego@Sun.COM smb_node_rename_check(smb_node_t *node)
7315772Sas200622 {
7328934SJose.Borrego@Sun.COM 	smb_ofile_t	*of;
7338934SJose.Borrego@Sun.COM 	uint32_t	status;
7345772Sas200622 
7358934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
7365772Sas200622 
7375772Sas200622 	/*
7385772Sas200622 	 * Intra-CIFS check
7395772Sas200622 	 */
7405772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
7416771Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
7426771Sjb150015 	while (of) {
7436771Sjb150015 		status = smb_ofile_rename_check(of);
7445772Sas200622 
7456771Sjb150015 		switch (status) {
7466771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7476771Sjb150015 		case NT_STATUS_SUCCESS:
7486771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7496771Sjb150015 			break;
7506771Sjb150015 		default:
7516771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
7526771Sjb150015 			smb_llist_exit(&node->n_ofile_list);
7536771Sjb150015 			return (status);
7545772Sas200622 		}
7555772Sas200622 	}
7565772Sas200622 	smb_llist_exit(&node->n_ofile_list);
7575772Sas200622 
7585772Sas200622 	/*
7595772Sas200622 	 * system-wide share check
7605772Sas200622 	 */
7615772Sas200622 	if (nbl_share_conflict(node->vp, NBL_RENAME, NULL))
7625772Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
7635772Sas200622 	else
7645772Sas200622 		return (NT_STATUS_SUCCESS);
7655772Sas200622 }
7665772Sas200622 
7676771Sjb150015 uint32_t
7685772Sas200622 smb_node_delete_check(smb_node_t *node)
7695772Sas200622 {
7708934SJose.Borrego@Sun.COM 	smb_ofile_t	*of;
7718934SJose.Borrego@Sun.COM 	uint32_t	status;
7725772Sas200622 
7738934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
7745772Sas200622 
775*11963SAfshin.Ardakani@Sun.COM 	if (smb_node_is_dir(node))
7765772Sas200622 		return (NT_STATUS_SUCCESS);
7775772Sas200622 
778*11963SAfshin.Ardakani@Sun.COM 	if (smb_node_is_reparse(node))
779*11963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_ACCESS_DENIED);
780*11963SAfshin.Ardakani@Sun.COM 
7815772Sas200622 	/*
7825772Sas200622 	 * intra-CIFS check
7835772Sas200622 	 */
7845772Sas200622 	smb_llist_enter(&node->n_ofile_list, RW_READER);
7856771Sjb150015 	of = smb_llist_head(&node->n_ofile_list);
7866771Sjb150015 	while (of) {
7876771Sjb150015 		status = smb_ofile_delete_check(of);
7886771Sjb150015 
7896771Sjb150015 		switch (status) {
7906771Sjb150015 		case NT_STATUS_INVALID_HANDLE:
7916771Sjb150015 		case NT_STATUS_SUCCESS:
7926771Sjb150015 			of = smb_llist_next(&node->n_ofile_list, of);
7936771Sjb150015 			break;
7946771Sjb150015 		default:
7956771Sjb150015 			ASSERT(status == NT_STATUS_SHARING_VIOLATION);
7965772Sas200622 			smb_llist_exit(&node->n_ofile_list);
7976771Sjb150015 			return (status);
7985772Sas200622 		}
7995772Sas200622 	}
8005772Sas200622 	smb_llist_exit(&node->n_ofile_list);
8015772Sas200622 
8025772Sas200622 	/*
8035772Sas200622 	 * system-wide share check
8045772Sas200622 	 */
8055772Sas200622 	if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL))
8065772Sas200622 		return (NT_STATUS_SHARING_VIOLATION);
8075772Sas200622 	else
8085772Sas200622 		return (NT_STATUS_SUCCESS);
8095772Sas200622 }
8105772Sas200622 
8119914Samw@Sun.COM void
8129914Samw@Sun.COM smb_node_notify_change(smb_node_t *node)
8139914Samw@Sun.COM {
8149914Samw@Sun.COM 	SMB_NODE_VALID(node);
8159914Samw@Sun.COM 
8169914Samw@Sun.COM 	if (node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
8179914Samw@Sun.COM 		node->flags |= NODE_FLAGS_CHANGED;
8189914Samw@Sun.COM 		smb_process_node_notify_change_queue(node);
8199914Samw@Sun.COM 	}
820*11963SAfshin.Ardakani@Sun.COM 
821*11963SAfshin.Ardakani@Sun.COM 	smb_node_notify_parents(node);
8229914Samw@Sun.COM }
8239914Samw@Sun.COM 
824*11963SAfshin.Ardakani@Sun.COM /*
825*11963SAfshin.Ardakani@Sun.COM  * smb_node_notify_parents
826*11963SAfshin.Ardakani@Sun.COM  *
827*11963SAfshin.Ardakani@Sun.COM  * Iterate up the directory tree notifying any parent
828*11963SAfshin.Ardakani@Sun.COM  * directories that are being watched for changes in
829*11963SAfshin.Ardakani@Sun.COM  * their sub directories.
830*11963SAfshin.Ardakani@Sun.COM  * Stop at the root node, which has a NULL parent node.
831*11963SAfshin.Ardakani@Sun.COM  */
832*11963SAfshin.Ardakani@Sun.COM void
833*11963SAfshin.Ardakani@Sun.COM smb_node_notify_parents(smb_node_t *dnode)
83411337SWilliam.Krier@Sun.COM {
835*11963SAfshin.Ardakani@Sun.COM 	smb_node_t *pnode = dnode;
836*11963SAfshin.Ardakani@Sun.COM 
837*11963SAfshin.Ardakani@Sun.COM 	SMB_NODE_VALID(dnode);
83811337SWilliam.Krier@Sun.COM 
839*11963SAfshin.Ardakani@Sun.COM 	while ((pnode = pnode->n_dnode) != NULL) {
840*11963SAfshin.Ardakani@Sun.COM 		SMB_NODE_VALID(pnode);
841*11963SAfshin.Ardakani@Sun.COM 		if ((pnode->flags & NODE_FLAGS_NOTIFY_CHANGE) &&
842*11963SAfshin.Ardakani@Sun.COM 		    (pnode->flags & NODE_FLAGS_WATCH_TREE)) {
843*11963SAfshin.Ardakani@Sun.COM 			pnode->flags |= NODE_FLAGS_CHANGED;
844*11963SAfshin.Ardakani@Sun.COM 			smb_process_node_notify_change_queue(pnode);
845*11963SAfshin.Ardakani@Sun.COM 		}
846*11963SAfshin.Ardakani@Sun.COM 	}
84711337SWilliam.Krier@Sun.COM }
84811337SWilliam.Krier@Sun.COM 
8495772Sas200622 /*
8505772Sas200622  * smb_node_start_crit()
8515772Sas200622  *
8525772Sas200622  * Enter critical region for share reservations.
8535772Sas200622  * See comments above smb_fsop_shrlock().
8545772Sas200622  */
8555772Sas200622 void
8565772Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode)
8575772Sas200622 {
8588934SJose.Borrego@Sun.COM 	rw_enter(&node->n_lock, mode);
8595772Sas200622 	nbl_start_crit(node->vp, mode);
8605772Sas200622 }
8615772Sas200622 
8625772Sas200622 /*
8635772Sas200622  * smb_node_end_crit()
8645772Sas200622  *
8655772Sas200622  * Exit critical region for share reservations.
8665772Sas200622  */
8675772Sas200622 void
8685772Sas200622 smb_node_end_crit(smb_node_t *node)
8695772Sas200622 {
8705772Sas200622 	nbl_end_crit(node->vp);
8718934SJose.Borrego@Sun.COM 	rw_exit(&node->n_lock);
8725772Sas200622 }
8735772Sas200622 
8745772Sas200622 int
8755772Sas200622 smb_node_in_crit(smb_node_t *node)
8765772Sas200622 {
8778934SJose.Borrego@Sun.COM 	return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_lock));
8788934SJose.Borrego@Sun.COM }
8798934SJose.Borrego@Sun.COM 
8808934SJose.Borrego@Sun.COM void
8818934SJose.Borrego@Sun.COM smb_node_rdlock(smb_node_t *node)
8828934SJose.Borrego@Sun.COM {
8838934SJose.Borrego@Sun.COM 	rw_enter(&node->n_lock, RW_READER);
8848934SJose.Borrego@Sun.COM }
8858934SJose.Borrego@Sun.COM 
8868934SJose.Borrego@Sun.COM void
8878934SJose.Borrego@Sun.COM smb_node_wrlock(smb_node_t *node)
8888934SJose.Borrego@Sun.COM {
8898934SJose.Borrego@Sun.COM 	rw_enter(&node->n_lock, RW_WRITER);
8908934SJose.Borrego@Sun.COM }
8918934SJose.Borrego@Sun.COM 
8928934SJose.Borrego@Sun.COM void
8938934SJose.Borrego@Sun.COM smb_node_unlock(smb_node_t *node)
8948934SJose.Borrego@Sun.COM {
8958934SJose.Borrego@Sun.COM 	rw_exit(&node->n_lock);
8968934SJose.Borrego@Sun.COM }
8978934SJose.Borrego@Sun.COM 
8988934SJose.Borrego@Sun.COM uint32_t
8998934SJose.Borrego@Sun.COM smb_node_get_ofile_count(smb_node_t *node)
9008934SJose.Borrego@Sun.COM {
9018934SJose.Borrego@Sun.COM 	uint32_t	cntr;
9028934SJose.Borrego@Sun.COM 
9038934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9048934SJose.Borrego@Sun.COM 
9058934SJose.Borrego@Sun.COM 	smb_llist_enter(&node->n_ofile_list, RW_READER);
9068934SJose.Borrego@Sun.COM 	cntr = smb_llist_get_count(&node->n_ofile_list);
9078934SJose.Borrego@Sun.COM 	smb_llist_exit(&node->n_ofile_list);
9088934SJose.Borrego@Sun.COM 	return (cntr);
9098934SJose.Borrego@Sun.COM }
9108934SJose.Borrego@Sun.COM 
9118934SJose.Borrego@Sun.COM void
9128934SJose.Borrego@Sun.COM smb_node_add_ofile(smb_node_t *node, smb_ofile_t *of)
9138934SJose.Borrego@Sun.COM {
9148934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9158934SJose.Borrego@Sun.COM 
9168934SJose.Borrego@Sun.COM 	smb_llist_enter(&node->n_ofile_list, RW_WRITER);
9178934SJose.Borrego@Sun.COM 	smb_llist_insert_tail(&node->n_ofile_list, of);
9188934SJose.Borrego@Sun.COM 	smb_llist_exit(&node->n_ofile_list);
9198934SJose.Borrego@Sun.COM }
9208934SJose.Borrego@Sun.COM 
9218934SJose.Borrego@Sun.COM void
9228934SJose.Borrego@Sun.COM smb_node_rem_ofile(smb_node_t *node, smb_ofile_t *of)
9238934SJose.Borrego@Sun.COM {
9248934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9258934SJose.Borrego@Sun.COM 
9268934SJose.Borrego@Sun.COM 	smb_llist_enter(&node->n_ofile_list, RW_WRITER);
9278934SJose.Borrego@Sun.COM 	smb_llist_remove(&node->n_ofile_list, of);
9288934SJose.Borrego@Sun.COM 	smb_llist_exit(&node->n_ofile_list);
9298934SJose.Borrego@Sun.COM }
9308934SJose.Borrego@Sun.COM 
93110001SJoyce.McIntosh@Sun.COM /*
93210001SJoyce.McIntosh@Sun.COM  * smb_node_inc_open_ofiles
93310001SJoyce.McIntosh@Sun.COM  */
9348934SJose.Borrego@Sun.COM void
9358934SJose.Borrego@Sun.COM smb_node_inc_open_ofiles(smb_node_t *node)
9368934SJose.Borrego@Sun.COM {
9378934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9388934SJose.Borrego@Sun.COM 
9398934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
9408934SJose.Borrego@Sun.COM 	node->n_open_count++;
9418934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
94210001SJoyce.McIntosh@Sun.COM 
94310504SKeyur.Desai@Sun.COM 	smb_node_init_cached_data(node);
9448934SJose.Borrego@Sun.COM }
9458934SJose.Borrego@Sun.COM 
94610001SJoyce.McIntosh@Sun.COM /*
94710001SJoyce.McIntosh@Sun.COM  * smb_node_dec_open_ofiles
94810001SJoyce.McIntosh@Sun.COM  */
9498934SJose.Borrego@Sun.COM void
9508934SJose.Borrego@Sun.COM smb_node_dec_open_ofiles(smb_node_t *node)
9518934SJose.Borrego@Sun.COM {
9528934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9538934SJose.Borrego@Sun.COM 
9548934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
9558934SJose.Borrego@Sun.COM 	node->n_open_count--;
9568934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
95710001SJoyce.McIntosh@Sun.COM 
95810504SKeyur.Desai@Sun.COM 	smb_node_clear_cached_data(node);
9598934SJose.Borrego@Sun.COM }
9608934SJose.Borrego@Sun.COM 
9618934SJose.Borrego@Sun.COM uint32_t
9628934SJose.Borrego@Sun.COM smb_node_get_open_ofiles(smb_node_t *node)
9638934SJose.Borrego@Sun.COM {
9648934SJose.Borrego@Sun.COM 	uint32_t	cnt;
9658934SJose.Borrego@Sun.COM 
9668934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
9678934SJose.Borrego@Sun.COM 
9688934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
9698934SJose.Borrego@Sun.COM 	cnt = node->n_open_count;
9708934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
9718934SJose.Borrego@Sun.COM 	return (cnt);
9725772Sas200622 }
9738934SJose.Borrego@Sun.COM 
9748934SJose.Borrego@Sun.COM /*
975*11963SAfshin.Ardakani@Sun.COM  * smb_node_getmntpath
976*11963SAfshin.Ardakani@Sun.COM  */
977*11963SAfshin.Ardakani@Sun.COM int
978*11963SAfshin.Ardakani@Sun.COM smb_node_getmntpath(smb_node_t *node, char *buf, uint32_t buflen)
979*11963SAfshin.Ardakani@Sun.COM {
980*11963SAfshin.Ardakani@Sun.COM 	vnode_t *vp, *root_vp;
981*11963SAfshin.Ardakani@Sun.COM 	vfs_t *vfsp;
982*11963SAfshin.Ardakani@Sun.COM 	int err;
983*11963SAfshin.Ardakani@Sun.COM 
984*11963SAfshin.Ardakani@Sun.COM 	ASSERT(node);
985*11963SAfshin.Ardakani@Sun.COM 	ASSERT(node->vp);
986*11963SAfshin.Ardakani@Sun.COM 	ASSERT(node->vp->v_vfsp);
987*11963SAfshin.Ardakani@Sun.COM 
988*11963SAfshin.Ardakani@Sun.COM 	vp = node->vp;
989*11963SAfshin.Ardakani@Sun.COM 	vfsp = vp->v_vfsp;
990*11963SAfshin.Ardakani@Sun.COM 
991*11963SAfshin.Ardakani@Sun.COM 	if (VFS_ROOT(vfsp, &root_vp))
992*11963SAfshin.Ardakani@Sun.COM 		return (ENOENT);
993*11963SAfshin.Ardakani@Sun.COM 
994*11963SAfshin.Ardakani@Sun.COM 	VN_HOLD(vp);
995*11963SAfshin.Ardakani@Sun.COM 
996*11963SAfshin.Ardakani@Sun.COM 	/* NULL is passed in as we want to start at "/" */
997*11963SAfshin.Ardakani@Sun.COM 	err = vnodetopath(NULL, root_vp, buf, buflen, kcred);
998*11963SAfshin.Ardakani@Sun.COM 
999*11963SAfshin.Ardakani@Sun.COM 	VN_RELE(vp);
1000*11963SAfshin.Ardakani@Sun.COM 	VN_RELE(root_vp);
1001*11963SAfshin.Ardakani@Sun.COM 	return (err);
1002*11963SAfshin.Ardakani@Sun.COM }
1003*11963SAfshin.Ardakani@Sun.COM 
1004*11963SAfshin.Ardakani@Sun.COM /*
1005*11963SAfshin.Ardakani@Sun.COM  * smb_node_getshrpath
1006*11963SAfshin.Ardakani@Sun.COM  *
1007*11963SAfshin.Ardakani@Sun.COM  * Determine the absolute pathname of 'node' within the share (tree).
1008*11963SAfshin.Ardakani@Sun.COM  * For example if the node represents file "test1.txt" in directory
1009*11963SAfshin.Ardakani@Sun.COM  * "dir1" the pathname would be: \dir1\test1.txt
1010*11963SAfshin.Ardakani@Sun.COM  *
1011*11963SAfshin.Ardakani@Sun.COM  * If node represents a named stream, construct the pathname for the
1012*11963SAfshin.Ardakani@Sun.COM  * associated unnamed stream then append the stream name.
1013*11963SAfshin.Ardakani@Sun.COM  */
1014*11963SAfshin.Ardakani@Sun.COM int
1015*11963SAfshin.Ardakani@Sun.COM smb_node_getshrpath(smb_node_t *node, smb_tree_t *tree,
1016*11963SAfshin.Ardakani@Sun.COM     char *buf, uint32_t buflen)
1017*11963SAfshin.Ardakani@Sun.COM {
1018*11963SAfshin.Ardakani@Sun.COM 	int rc;
1019*11963SAfshin.Ardakani@Sun.COM 	vnode_t *vp;
1020*11963SAfshin.Ardakani@Sun.COM 
1021*11963SAfshin.Ardakani@Sun.COM 	(void) bzero(buf, buflen);
1022*11963SAfshin.Ardakani@Sun.COM 
1023*11963SAfshin.Ardakani@Sun.COM 	if (SMB_IS_STREAM(node))
1024*11963SAfshin.Ardakani@Sun.COM 		vp = node->n_unode->vp;
1025*11963SAfshin.Ardakani@Sun.COM 	else
1026*11963SAfshin.Ardakani@Sun.COM 		vp = node->vp;
1027*11963SAfshin.Ardakani@Sun.COM 
1028*11963SAfshin.Ardakani@Sun.COM 	if (vp == tree->t_snode->vp)
1029*11963SAfshin.Ardakani@Sun.COM 		return (0);
1030*11963SAfshin.Ardakani@Sun.COM 
1031*11963SAfshin.Ardakani@Sun.COM 	rc = vnodetopath(tree->t_snode->vp, vp, buf, buflen, kcred);
1032*11963SAfshin.Ardakani@Sun.COM 	if (rc == 0) {
1033*11963SAfshin.Ardakani@Sun.COM 		(void) strsubst(buf, '/', '\\');
1034*11963SAfshin.Ardakani@Sun.COM 
1035*11963SAfshin.Ardakani@Sun.COM 		if (SMB_IS_STREAM(node))
1036*11963SAfshin.Ardakani@Sun.COM 			(void) strlcat(buf, node->od_name, buflen);
1037*11963SAfshin.Ardakani@Sun.COM 	}
1038*11963SAfshin.Ardakani@Sun.COM 
1039*11963SAfshin.Ardakani@Sun.COM 	return (rc);
1040*11963SAfshin.Ardakani@Sun.COM }
1041*11963SAfshin.Ardakani@Sun.COM 
1042*11963SAfshin.Ardakani@Sun.COM /*
10438934SJose.Borrego@Sun.COM  * smb_node_alloc
10448934SJose.Borrego@Sun.COM  */
10458934SJose.Borrego@Sun.COM static smb_node_t *
10468934SJose.Borrego@Sun.COM smb_node_alloc(
10478934SJose.Borrego@Sun.COM     char	*od_name,
10488934SJose.Borrego@Sun.COM     vnode_t	*vp,
10498934SJose.Borrego@Sun.COM     smb_llist_t	*bucket,
10508934SJose.Borrego@Sun.COM     uint32_t	hashkey)
10518934SJose.Borrego@Sun.COM {
10528934SJose.Borrego@Sun.COM 	smb_node_t	*node;
1053*11963SAfshin.Ardakani@Sun.COM 	vnode_t		*root_vp;
10548934SJose.Borrego@Sun.COM 
10558934SJose.Borrego@Sun.COM 	node = kmem_cache_alloc(smb_node_cache, KM_SLEEP);
10568934SJose.Borrego@Sun.COM 
10578934SJose.Borrego@Sun.COM 	if (node->n_audit_buf != NULL)
10588934SJose.Borrego@Sun.COM 		node->n_audit_buf->anb_index = 0;
10598934SJose.Borrego@Sun.COM 
106010001SJoyce.McIntosh@Sun.COM 	node->flags = 0;
10618934SJose.Borrego@Sun.COM 	VN_HOLD(vp);
10628934SJose.Borrego@Sun.COM 	node->vp = vp;
10638934SJose.Borrego@Sun.COM 	node->n_refcnt = 1;
10648934SJose.Borrego@Sun.COM 	node->n_hash_bucket = bucket;
10658934SJose.Borrego@Sun.COM 	node->n_hashkey = hashkey;
10668934SJose.Borrego@Sun.COM 	node->readonly_creator = NULL;
10678934SJose.Borrego@Sun.COM 	node->waiting_event = 0;
10688934SJose.Borrego@Sun.COM 	node->n_open_count = 0;
106910122SJordan.Brown@Sun.COM 	node->n_dnode = NULL;
107010122SJordan.Brown@Sun.COM 	node->n_unode = NULL;
10718934SJose.Borrego@Sun.COM 	node->delete_on_close_cred = NULL;
10729231SAfshin.Ardakani@Sun.COM 	node->n_delete_on_close_flags = 0;
10738934SJose.Borrego@Sun.COM 
10748934SJose.Borrego@Sun.COM 	(void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
10758934SJose.Borrego@Sun.COM 	if (strcmp(od_name, XATTR_DIR) == 0)
10768934SJose.Borrego@Sun.COM 		node->flags |= NODE_XATTR_DIR;
10778934SJose.Borrego@Sun.COM 
1078*11963SAfshin.Ardakani@Sun.COM 	if (VFS_ROOT(vp->v_vfsp, &root_vp) == 0) {
1079*11963SAfshin.Ardakani@Sun.COM 		if (vp == root_vp)
1080*11963SAfshin.Ardakani@Sun.COM 			node->flags |= NODE_FLAGS_VFSROOT;
1081*11963SAfshin.Ardakani@Sun.COM 		VN_RELE(root_vp);
1082*11963SAfshin.Ardakani@Sun.COM 	}
1083*11963SAfshin.Ardakani@Sun.COM 
10848934SJose.Borrego@Sun.COM 	node->n_state = SMB_NODE_STATE_AVAILABLE;
10858934SJose.Borrego@Sun.COM 	node->n_magic = SMB_NODE_MAGIC;
1086*11963SAfshin.Ardakani@Sun.COM 
10878934SJose.Borrego@Sun.COM 	return (node);
10888934SJose.Borrego@Sun.COM }
10898934SJose.Borrego@Sun.COM 
10908934SJose.Borrego@Sun.COM /*
10918934SJose.Borrego@Sun.COM  * smb_node_free
10928934SJose.Borrego@Sun.COM  */
10938934SJose.Borrego@Sun.COM static void
10948934SJose.Borrego@Sun.COM smb_node_free(smb_node_t *node)
10958934SJose.Borrego@Sun.COM {
10968934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
10978934SJose.Borrego@Sun.COM 
10988934SJose.Borrego@Sun.COM 	node->n_magic = 0;
10998934SJose.Borrego@Sun.COM 	VERIFY(!list_link_active(&node->n_lnd));
11008934SJose.Borrego@Sun.COM 	VERIFY(node->n_lock_list.ll_count == 0);
11018934SJose.Borrego@Sun.COM 	VERIFY(node->n_ofile_list.ll_count == 0);
11028934SJose.Borrego@Sun.COM 	VERIFY(node->n_oplock.ol_xthread == NULL);
11039021Samw@Sun.COM 	VERIFY(mutex_owner(&node->n_mutex) == NULL);
11049021Samw@Sun.COM 	VERIFY(!RW_LOCK_HELD(&node->n_lock));
11058934SJose.Borrego@Sun.COM 	VN_RELE(node->vp);
11068934SJose.Borrego@Sun.COM 	kmem_cache_free(smb_node_cache, node);
11078934SJose.Borrego@Sun.COM }
11088934SJose.Borrego@Sun.COM 
11098934SJose.Borrego@Sun.COM /*
11108934SJose.Borrego@Sun.COM  * smb_node_constructor
11118934SJose.Borrego@Sun.COM  */
11128934SJose.Borrego@Sun.COM static int
11138934SJose.Borrego@Sun.COM smb_node_constructor(void *buf, void *un, int kmflags)
11148934SJose.Borrego@Sun.COM {
11158934SJose.Borrego@Sun.COM 	_NOTE(ARGUNUSED(kmflags, un))
11168934SJose.Borrego@Sun.COM 
11178934SJose.Borrego@Sun.COM 	smb_node_t	*node = (smb_node_t *)buf;
11188934SJose.Borrego@Sun.COM 
11198934SJose.Borrego@Sun.COM 	bzero(node, sizeof (smb_node_t));
11208934SJose.Borrego@Sun.COM 
11218934SJose.Borrego@Sun.COM 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
11228934SJose.Borrego@Sun.COM 	    offsetof(smb_ofile_t, f_nnd));
11238934SJose.Borrego@Sun.COM 	smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
11248934SJose.Borrego@Sun.COM 	    offsetof(smb_lock_t, l_lnd));
11258934SJose.Borrego@Sun.COM 	cv_init(&node->n_oplock.ol_cv, NULL, CV_DEFAULT, NULL);
11268934SJose.Borrego@Sun.COM 	rw_init(&node->n_lock, NULL, RW_DEFAULT, NULL);
11278934SJose.Borrego@Sun.COM 	mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL);
11288934SJose.Borrego@Sun.COM 	smb_node_create_audit_buf(node, kmflags);
11298934SJose.Borrego@Sun.COM 	return (0);
11308934SJose.Borrego@Sun.COM }
11318934SJose.Borrego@Sun.COM 
11328934SJose.Borrego@Sun.COM /*
11338934SJose.Borrego@Sun.COM  * smb_node_destructor
11348934SJose.Borrego@Sun.COM  */
11358934SJose.Borrego@Sun.COM static void
11368934SJose.Borrego@Sun.COM smb_node_destructor(void *buf, void *un)
11378934SJose.Borrego@Sun.COM {
11388934SJose.Borrego@Sun.COM 	_NOTE(ARGUNUSED(un))
11398934SJose.Borrego@Sun.COM 
11408934SJose.Borrego@Sun.COM 	smb_node_t	*node = (smb_node_t *)buf;
11418934SJose.Borrego@Sun.COM 
11428934SJose.Borrego@Sun.COM 	smb_node_destroy_audit_buf(node);
11438934SJose.Borrego@Sun.COM 	mutex_destroy(&node->n_mutex);
11448934SJose.Borrego@Sun.COM 	rw_destroy(&node->n_lock);
11458934SJose.Borrego@Sun.COM 	cv_destroy(&node->n_oplock.ol_cv);
11468934SJose.Borrego@Sun.COM 	smb_llist_destructor(&node->n_lock_list);
11478934SJose.Borrego@Sun.COM 	smb_llist_destructor(&node->n_ofile_list);
11488934SJose.Borrego@Sun.COM }
11498934SJose.Borrego@Sun.COM 
11508934SJose.Borrego@Sun.COM /*
11518934SJose.Borrego@Sun.COM  * smb_node_create_audit_buf
11528934SJose.Borrego@Sun.COM  */
11538934SJose.Borrego@Sun.COM static void
11548934SJose.Borrego@Sun.COM smb_node_create_audit_buf(smb_node_t *node, int kmflags)
11558934SJose.Borrego@Sun.COM {
11568934SJose.Borrego@Sun.COM 	smb_audit_buf_node_t	*abn;
11578934SJose.Borrego@Sun.COM 
11588934SJose.Borrego@Sun.COM 	if (smb_audit_flags & SMB_AUDIT_NODE) {
11598934SJose.Borrego@Sun.COM 		abn = kmem_zalloc(sizeof (smb_audit_buf_node_t), kmflags);
11608934SJose.Borrego@Sun.COM 		abn->anb_max_index = SMB_AUDIT_BUF_MAX_REC - 1;
11618934SJose.Borrego@Sun.COM 		node->n_audit_buf = abn;
11628934SJose.Borrego@Sun.COM 	}
11638934SJose.Borrego@Sun.COM }
11648934SJose.Borrego@Sun.COM 
11658934SJose.Borrego@Sun.COM /*
11668934SJose.Borrego@Sun.COM  * smb_node_destroy_audit_buf
11678934SJose.Borrego@Sun.COM  */
11688934SJose.Borrego@Sun.COM static void
11698934SJose.Borrego@Sun.COM smb_node_destroy_audit_buf(smb_node_t *node)
11708934SJose.Borrego@Sun.COM {
11718934SJose.Borrego@Sun.COM 	if (node->n_audit_buf != NULL) {
11728934SJose.Borrego@Sun.COM 		kmem_free(node->n_audit_buf, sizeof (smb_audit_buf_node_t));
11738934SJose.Borrego@Sun.COM 		node->n_audit_buf = NULL;
11748934SJose.Borrego@Sun.COM 	}
11758934SJose.Borrego@Sun.COM }
11768934SJose.Borrego@Sun.COM 
11778934SJose.Borrego@Sun.COM /*
11788934SJose.Borrego@Sun.COM  * smb_node_audit
11798934SJose.Borrego@Sun.COM  *
11808934SJose.Borrego@Sun.COM  * This function saves the calling stack in the audit buffer of the node passed
11818934SJose.Borrego@Sun.COM  * in.
11828934SJose.Borrego@Sun.COM  */
11838934SJose.Borrego@Sun.COM static void
11848934SJose.Borrego@Sun.COM smb_node_audit(smb_node_t *node)
11858934SJose.Borrego@Sun.COM {
11868934SJose.Borrego@Sun.COM 	smb_audit_buf_node_t	*abn;
11878934SJose.Borrego@Sun.COM 	smb_audit_record_node_t	*anr;
11888934SJose.Borrego@Sun.COM 
11898934SJose.Borrego@Sun.COM 	if (node->n_audit_buf) {
11908934SJose.Borrego@Sun.COM 		abn = node->n_audit_buf;
11918934SJose.Borrego@Sun.COM 		anr = abn->anb_records;
11928934SJose.Borrego@Sun.COM 		anr += abn->anb_index;
11938934SJose.Borrego@Sun.COM 		abn->anb_index++;
11948934SJose.Borrego@Sun.COM 		abn->anb_index &= abn->anb_max_index;
11958934SJose.Borrego@Sun.COM 		anr->anr_refcnt = node->n_refcnt;
11968934SJose.Borrego@Sun.COM 		anr->anr_depth = getpcstack(anr->anr_stack,
11978934SJose.Borrego@Sun.COM 		    SMB_AUDIT_STACK_DEPTH);
11988934SJose.Borrego@Sun.COM 	}
11998934SJose.Borrego@Sun.COM }
12008934SJose.Borrego@Sun.COM 
12018934SJose.Borrego@Sun.COM static smb_llist_t *
12028934SJose.Borrego@Sun.COM smb_node_get_hash(fsid_t *fsid, smb_attr_t *attr, uint32_t *phashkey)
12038934SJose.Borrego@Sun.COM {
12048934SJose.Borrego@Sun.COM 	uint32_t	hashkey;
12058934SJose.Borrego@Sun.COM 
12068934SJose.Borrego@Sun.COM 	hashkey = fsid->val[0] + attr->sa_vattr.va_nodeid;
12078934SJose.Borrego@Sun.COM 	hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8);
12088934SJose.Borrego@Sun.COM 	*phashkey = hashkey;
12098934SJose.Borrego@Sun.COM 	return (&smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]);
12108934SJose.Borrego@Sun.COM }
121110001SJoyce.McIntosh@Sun.COM 
121210001SJoyce.McIntosh@Sun.COM boolean_t
1213*11963SAfshin.Ardakani@Sun.COM smb_node_is_file(smb_node_t *node)
1214*11963SAfshin.Ardakani@Sun.COM {
1215*11963SAfshin.Ardakani@Sun.COM 	SMB_NODE_VALID(node);
1216*11963SAfshin.Ardakani@Sun.COM 	return (node->vp->v_type == VREG);
1217*11963SAfshin.Ardakani@Sun.COM }
1218*11963SAfshin.Ardakani@Sun.COM 
1219*11963SAfshin.Ardakani@Sun.COM boolean_t
122010001SJoyce.McIntosh@Sun.COM smb_node_is_dir(smb_node_t *node)
122110001SJoyce.McIntosh@Sun.COM {
122210001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
1223*11963SAfshin.Ardakani@Sun.COM 	return ((node->vp->v_type == VDIR) ||
1224*11963SAfshin.Ardakani@Sun.COM 	    (node->flags & NODE_FLAGS_DFSLINK));
1225*11963SAfshin.Ardakani@Sun.COM }
1226*11963SAfshin.Ardakani@Sun.COM 
1227*11963SAfshin.Ardakani@Sun.COM boolean_t
1228*11963SAfshin.Ardakani@Sun.COM smb_node_is_symlink(smb_node_t *node)
1229*11963SAfshin.Ardakani@Sun.COM {
1230*11963SAfshin.Ardakani@Sun.COM 	SMB_NODE_VALID(node);
1231*11963SAfshin.Ardakani@Sun.COM 	return ((node->vp->v_type == VLNK) &&
1232*11963SAfshin.Ardakani@Sun.COM 	    ((node->flags & NODE_FLAGS_REPARSE) == 0));
123310001SJoyce.McIntosh@Sun.COM }
123410001SJoyce.McIntosh@Sun.COM 
123510001SJoyce.McIntosh@Sun.COM boolean_t
1236*11963SAfshin.Ardakani@Sun.COM smb_node_is_dfslink(smb_node_t *node)
1237*11963SAfshin.Ardakani@Sun.COM {
1238*11963SAfshin.Ardakani@Sun.COM 	SMB_NODE_VALID(node);
1239*11963SAfshin.Ardakani@Sun.COM 	return ((node->vp->v_type == VLNK) &&
1240*11963SAfshin.Ardakani@Sun.COM 	    (node->flags & NODE_FLAGS_DFSLINK));
1241*11963SAfshin.Ardakani@Sun.COM }
1242*11963SAfshin.Ardakani@Sun.COM 
1243*11963SAfshin.Ardakani@Sun.COM boolean_t
1244*11963SAfshin.Ardakani@Sun.COM smb_node_is_reparse(smb_node_t *node)
124510001SJoyce.McIntosh@Sun.COM {
124610001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
1247*11963SAfshin.Ardakani@Sun.COM 	return ((node->vp->v_type == VLNK) &&
1248*11963SAfshin.Ardakani@Sun.COM 	    (node->flags & NODE_FLAGS_REPARSE));
1249*11963SAfshin.Ardakani@Sun.COM }
1250*11963SAfshin.Ardakani@Sun.COM 
1251*11963SAfshin.Ardakani@Sun.COM boolean_t
1252*11963SAfshin.Ardakani@Sun.COM smb_node_is_vfsroot(smb_node_t *node)
1253*11963SAfshin.Ardakani@Sun.COM {
1254*11963SAfshin.Ardakani@Sun.COM 	SMB_NODE_VALID(node);
1255*11963SAfshin.Ardakani@Sun.COM 	return ((node->flags & NODE_FLAGS_VFSROOT) == NODE_FLAGS_VFSROOT);
1256*11963SAfshin.Ardakani@Sun.COM }
1257*11963SAfshin.Ardakani@Sun.COM 
1258*11963SAfshin.Ardakani@Sun.COM boolean_t
1259*11963SAfshin.Ardakani@Sun.COM smb_node_is_system(smb_node_t *node)
1260*11963SAfshin.Ardakani@Sun.COM {
1261*11963SAfshin.Ardakani@Sun.COM 	SMB_NODE_VALID(node);
1262*11963SAfshin.Ardakani@Sun.COM 	return ((node->flags & NODE_FLAGS_SYSTEM) == NODE_FLAGS_SYSTEM);
126310001SJoyce.McIntosh@Sun.COM }
126410001SJoyce.McIntosh@Sun.COM 
126510001SJoyce.McIntosh@Sun.COM /*
126610001SJoyce.McIntosh@Sun.COM  * smb_node_file_is_readonly
126710001SJoyce.McIntosh@Sun.COM  *
126810001SJoyce.McIntosh@Sun.COM  * Checks if the file (which node represents) is marked readonly
126910001SJoyce.McIntosh@Sun.COM  * in the filesystem. No account is taken of any pending readonly
127010001SJoyce.McIntosh@Sun.COM  * in the node, which must be handled by the callers.
127110001SJoyce.McIntosh@Sun.COM  * (See SMB_OFILE_IS_READONLY and SMB_PATHFILE_IS_READONLY)
127210001SJoyce.McIntosh@Sun.COM  */
127310001SJoyce.McIntosh@Sun.COM boolean_t
127410001SJoyce.McIntosh@Sun.COM smb_node_file_is_readonly(smb_node_t *node)
127510001SJoyce.McIntosh@Sun.COM {
127610001SJoyce.McIntosh@Sun.COM 	smb_attr_t attr;
127710001SJoyce.McIntosh@Sun.COM 
127810001SJoyce.McIntosh@Sun.COM 	if (node == NULL)
127910001SJoyce.McIntosh@Sun.COM 		return (B_FALSE);
128010001SJoyce.McIntosh@Sun.COM 
128110001SJoyce.McIntosh@Sun.COM 	bzero(&attr, sizeof (smb_attr_t));
128210001SJoyce.McIntosh@Sun.COM 	attr.sa_mask = SMB_AT_DOSATTR;
128310001SJoyce.McIntosh@Sun.COM 	(void) smb_fsop_getattr(NULL, kcred, node, &attr);
128410001SJoyce.McIntosh@Sun.COM 	return ((attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) != 0);
128510001SJoyce.McIntosh@Sun.COM }
128610001SJoyce.McIntosh@Sun.COM 
128710001SJoyce.McIntosh@Sun.COM /*
128810001SJoyce.McIntosh@Sun.COM  * smb_node_setattr
128910001SJoyce.McIntosh@Sun.COM  *
129010001SJoyce.McIntosh@Sun.COM  * The sr may be NULL, for example when closing an ofile.
129110001SJoyce.McIntosh@Sun.COM  * The ofile may be NULL, for example when a client request
129210001SJoyce.McIntosh@Sun.COM  * specifies the file by pathname.
129310001SJoyce.McIntosh@Sun.COM  *
129410504SKeyur.Desai@Sun.COM  * Timestamps
129510001SJoyce.McIntosh@Sun.COM  * When attributes are set on an ofile, any pending timestamps
129610001SJoyce.McIntosh@Sun.COM  * from a write request on the ofile are implicitly set to "now".
129710001SJoyce.McIntosh@Sun.COM  * For compatibility with windows the following timestamps are
129810001SJoyce.McIntosh@Sun.COM  * also implicitly set to now:
129910001SJoyce.McIntosh@Sun.COM  * - if any attribute is being explicitly set, set ctime to now
130010001SJoyce.McIntosh@Sun.COM  * - if file size is being explicitly set, set atime & ctime to now
130110001SJoyce.McIntosh@Sun.COM  *
130210504SKeyur.Desai@Sun.COM  * Any timestamp that is being explicitly set, or has previously
130310001SJoyce.McIntosh@Sun.COM  * been explicitly set on the ofile, is excluded from implicit
130410001SJoyce.McIntosh@Sun.COM  * (now) setting.
130510001SJoyce.McIntosh@Sun.COM  *
130610001SJoyce.McIntosh@Sun.COM  * Updates the node's cached timestamp values.
130710001SJoyce.McIntosh@Sun.COM  * Updates the ofile's explicit times flag.
130810001SJoyce.McIntosh@Sun.COM  *
130910504SKeyur.Desai@Sun.COM  * File allocation size
131010504SKeyur.Desai@Sun.COM  * When the file allocation size is set it is first rounded up
131110504SKeyur.Desai@Sun.COM  * to block size. If the file size is smaller than the allocation
131210504SKeyur.Desai@Sun.COM  * size the file is truncated by setting the filesize to allocsz.
131310504SKeyur.Desai@Sun.COM  * If there are open ofiles, the allocsz is cached on the node.
131410504SKeyur.Desai@Sun.COM  *
131510504SKeyur.Desai@Sun.COM  * Updates the node's cached allocsz value.
131610504SKeyur.Desai@Sun.COM  *
131710001SJoyce.McIntosh@Sun.COM  * Returns: errno
131810001SJoyce.McIntosh@Sun.COM  */
131910001SJoyce.McIntosh@Sun.COM int
132010001SJoyce.McIntosh@Sun.COM smb_node_setattr(smb_request_t *sr, smb_node_t *node,
132110001SJoyce.McIntosh@Sun.COM     cred_t *cr, smb_ofile_t *of, smb_attr_t *attr)
132210001SJoyce.McIntosh@Sun.COM {
132310001SJoyce.McIntosh@Sun.COM 	int rc;
132410504SKeyur.Desai@Sun.COM 	uint32_t pending_times = 0;
132510504SKeyur.Desai@Sun.COM 	uint32_t explicit_times = 0;
132610001SJoyce.McIntosh@Sun.COM 	timestruc_t now;
132710504SKeyur.Desai@Sun.COM 	smb_attr_t tmp_attr;
132810001SJoyce.McIntosh@Sun.COM 
132910001SJoyce.McIntosh@Sun.COM 	ASSERT(attr);
133010001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
133110001SJoyce.McIntosh@Sun.COM 
133210504SKeyur.Desai@Sun.COM 	/* set attributes specified in attr */
133310504SKeyur.Desai@Sun.COM 	if (attr->sa_mask != 0) {
133410504SKeyur.Desai@Sun.COM 		/* if allocation size is < file size, truncate the file */
133510504SKeyur.Desai@Sun.COM 		if (attr->sa_mask & SMB_AT_ALLOCSZ) {
133610504SKeyur.Desai@Sun.COM 			attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_allocsz);
133710001SJoyce.McIntosh@Sun.COM 
133810504SKeyur.Desai@Sun.COM 			bzero(&tmp_attr, sizeof (smb_attr_t));
133910504SKeyur.Desai@Sun.COM 			tmp_attr.sa_mask = SMB_AT_SIZE;
134010504SKeyur.Desai@Sun.COM 			(void) smb_fsop_getattr(NULL, kcred, node, &tmp_attr);
134110001SJoyce.McIntosh@Sun.COM 
134210504SKeyur.Desai@Sun.COM 			if (tmp_attr.sa_vattr.va_size > attr->sa_allocsz) {
134310504SKeyur.Desai@Sun.COM 				attr->sa_vattr.va_size = attr->sa_allocsz;
134410504SKeyur.Desai@Sun.COM 				attr->sa_mask |= SMB_AT_SIZE;
134510504SKeyur.Desai@Sun.COM 			}
134610001SJoyce.McIntosh@Sun.COM 		}
134710504SKeyur.Desai@Sun.COM 
134810504SKeyur.Desai@Sun.COM 		rc = smb_fsop_setattr(sr, cr, node, attr);
134910504SKeyur.Desai@Sun.COM 		if (rc != 0)
135010504SKeyur.Desai@Sun.COM 			return (rc);
135110504SKeyur.Desai@Sun.COM 
135210504SKeyur.Desai@Sun.COM 		smb_node_set_cached_allocsz(node, attr);
135310504SKeyur.Desai@Sun.COM 		smb_node_set_cached_timestamps(node, attr);
135410504SKeyur.Desai@Sun.COM 		if (of) {
135510504SKeyur.Desai@Sun.COM 			smb_ofile_set_explicit_times(of,
135610504SKeyur.Desai@Sun.COM 			    (attr->sa_mask & SMB_AT_TIMES));
135710001SJoyce.McIntosh@Sun.COM 		}
135810001SJoyce.McIntosh@Sun.COM 	}
135910001SJoyce.McIntosh@Sun.COM 
136010504SKeyur.Desai@Sun.COM 	/*
136110504SKeyur.Desai@Sun.COM 	 * Determine which timestamps to implicitly set to "now".
136210504SKeyur.Desai@Sun.COM 	 * Don't overwrite timestamps already explicitly set.
136310504SKeyur.Desai@Sun.COM 	 */
136410504SKeyur.Desai@Sun.COM 	bzero(&tmp_attr, sizeof (smb_attr_t));
136510504SKeyur.Desai@Sun.COM 	gethrestime(&now);
136610504SKeyur.Desai@Sun.COM 	tmp_attr.sa_vattr.va_atime = now;
136710504SKeyur.Desai@Sun.COM 	tmp_attr.sa_vattr.va_mtime = now;
136810504SKeyur.Desai@Sun.COM 	tmp_attr.sa_vattr.va_ctime = now;
136910001SJoyce.McIntosh@Sun.COM 
137010504SKeyur.Desai@Sun.COM 	/* pending write timestamps */
137110504SKeyur.Desai@Sun.COM 	if (of) {
137210504SKeyur.Desai@Sun.COM 		if (smb_ofile_write_time_pending(of)) {
137310504SKeyur.Desai@Sun.COM 			pending_times |=
137410504SKeyur.Desai@Sun.COM 			    (SMB_AT_MTIME | SMB_AT_CTIME | SMB_AT_ATIME);
137510504SKeyur.Desai@Sun.COM 		}
137610504SKeyur.Desai@Sun.COM 		explicit_times |= (smb_ofile_explicit_times(of));
137710504SKeyur.Desai@Sun.COM 	}
137810504SKeyur.Desai@Sun.COM 	explicit_times |= (attr->sa_mask & SMB_AT_TIMES);
137910504SKeyur.Desai@Sun.COM 	pending_times &= ~explicit_times;
138010001SJoyce.McIntosh@Sun.COM 
138110504SKeyur.Desai@Sun.COM 	if (pending_times) {
138210504SKeyur.Desai@Sun.COM 		tmp_attr.sa_mask = pending_times;
138310504SKeyur.Desai@Sun.COM 		(void) smb_fsop_setattr(NULL, kcred, node, &tmp_attr);
138410504SKeyur.Desai@Sun.COM 	}
138510001SJoyce.McIntosh@Sun.COM 
138610504SKeyur.Desai@Sun.COM 	/* additional timestamps to update in cache */
138710504SKeyur.Desai@Sun.COM 	if (attr->sa_mask)
138810504SKeyur.Desai@Sun.COM 		tmp_attr.sa_mask |= SMB_AT_CTIME;
138910504SKeyur.Desai@Sun.COM 	if (attr->sa_mask & (SMB_AT_SIZE | SMB_AT_ALLOCSZ))
139010504SKeyur.Desai@Sun.COM 		tmp_attr.sa_mask |= SMB_AT_MTIME;
139110504SKeyur.Desai@Sun.COM 	tmp_attr.sa_mask &= ~explicit_times;
139210504SKeyur.Desai@Sun.COM 
139310504SKeyur.Desai@Sun.COM 	if (tmp_attr.sa_mask)
139410504SKeyur.Desai@Sun.COM 		smb_node_set_cached_timestamps(node, &tmp_attr);
139510001SJoyce.McIntosh@Sun.COM 
1396*11963SAfshin.Ardakani@Sun.COM 	if (tmp_attr.sa_mask & SMB_AT_MTIME || explicit_times & SMB_AT_MTIME) {
1397*11963SAfshin.Ardakani@Sun.COM 		if (node->n_dnode != NULL)
1398*11963SAfshin.Ardakani@Sun.COM 			smb_node_notify_change(node->n_dnode);
1399*11963SAfshin.Ardakani@Sun.COM 	}
140011337SWilliam.Krier@Sun.COM 
140110001SJoyce.McIntosh@Sun.COM 	return (0);
140210001SJoyce.McIntosh@Sun.COM }
140310001SJoyce.McIntosh@Sun.COM 
140410001SJoyce.McIntosh@Sun.COM /*
140510001SJoyce.McIntosh@Sun.COM  * smb_node_getattr
140610001SJoyce.McIntosh@Sun.COM  *
140710001SJoyce.McIntosh@Sun.COM  * Get attributes from the file system and apply any smb-specific
140810001SJoyce.McIntosh@Sun.COM  * overrides for size, dos attributes and timestamps
140910001SJoyce.McIntosh@Sun.COM  *
141010001SJoyce.McIntosh@Sun.COM  * node->readonly_creator reflects whether a readonly set is pending
141110001SJoyce.McIntosh@Sun.COM  * from a readonly create. The readonly attribute should be visible to
141210001SJoyce.McIntosh@Sun.COM  * all clients even though the readonly creator fid is immune to the
141310001SJoyce.McIntosh@Sun.COM  * readonly bit until close.
141410001SJoyce.McIntosh@Sun.COM  *
141510001SJoyce.McIntosh@Sun.COM  * Returns: errno
141610001SJoyce.McIntosh@Sun.COM  */
141710001SJoyce.McIntosh@Sun.COM int
141810001SJoyce.McIntosh@Sun.COM smb_node_getattr(smb_request_t *sr, smb_node_t *node, smb_attr_t *attr)
141910001SJoyce.McIntosh@Sun.COM {
142010001SJoyce.McIntosh@Sun.COM 	int rc;
142110001SJoyce.McIntosh@Sun.COM 
142210001SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
142310001SJoyce.McIntosh@Sun.COM 
142410001SJoyce.McIntosh@Sun.COM 	bzero(attr, sizeof (smb_attr_t));
142510001SJoyce.McIntosh@Sun.COM 	attr->sa_mask = SMB_AT_ALL;
142610001SJoyce.McIntosh@Sun.COM 	rc = smb_fsop_getattr(sr, kcred, node, attr);
142710001SJoyce.McIntosh@Sun.COM 	if (rc != 0)
142810001SJoyce.McIntosh@Sun.COM 		return (rc);
142910001SJoyce.McIntosh@Sun.COM 
143010001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
143110001SJoyce.McIntosh@Sun.COM 
1432*11963SAfshin.Ardakani@Sun.COM 	if (smb_node_is_dir(node)) {
143310001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_size = 0;
143410504SKeyur.Desai@Sun.COM 		attr->sa_allocsz = 0;
143510966SJordan.Brown@Sun.COM 		attr->sa_vattr.va_nlink = 1;
143610504SKeyur.Desai@Sun.COM 	}
143710001SJoyce.McIntosh@Sun.COM 
143810001SJoyce.McIntosh@Sun.COM 	if (node->readonly_creator)
143910001SJoyce.McIntosh@Sun.COM 		attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY;
144010001SJoyce.McIntosh@Sun.COM 	if (attr->sa_dosattr == 0)
144110001SJoyce.McIntosh@Sun.COM 		attr->sa_dosattr = FILE_ATTRIBUTE_NORMAL;
144210001SJoyce.McIntosh@Sun.COM 
144310504SKeyur.Desai@Sun.COM 
144410001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
144510001SJoyce.McIntosh@Sun.COM 
144610504SKeyur.Desai@Sun.COM 	smb_node_get_cached_allocsz(node, attr);
144710001SJoyce.McIntosh@Sun.COM 	smb_node_get_cached_timestamps(node, attr);
144810504SKeyur.Desai@Sun.COM 
144910001SJoyce.McIntosh@Sun.COM 	return (0);
145010001SJoyce.McIntosh@Sun.COM }
145110001SJoyce.McIntosh@Sun.COM 
145210001SJoyce.McIntosh@Sun.COM /*
145310504SKeyur.Desai@Sun.COM  * smb_node_init_cached_data
145410504SKeyur.Desai@Sun.COM  */
145510504SKeyur.Desai@Sun.COM static void
145610504SKeyur.Desai@Sun.COM smb_node_init_cached_data(smb_node_t *node)
145710504SKeyur.Desai@Sun.COM {
145810504SKeyur.Desai@Sun.COM 	smb_attr_t attr;
145910504SKeyur.Desai@Sun.COM 
146010504SKeyur.Desai@Sun.COM 	bzero(&attr, sizeof (smb_attr_t));
146110504SKeyur.Desai@Sun.COM 	attr.sa_mask = SMB_AT_ALL;
146210504SKeyur.Desai@Sun.COM 	(void) smb_fsop_getattr(NULL, kcred, node, &attr);
146310504SKeyur.Desai@Sun.COM 
146410504SKeyur.Desai@Sun.COM 	smb_node_init_cached_allocsz(node, &attr);
146510504SKeyur.Desai@Sun.COM 	smb_node_init_cached_timestamps(node, &attr);
146610504SKeyur.Desai@Sun.COM }
146710504SKeyur.Desai@Sun.COM 
146810504SKeyur.Desai@Sun.COM /*
146910504SKeyur.Desai@Sun.COM  * smb_node_clear_cached_data
147010504SKeyur.Desai@Sun.COM  */
147110504SKeyur.Desai@Sun.COM static void
147210504SKeyur.Desai@Sun.COM smb_node_clear_cached_data(smb_node_t *node)
147310504SKeyur.Desai@Sun.COM {
147410504SKeyur.Desai@Sun.COM 	smb_node_clear_cached_allocsz(node);
147510504SKeyur.Desai@Sun.COM 	smb_node_clear_cached_timestamps(node);
147610504SKeyur.Desai@Sun.COM }
147710504SKeyur.Desai@Sun.COM 
147810504SKeyur.Desai@Sun.COM /*
147910504SKeyur.Desai@Sun.COM  * File allocation size (allocsz) caching
148010504SKeyur.Desai@Sun.COM  *
148110504SKeyur.Desai@Sun.COM  * When there are open ofiles on the node, the file allocsz is cached.
148210504SKeyur.Desai@Sun.COM  * The cached value (n_allocsz) is initialized when the first ofile is
148310504SKeyur.Desai@Sun.COM  * opened and cleared when the last is closed. Allocsz calculated from
148410504SKeyur.Desai@Sun.COM  * the filesize (rounded up to block size).
148510504SKeyur.Desai@Sun.COM  * When the allocation size is queried, if the cached allocsz is less
148610504SKeyur.Desai@Sun.COM  * than the filesize, it is recalculated from the filesize.
148710504SKeyur.Desai@Sun.COM  */
148810504SKeyur.Desai@Sun.COM 
148910504SKeyur.Desai@Sun.COM /*
149010504SKeyur.Desai@Sun.COM  * smb_node_init_cached_allocsz
149110504SKeyur.Desai@Sun.COM  *
149210504SKeyur.Desai@Sun.COM  * If there are open ofiles, cache the allocsz in the node.
149310504SKeyur.Desai@Sun.COM  * Calculate the allocsz from the filesizes.
149410504SKeyur.Desai@Sun.COM  * block size).
149510504SKeyur.Desai@Sun.COM  */
149610504SKeyur.Desai@Sun.COM static void
149710504SKeyur.Desai@Sun.COM smb_node_init_cached_allocsz(smb_node_t *node, smb_attr_t *attr)
149810504SKeyur.Desai@Sun.COM {
149910504SKeyur.Desai@Sun.COM 	mutex_enter(&node->n_mutex);
150010504SKeyur.Desai@Sun.COM 	if (node->n_open_count == 1)
150110504SKeyur.Desai@Sun.COM 		node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size);
150210504SKeyur.Desai@Sun.COM 	mutex_exit(&node->n_mutex);
150310504SKeyur.Desai@Sun.COM }
150410504SKeyur.Desai@Sun.COM 
150510504SKeyur.Desai@Sun.COM /*
150610504SKeyur.Desai@Sun.COM  * smb_node_clear_cached_allocsz
150710504SKeyur.Desai@Sun.COM  */
150810504SKeyur.Desai@Sun.COM static void
150910504SKeyur.Desai@Sun.COM smb_node_clear_cached_allocsz(smb_node_t *node)
151010504SKeyur.Desai@Sun.COM {
151110504SKeyur.Desai@Sun.COM 	mutex_enter(&node->n_mutex);
151210504SKeyur.Desai@Sun.COM 	if (node->n_open_count == 0)
151310504SKeyur.Desai@Sun.COM 		node->n_allocsz = 0;
151410504SKeyur.Desai@Sun.COM 	mutex_exit(&node->n_mutex);
151510504SKeyur.Desai@Sun.COM }
151610504SKeyur.Desai@Sun.COM 
151710504SKeyur.Desai@Sun.COM /*
151810504SKeyur.Desai@Sun.COM  * smb_node_get_cached_allocsz
151910504SKeyur.Desai@Sun.COM  *
152010504SKeyur.Desai@Sun.COM  * If there is no cached allocsz (no open files), calculate
152110504SKeyur.Desai@Sun.COM  * allocsz from the filesize.
152210504SKeyur.Desai@Sun.COM  * If the allocsz is cached but is smaller than the filesize
152310504SKeyur.Desai@Sun.COM  * recalculate the cached allocsz from the filesize.
152410504SKeyur.Desai@Sun.COM  *
152510504SKeyur.Desai@Sun.COM  * Return allocs in attr->sa_allocsz.
152610504SKeyur.Desai@Sun.COM  */
152710504SKeyur.Desai@Sun.COM static void
152810504SKeyur.Desai@Sun.COM smb_node_get_cached_allocsz(smb_node_t *node, smb_attr_t *attr)
152910504SKeyur.Desai@Sun.COM {
1530*11963SAfshin.Ardakani@Sun.COM 	if (smb_node_is_dir(node))
153110504SKeyur.Desai@Sun.COM 		return;
153210504SKeyur.Desai@Sun.COM 
153310504SKeyur.Desai@Sun.COM 	mutex_enter(&node->n_mutex);
153410504SKeyur.Desai@Sun.COM 	if (node->n_open_count == 0) {
153510504SKeyur.Desai@Sun.COM 		attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size);
153610504SKeyur.Desai@Sun.COM 	} else {
153710504SKeyur.Desai@Sun.COM 		if (node->n_allocsz < attr->sa_vattr.va_size)
153810504SKeyur.Desai@Sun.COM 			node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size);
153910504SKeyur.Desai@Sun.COM 		attr->sa_allocsz = node->n_allocsz;
154010504SKeyur.Desai@Sun.COM 	}
154110504SKeyur.Desai@Sun.COM 	mutex_exit(&node->n_mutex);
154210504SKeyur.Desai@Sun.COM }
154310504SKeyur.Desai@Sun.COM 
154410504SKeyur.Desai@Sun.COM /*
154510504SKeyur.Desai@Sun.COM  * smb_node_set_cached_allocsz
154610504SKeyur.Desai@Sun.COM  *
154710504SKeyur.Desai@Sun.COM  * attr->sa_allocsz has already been rounded to block size by
154810504SKeyur.Desai@Sun.COM  * the caller.
154910504SKeyur.Desai@Sun.COM  */
155010504SKeyur.Desai@Sun.COM static void
155110504SKeyur.Desai@Sun.COM smb_node_set_cached_allocsz(smb_node_t *node, smb_attr_t *attr)
155210504SKeyur.Desai@Sun.COM {
155310504SKeyur.Desai@Sun.COM 	mutex_enter(&node->n_mutex);
155410504SKeyur.Desai@Sun.COM 	if (attr->sa_mask & SMB_AT_ALLOCSZ) {
155510504SKeyur.Desai@Sun.COM 		if (node->n_open_count > 0)
155610504SKeyur.Desai@Sun.COM 			node->n_allocsz = attr->sa_allocsz;
155710504SKeyur.Desai@Sun.COM 	}
155810504SKeyur.Desai@Sun.COM 	mutex_exit(&node->n_mutex);
155910504SKeyur.Desai@Sun.COM }
156010504SKeyur.Desai@Sun.COM 
156110504SKeyur.Desai@Sun.COM 
156210504SKeyur.Desai@Sun.COM /*
156310001SJoyce.McIntosh@Sun.COM  * Timestamp caching
156410001SJoyce.McIntosh@Sun.COM  *
156510001SJoyce.McIntosh@Sun.COM  * Solaris file systems handle timestamps different from NTFS. For
156610001SJoyce.McIntosh@Sun.COM  * example when file data is written NTFS doesn't update the timestamps
156710001SJoyce.McIntosh@Sun.COM  * until the file is closed, and then only if they haven't been explicity
156810001SJoyce.McIntosh@Sun.COM  * set via a set attribute request. In order to provide a more similar
156910001SJoyce.McIntosh@Sun.COM  * view of an open file's timestamps, we cache the timestamps in the
157010001SJoyce.McIntosh@Sun.COM  * node and manipulate them in a manner more consistent with windows.
157110001SJoyce.McIntosh@Sun.COM  * (See handling of explicit times and pending timestamps from a write
157210001SJoyce.McIntosh@Sun.COM  * request in smb_node_getattr and smb_node_setattr above.)
157310001SJoyce.McIntosh@Sun.COM  * Timestamps remain cached while there are open ofiles for the node.
157410001SJoyce.McIntosh@Sun.COM  * This includes open ofiles for named streams.
157510001SJoyce.McIntosh@Sun.COM  * n_open_ofiles cannot be used as this doesn't include ofiles opened
157610001SJoyce.McIntosh@Sun.COM  * for the node's named streams. Thus n_timestamps contains a count
157710001SJoyce.McIntosh@Sun.COM  * of open ofiles (t_open_ofiles), including named streams' ofiles,
157810001SJoyce.McIntosh@Sun.COM  * to be used to control timestamp caching.
157910001SJoyce.McIntosh@Sun.COM  *
158010001SJoyce.McIntosh@Sun.COM  * If a node represents a named stream the associated unnamed streams
158110001SJoyce.McIntosh@Sun.COM  * cached timestamps are used instead.
158210001SJoyce.McIntosh@Sun.COM  */
158310001SJoyce.McIntosh@Sun.COM 
158410001SJoyce.McIntosh@Sun.COM /*
158510001SJoyce.McIntosh@Sun.COM  * smb_node_init_cached_timestamps
158610001SJoyce.McIntosh@Sun.COM  *
158710001SJoyce.McIntosh@Sun.COM  * Increment count of open ofiles which are using the cached timestamps.
158810001SJoyce.McIntosh@Sun.COM  * If this is the first open ofile, init the cached timestamps from the
158910001SJoyce.McIntosh@Sun.COM  * file system values.
159010001SJoyce.McIntosh@Sun.COM  */
159110001SJoyce.McIntosh@Sun.COM static void
159210504SKeyur.Desai@Sun.COM smb_node_init_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
159310001SJoyce.McIntosh@Sun.COM {
159410001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
159510001SJoyce.McIntosh@Sun.COM 
159610001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
159710001SJoyce.McIntosh@Sun.COM 		node = unode;
159810001SJoyce.McIntosh@Sun.COM 
159910001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
160010001SJoyce.McIntosh@Sun.COM 	++(node->n_timestamps.t_open_ofiles);
160110001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_open_ofiles == 1) {
160210504SKeyur.Desai@Sun.COM 		node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime;
160310504SKeyur.Desai@Sun.COM 		node->n_timestamps.t_atime = attr->sa_vattr.va_atime;
160410504SKeyur.Desai@Sun.COM 		node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime;
160510504SKeyur.Desai@Sun.COM 		node->n_timestamps.t_crtime = attr->sa_crtime;
160610001SJoyce.McIntosh@Sun.COM 		node->n_timestamps.t_cached = B_TRUE;
160710001SJoyce.McIntosh@Sun.COM 	}
160810001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
160910001SJoyce.McIntosh@Sun.COM }
161010001SJoyce.McIntosh@Sun.COM 
161110001SJoyce.McIntosh@Sun.COM /*
161210001SJoyce.McIntosh@Sun.COM  * smb_node_clear_cached_timestamps
161310001SJoyce.McIntosh@Sun.COM  *
161410001SJoyce.McIntosh@Sun.COM  * Decrement count of open ofiles using the cached timestamps.
161510001SJoyce.McIntosh@Sun.COM  * If the decremented count is zero, clear the cached timestamps.
161610001SJoyce.McIntosh@Sun.COM  */
161710001SJoyce.McIntosh@Sun.COM static void
161810001SJoyce.McIntosh@Sun.COM smb_node_clear_cached_timestamps(smb_node_t *node)
161910001SJoyce.McIntosh@Sun.COM {
162010001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
162110001SJoyce.McIntosh@Sun.COM 
162210001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
162310001SJoyce.McIntosh@Sun.COM 		node = unode;
162410001SJoyce.McIntosh@Sun.COM 
162510001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
162610001SJoyce.McIntosh@Sun.COM 	ASSERT(node->n_timestamps.t_open_ofiles > 0);
162710001SJoyce.McIntosh@Sun.COM 	--(node->n_timestamps.t_open_ofiles);
162810001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_open_ofiles == 0)
162910001SJoyce.McIntosh@Sun.COM 		bzero(&node->n_timestamps, sizeof (smb_times_t));
163010001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
163110001SJoyce.McIntosh@Sun.COM }
163210001SJoyce.McIntosh@Sun.COM 
163310001SJoyce.McIntosh@Sun.COM /*
163410001SJoyce.McIntosh@Sun.COM  * smb_node_get_cached_timestamps
163510001SJoyce.McIntosh@Sun.COM  *
163610001SJoyce.McIntosh@Sun.COM  * Overwrite timestamps in attr with those cached in node.
163710001SJoyce.McIntosh@Sun.COM  */
163810001SJoyce.McIntosh@Sun.COM static void
163910001SJoyce.McIntosh@Sun.COM smb_node_get_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
164010001SJoyce.McIntosh@Sun.COM {
164110001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
164210001SJoyce.McIntosh@Sun.COM 
164310001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
164410001SJoyce.McIntosh@Sun.COM 		node = unode;
164510001SJoyce.McIntosh@Sun.COM 
164610001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
164710001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_cached) {
164810001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_mtime = node->n_timestamps.t_mtime;
164910001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_atime = node->n_timestamps.t_atime;
165010001SJoyce.McIntosh@Sun.COM 		attr->sa_vattr.va_ctime = node->n_timestamps.t_ctime;
165110001SJoyce.McIntosh@Sun.COM 		attr->sa_crtime = node->n_timestamps.t_crtime;
165210001SJoyce.McIntosh@Sun.COM 	}
165310001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
165410001SJoyce.McIntosh@Sun.COM }
165510001SJoyce.McIntosh@Sun.COM 
165610001SJoyce.McIntosh@Sun.COM /*
165710001SJoyce.McIntosh@Sun.COM  * smb_node_set_cached_timestamps
165810001SJoyce.McIntosh@Sun.COM  *
165910001SJoyce.McIntosh@Sun.COM  * Update the node's cached timestamps with values from attr.
166010001SJoyce.McIntosh@Sun.COM  */
166110001SJoyce.McIntosh@Sun.COM static void
166210001SJoyce.McIntosh@Sun.COM smb_node_set_cached_timestamps(smb_node_t *node, smb_attr_t *attr)
166310001SJoyce.McIntosh@Sun.COM {
166410001SJoyce.McIntosh@Sun.COM 	smb_node_t *unode;
166510001SJoyce.McIntosh@Sun.COM 
166610001SJoyce.McIntosh@Sun.COM 	if ((unode = SMB_IS_STREAM(node)) != NULL)
166710001SJoyce.McIntosh@Sun.COM 		node = unode;
166810001SJoyce.McIntosh@Sun.COM 
166910001SJoyce.McIntosh@Sun.COM 	mutex_enter(&node->n_mutex);
167010001SJoyce.McIntosh@Sun.COM 	if (node->n_timestamps.t_cached) {
167110001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_MTIME)
167210001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime;
167310001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_ATIME)
167410001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_atime = attr->sa_vattr.va_atime;
167510001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_CTIME)
167610001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime;
167710001SJoyce.McIntosh@Sun.COM 		if (attr->sa_mask & SMB_AT_CRTIME)
167810001SJoyce.McIntosh@Sun.COM 			node->n_timestamps.t_crtime = attr->sa_crtime;
167910001SJoyce.McIntosh@Sun.COM 	}
168010001SJoyce.McIntosh@Sun.COM 	mutex_exit(&node->n_mutex);
168110001SJoyce.McIntosh@Sun.COM }
1682*11963SAfshin.Ardakani@Sun.COM 
1683*11963SAfshin.Ardakani@Sun.COM /*
1684*11963SAfshin.Ardakani@Sun.COM  * Check to see if the node represents a reparse point.
1685*11963SAfshin.Ardakani@Sun.COM  * If yes, whether the reparse point contains a DFS link.
1686*11963SAfshin.Ardakani@Sun.COM  */
1687*11963SAfshin.Ardakani@Sun.COM static void
1688*11963SAfshin.Ardakani@Sun.COM smb_node_init_reparse(smb_node_t *node, smb_attr_t *attr)
1689*11963SAfshin.Ardakani@Sun.COM {
1690*11963SAfshin.Ardakani@Sun.COM 	nvlist_t *nvl;
1691*11963SAfshin.Ardakani@Sun.COM 	nvpair_t *rec;
1692*11963SAfshin.Ardakani@Sun.COM 	char *rec_type;
1693*11963SAfshin.Ardakani@Sun.COM 
1694*11963SAfshin.Ardakani@Sun.COM 	if ((attr->sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
1695*11963SAfshin.Ardakani@Sun.COM 		return;
1696*11963SAfshin.Ardakani@Sun.COM 
1697*11963SAfshin.Ardakani@Sun.COM 	if ((nvl = reparse_init()) == NULL)
1698*11963SAfshin.Ardakani@Sun.COM 		return;
1699*11963SAfshin.Ardakani@Sun.COM 
1700*11963SAfshin.Ardakani@Sun.COM 	if (reparse_vnode_parse(node->vp, nvl) != 0) {
1701*11963SAfshin.Ardakani@Sun.COM 		reparse_free(nvl);
1702*11963SAfshin.Ardakani@Sun.COM 		return;
1703*11963SAfshin.Ardakani@Sun.COM 	}
1704*11963SAfshin.Ardakani@Sun.COM 
1705*11963SAfshin.Ardakani@Sun.COM 	node->flags |= NODE_FLAGS_REPARSE;
1706*11963SAfshin.Ardakani@Sun.COM 
1707*11963SAfshin.Ardakani@Sun.COM 	rec = nvlist_next_nvpair(nvl, NULL);
1708*11963SAfshin.Ardakani@Sun.COM 	while (rec != NULL) {
1709*11963SAfshin.Ardakani@Sun.COM 		rec_type = nvpair_name(rec);
1710*11963SAfshin.Ardakani@Sun.COM 		if ((rec_type != NULL) &&
1711*11963SAfshin.Ardakani@Sun.COM 		    (strcasecmp(rec_type, DFS_REPARSE_SVCTYPE) == 0)) {
1712*11963SAfshin.Ardakani@Sun.COM 			node->flags |= NODE_FLAGS_DFSLINK;
1713*11963SAfshin.Ardakani@Sun.COM 			break;
1714*11963SAfshin.Ardakani@Sun.COM 		}
1715*11963SAfshin.Ardakani@Sun.COM 		rec = nvlist_next_nvpair(nvl, rec);
1716*11963SAfshin.Ardakani@Sun.COM 	}
1717*11963SAfshin.Ardakani@Sun.COM 
1718*11963SAfshin.Ardakani@Sun.COM 	reparse_free(nvl);
1719*11963SAfshin.Ardakani@Sun.COM }
1720*11963SAfshin.Ardakani@Sun.COM 
1721*11963SAfshin.Ardakani@Sun.COM /*
1722*11963SAfshin.Ardakani@Sun.COM  * smb_node_init_system
1723*11963SAfshin.Ardakani@Sun.COM  *
1724*11963SAfshin.Ardakani@Sun.COM  * If the node represents a special system file set NODE_FLAG_SYSTEM.
1725*11963SAfshin.Ardakani@Sun.COM  * System files:
1726*11963SAfshin.Ardakani@Sun.COM  * - any node whose parent dnode has NODE_FLAG_SYSTEM set
1727*11963SAfshin.Ardakani@Sun.COM  * - any node whose associated unnamed stream node (unode) has
1728*11963SAfshin.Ardakani@Sun.COM  *   NODE_FLAG_SYSTEM set
1729*11963SAfshin.Ardakani@Sun.COM  * - .$EXTEND at root of share (quota management)
1730*11963SAfshin.Ardakani@Sun.COM  */
1731*11963SAfshin.Ardakani@Sun.COM static void
1732*11963SAfshin.Ardakani@Sun.COM smb_node_init_system(smb_node_t *node)
1733*11963SAfshin.Ardakani@Sun.COM {
1734*11963SAfshin.Ardakani@Sun.COM 	smb_node_t *dnode = node->n_dnode;
1735*11963SAfshin.Ardakani@Sun.COM 	smb_node_t *unode = node->n_unode;
1736*11963SAfshin.Ardakani@Sun.COM 
1737*11963SAfshin.Ardakani@Sun.COM 	if ((dnode) && (dnode->flags & NODE_FLAGS_SYSTEM)) {
1738*11963SAfshin.Ardakani@Sun.COM 		node->flags |= NODE_FLAGS_SYSTEM;
1739*11963SAfshin.Ardakani@Sun.COM 		return;
1740*11963SAfshin.Ardakani@Sun.COM 	}
1741*11963SAfshin.Ardakani@Sun.COM 
1742*11963SAfshin.Ardakani@Sun.COM 	if ((unode) && (unode->flags & NODE_FLAGS_SYSTEM)) {
1743*11963SAfshin.Ardakani@Sun.COM 		node->flags |= NODE_FLAGS_SYSTEM;
1744*11963SAfshin.Ardakani@Sun.COM 		return;
1745*11963SAfshin.Ardakani@Sun.COM 	}
1746*11963SAfshin.Ardakani@Sun.COM 
1747*11963SAfshin.Ardakani@Sun.COM 	if ((dnode) && (smb_node_is_vfsroot(node->n_dnode) &&
1748*11963SAfshin.Ardakani@Sun.COM 	    (strcasecmp(node->od_name, ".$EXTEND") == 0))) {
1749*11963SAfshin.Ardakani@Sun.COM 		node->flags |= NODE_FLAGS_SYSTEM;
1750*11963SAfshin.Ardakani@Sun.COM 	}
1751*11963SAfshin.Ardakani@Sun.COM }
1752