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 /* 22*8670SJose.Borrego@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 235331Samw * Use is subject to license terms. 245331Samw */ 255331Samw 267348SJose.Borrego@Sun.COM #pragma ident "@(#)smb_node.c 1.9 08/08/07 SMI" 275331Samw 285331Samw /* 295331Samw * SMB Node State Machine 305331Samw * ---------------------- 315331Samw * 325331Samw * +----------------------------+ T0 335331Samw * | SMB_NODE_STATE_AVAILABLE |<----------- Creation/Allocation 345331Samw * +----------------------------+ 355331Samw * | 365331Samw * | T1 375331Samw * | 385331Samw * v 395331Samw * +-----------------------------+ T2 405331Samw * | SMB_NODE_STATE_DESTROYING |----------> Deletion/Free 415331Samw * +-----------------------------+ 425331Samw * 435331Samw * Transition T0 445331Samw * 455331Samw * This transition occurs in smb_node_lookup(). If the node looked for is 465331Samw * not found in the has table a new node is created. The reference count is 475331Samw * initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE. 485331Samw * 495331Samw * Transition T1 505331Samw * 515331Samw * This transition occurs in smb_node_release(). If the reference count 525331Samw * drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more 535331Samw * reference count will be given out for that node. 545331Samw * 555331Samw * Transition T2 565331Samw * 575331Samw * This transition occurs in smb_node_release(). The structure is deleted. 585331Samw * 595331Samw * Comments 605331Samw * -------- 615331Samw * 625331Samw * The reason the smb node has 2 states is the following synchronization 635331Samw * rule: 645331Samw * 655331Samw * There's a mutex embedded in the node used to protect its fields and 665331Samw * there's a lock embedded in the bucket of the hash table the node belongs 675331Samw * to. To increment or to decrement the reference count the mutex must be 685331Samw * entered. To insert the node into the bucket and to remove it from the 695331Samw * bucket the lock must be entered in RW_WRITER mode. When both (mutex and 705331Samw * lock) have to be entered, the lock has always to be entered first then 715331Samw * the mutex. This prevents a deadlock between smb_node_lookup() and 725331Samw * smb_node_release() from occurring. However, in smb_node_release() when the 735331Samw * reference count drops to zero and triggers the deletion of the node, the 745331Samw * mutex has to be released before entering the lock of the bucket (to 755331Samw * remove the node). This creates a window during which the node that is 765331Samw * about to be freed could be given out by smb_node_lookup(). To close that 775331Samw * window the node is moved to the state SMB_NODE_STATE_DESTROYING before 785331Samw * releasing the mutex. That way, even if smb_node_lookup() finds it, the 795331Samw * state will indicate that the node should be treated as non existent (of 805331Samw * course the state of the node should be tested/updated under the 815331Samw * protection of the mutex). 825331Samw */ 835331Samw #include <smbsrv/smb_incl.h> 845331Samw #include <smbsrv/smb_fsops.h> 855331Samw #include <sys/pathname.h> 865331Samw #include <sys/sdt.h> 875772Sas200622 #include <sys/nbmlock.h> 885331Samw 895331Samw uint32_t smb_is_executable(char *path); 905331Samw static void smb_node_delete_on_close(smb_node_t *node); 915331Samw 925331Samw #define VALIDATE_DIR_NODE(_dir_, _node_) \ 935331Samw ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \ 945331Samw ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \ 955331Samw ASSERT((_dir_)->dir_snode != (_node_)); 965331Samw 976139Sjb150015 static boolean_t smb_node_initialized = B_FALSE; 986139Sjb150015 static smb_llist_t smb_node_hash_table[SMBND_HASH_MASK+1]; 996139Sjb150015 1006139Sjb150015 /* 1016139Sjb150015 * smb_node_init 1026139Sjb150015 * 1036139Sjb150015 * Initialization of the SMB node layer. 1046139Sjb150015 * 1056139Sjb150015 * This function is not multi-thread safe. The caller must make sure only one 1066139Sjb150015 * thread makes the call. 1076139Sjb150015 */ 1086139Sjb150015 int 1096139Sjb150015 smb_node_init(void) 1106139Sjb150015 { 1116139Sjb150015 int i; 1126139Sjb150015 1136139Sjb150015 if (smb_node_initialized) 1146139Sjb150015 return (0); 1156139Sjb150015 1166139Sjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 1176139Sjb150015 smb_llist_constructor(&smb_node_hash_table[i], 1186139Sjb150015 sizeof (smb_node_t), offsetof(smb_node_t, n_lnd)); 1196139Sjb150015 } 1206139Sjb150015 smb_node_initialized = B_TRUE; 1216139Sjb150015 return (0); 1226139Sjb150015 } 1236139Sjb150015 1246139Sjb150015 /* 1256139Sjb150015 * smb_node_fini 1266139Sjb150015 * 1276139Sjb150015 * This function is not multi-thread safe. The caller must make sure only one 1286139Sjb150015 * thread makes the call. 1296139Sjb150015 */ 1306139Sjb150015 void 1316139Sjb150015 smb_node_fini(void) 1326139Sjb150015 { 1336139Sjb150015 int i; 1346139Sjb150015 1356139Sjb150015 if (!smb_node_initialized) 1366139Sjb150015 return; 1376139Sjb150015 1386139Sjb150015 #ifdef DEBUG 1396139Sjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 1406139Sjb150015 smb_node_t *node; 1416139Sjb150015 1426139Sjb150015 /* 1436139Sjb150015 * The following sequence is just intended for sanity check. 1446139Sjb150015 * This will have to be modified when the code goes into 1456139Sjb150015 * production. 1466139Sjb150015 * 1476139Sjb150015 * The SMB node hash table should be emtpy at this point. If the 1486139Sjb150015 * hash table is not empty a panic will be triggered. 1496139Sjb150015 * 1506139Sjb150015 * The reason why SMB nodes are still remaining in the hash 1516139Sjb150015 * table is problably due to a mismatch between calls to 1526139Sjb150015 * smb_node_lookup() and smb_node_release(). You must track that 1536139Sjb150015 * down. 1546139Sjb150015 */ 1556139Sjb150015 node = smb_llist_head(&smb_node_hash_table[i]); 1566139Sjb150015 ASSERT(node == NULL); 1576139Sjb150015 } 1586139Sjb150015 #endif 1596139Sjb150015 1606139Sjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 1616139Sjb150015 smb_llist_destructor(&smb_node_hash_table[i]); 1626139Sjb150015 } 1636139Sjb150015 smb_node_initialized = B_FALSE; 1646139Sjb150015 } 1656139Sjb150015 1665331Samw /* 1675331Samw * smb_node_lookup() 1685331Samw * 1695331Samw * NOTE: This routine should only be called by the file system interface layer, 1705331Samw * and not by SMB. 1715331Samw * 1725331Samw * smb_node_lookup() is called upon successful lookup, mkdir, and create 1735331Samw * (for both non-streams and streams). In each of these cases, a held vnode is 174*8670SJose.Borrego@Sun.COM * passed into this routine. If a new smb_node is created it will take its 175*8670SJose.Borrego@Sun.COM * own hold on the vnode. The caller's hold therefore still belongs to, and 176*8670SJose.Borrego@Sun.COM * should be released by, the caller. 1775331Samw * 1785331Samw * A reference is taken on the smb_node whether found in the hash table 1795331Samw * or newly created. 1805331Samw * 1815331Samw * If an smb_node needs to be created, a reference is also taken on the 1825331Samw * dir_snode (if passed in). 1835331Samw * 1845331Samw * See smb_node_release() for details on the release of these references. 1855331Samw */ 1865331Samw 1875331Samw /*ARGSUSED*/ 1885331Samw smb_node_t * 1895331Samw smb_node_lookup( 1905331Samw struct smb_request *sr, 1915331Samw struct open_param *op, 1925331Samw cred_t *cred, 1935331Samw vnode_t *vp, 1945331Samw char *od_name, 1955331Samw smb_node_t *dir_snode, 1965331Samw smb_node_t *unnamed_node, 1975331Samw smb_attr_t *attr) 1985331Samw { 1995331Samw smb_llist_t *node_hdr; 2005331Samw smb_node_t *node; 2015331Samw uint32_t hashkey = 0; 2027348SJose.Borrego@Sun.COM fsid_t fsid; 2035331Samw int error; 2045331Samw krw_t lock_mode; 2055331Samw vnode_t *unnamed_vp = NULL; 2065331Samw 2075331Samw /* 2085331Samw * smb_vop_getattr() is called here instead of smb_fsop_getattr(), 2095331Samw * because the node may not yet exist. We also do not want to call 2105331Samw * it with the list lock held. 2115331Samw */ 2125331Samw 2135331Samw if (unnamed_node) 2145331Samw unnamed_vp = unnamed_node->vp; 2155331Samw 2165331Samw /* 2175331Samw * This getattr is performed on behalf of the server 2185331Samw * that's why kcred is used not the user's cred 2195331Samw */ 2205331Samw attr->sa_mask = SMB_AT_ALL; 2215772Sas200622 error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred); 2225331Samw if (error) 2235331Samw return (NULL); 2245331Samw 2257348SJose.Borrego@Sun.COM if (sr && sr->tid_tree) { 2267348SJose.Borrego@Sun.COM /* 2277348SJose.Borrego@Sun.COM * The fsid for a file is that of the tree, even 2287348SJose.Borrego@Sun.COM * if the file resides in a different mountpoint 2297348SJose.Borrego@Sun.COM * under the share. 2307348SJose.Borrego@Sun.COM */ 2317348SJose.Borrego@Sun.COM fsid = SMB_TREE_FSID(sr->tid_tree); 2325331Samw } else { 2337348SJose.Borrego@Sun.COM /* 2347348SJose.Borrego@Sun.COM * This should be getting executed only for the 2357348SJose.Borrego@Sun.COM * tree root smb_node. 2367348SJose.Borrego@Sun.COM */ 2377348SJose.Borrego@Sun.COM fsid = vp->v_vfsp->vfs_fsid; 2385331Samw } 2395331Samw 2407348SJose.Borrego@Sun.COM hashkey = fsid.val[0] + attr->sa_vattr.va_nodeid; 2415331Samw hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 2426139Sjb150015 node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]; 2435331Samw lock_mode = RW_READER; 2445331Samw 2455331Samw smb_llist_enter(node_hdr, lock_mode); 2465331Samw for (;;) { 2475331Samw node = list_head(&node_hdr->ll_list); 2485331Samw while (node) { 2495331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 2505331Samw ASSERT(node->n_hash_bucket == node_hdr); 2515331Samw if ((node->n_hashkey == hashkey) && (node->vp == vp)) { 2525331Samw smb_rwx_xenter(&node->n_lock); 2535331Samw DTRACE_PROBE1(smb_node_lookup_hit, 2545331Samw smb_node_t *, node); 2555331Samw switch (node->n_state) { 2565331Samw case SMB_NODE_STATE_AVAILABLE: 2575331Samw /* The node was found. */ 2585331Samw node->n_refcnt++; 2595331Samw if ((node->dir_snode == NULL) && 2605331Samw (dir_snode != NULL) && 2615331Samw (strcmp(od_name, "..") != 0) && 2625331Samw (strcmp(od_name, ".") != 0)) { 2635331Samw VALIDATE_DIR_NODE(dir_snode, 2645331Samw node); 2655331Samw node->dir_snode = dir_snode; 2665331Samw smb_node_ref(dir_snode); 2675331Samw } 2685331Samw node->attr = *attr; 2695772Sas200622 node->n_size = attr->sa_vattr.va_size; 2705331Samw 2715331Samw smb_audit_node(node); 2725331Samw smb_rwx_xexit(&node->n_lock); 2735331Samw smb_llist_exit(node_hdr); 2745331Samw return (node); 2755331Samw 2765331Samw case SMB_NODE_STATE_DESTROYING: 2775331Samw /* 2785331Samw * Although the node exists it is about 2795331Samw * to be destroyed. We act as it hasn't 2805331Samw * been found. 2815331Samw */ 2825331Samw smb_rwx_xexit(&node->n_lock); 2835331Samw break; 2845331Samw default: 2855331Samw /* 2865331Samw * Although the node exists it is in an 2875331Samw * unknown state. We act as it hasn't 2885331Samw * been found. 2895331Samw */ 2905331Samw ASSERT(0); 2915331Samw smb_rwx_xexit(&node->n_lock); 2925331Samw break; 2935331Samw } 2945331Samw } 2955331Samw node = smb_llist_next(node_hdr, node); 2965331Samw } 2975331Samw if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) { 2985331Samw lock_mode = RW_WRITER; 2995331Samw continue; 3005331Samw } 3015331Samw break; 3025331Samw } 3036139Sjb150015 node = kmem_cache_alloc(sr->sr_server->si_cache_node, KM_SLEEP); 3045331Samw bzero(node, sizeof (smb_node_t)); 3055331Samw 3065331Samw node->n_state = SMB_NODE_STATE_AVAILABLE; 3075331Samw node->n_hash_bucket = node_hdr; 3086139Sjb150015 node->n_sr = sr; 3096139Sjb150015 node->vp = vp; 310*8670SJose.Borrego@Sun.COM VN_HOLD(node->vp); 3116139Sjb150015 node->n_hashkey = hashkey; 3126139Sjb150015 node->n_refcnt = 1; 3136139Sjb150015 node->attr = *attr; 3146139Sjb150015 node->flags |= NODE_FLAGS_ATTR_VALID; 3156139Sjb150015 node->n_size = node->attr.sa_vattr.va_size; 3166139Sjb150015 node->n_orig_session_id = sr->session->s_kid; 3176139Sjb150015 node->n_orig_uid = crgetuid(sr->user_cr); 3186139Sjb150015 node->n_cache = sr->sr_server->si_cache_node; 3195331Samw 3206139Sjb150015 ASSERT(od_name); 3216139Sjb150015 (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); 3226139Sjb150015 3235331Samw smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 3245331Samw offsetof(smb_ofile_t, f_nnd)); 3255331Samw smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 3265331Samw offsetof(smb_lock_t, l_lnd)); 3275331Samw 3285331Samw 3295967Scp160787 if (strcmp(od_name, XATTR_DIR) == 0) 3305967Scp160787 node->flags |= NODE_XATTR_DIR; 3315331Samw if (op) 3325331Samw node->flags |= smb_is_executable(op->fqi.last_comp); 3335331Samw 3345331Samw if (dir_snode) { 3355331Samw smb_node_ref(dir_snode); 3365331Samw node->dir_snode = dir_snode; 3375331Samw ASSERT(dir_snode->dir_snode != node); 3385331Samw ASSERT((dir_snode->vp->v_xattrdir) || 3395331Samw (dir_snode->vp->v_type == VDIR)); 3405331Samw } 3415331Samw 3425331Samw if (unnamed_node) { 3435331Samw smb_node_ref(unnamed_node); 3445331Samw node->unnamed_stream_node = unnamed_node; 3455331Samw } 3465331Samw 3475331Samw smb_rwx_init(&node->n_lock); 3485331Samw node->n_magic = SMB_NODE_MAGIC; 3495331Samw smb_audit_buf_node_create(node); 3505331Samw DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); 3515331Samw smb_audit_node(node); 3525331Samw smb_llist_insert_head(node_hdr, node); 3536139Sjb150015 3545331Samw smb_llist_exit(node_hdr); 3555331Samw return (node); 3565331Samw } 3575331Samw 3585331Samw /* 3595331Samw * smb_stream_node_lookup() 3605331Samw * 3615331Samw * Note: stream_name (the name that will be stored in the "od_name" field 3625331Samw * of a stream's smb_node) is the same as the on-disk name for the stream 3635331Samw * except that it does not have SMB_STREAM_PREFIX prepended. 3645331Samw */ 3655331Samw 3665331Samw smb_node_t * 3675331Samw smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, 3685331Samw vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr) 3695331Samw { 3705331Samw smb_node_t *xattrdir_node; 3715331Samw smb_node_t *snode; 3725331Samw smb_attr_t tmp_attr; 3735331Samw 3745331Samw xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, 3755331Samw fnode, NULL, &tmp_attr); 3765331Samw 3775331Samw if (xattrdir_node == NULL) 3785331Samw return (NULL); 3795331Samw 3805331Samw snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, 3815331Samw fnode, ret_attr); 3825331Samw 3835331Samw (void) smb_node_release(xattrdir_node); 3845331Samw return (snode); 3855331Samw } 3865331Samw 3875331Samw 3885331Samw /* 3895331Samw * This function should be called whenever a reference is needed on an 3905331Samw * smb_node pointer. The copy of an smb_node pointer from one non-local 3915331Samw * data structure to another requires a reference to be taken on the smb_node 3925331Samw * (unless the usage is localized). Each data structure deallocation routine 3935331Samw * will call smb_node_release() on its smb_node pointers. 3945331Samw * 3955331Samw * In general, an smb_node pointer residing in a structure should never be 3965331Samw * stale. A node pointer may be NULL, however, and care should be taken 3975331Samw * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. 3985331Samw * Care also needs to be taken with respect to racing deallocations of a 3995331Samw * structure. 4005331Samw */ 4015331Samw 4025331Samw void 4035331Samw smb_node_ref(smb_node_t *node) 4045331Samw { 4055331Samw ASSERT(node); 4065331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 4075331Samw ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 4085331Samw 4095331Samw smb_rwx_xenter(&node->n_lock); 4105331Samw node->n_refcnt++; 4115331Samw ASSERT(node->n_refcnt); 4125331Samw DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); 4135331Samw smb_audit_node(node); 4145331Samw smb_rwx_xexit(&node->n_lock); 4155331Samw } 4165331Samw 4175331Samw /* 4185331Samw * smb_node_lookup() takes a hold on an smb_node, whether found in the 4195331Samw * hash table or newly created. This hold is expected to be released 4205331Samw * in the following manner. 4215331Samw * 4225331Samw * smb_node_lookup() takes an address of an smb_node pointer. This should 4235331Samw * be getting passed down via a lookup (whether path name or component), mkdir, 4245331Samw * create. If the original smb_node pointer resides in a data structure, then 4255331Samw * the deallocation routine for the data structure is responsible for calling 4265331Samw * smb_node_release() on the smb_node pointer. Alternatively, 4275331Samw * smb_node_release() can be called as soon as the smb_node pointer is no longer 4285331Samw * needed. In this case, callers are responsible for setting an embedded 4295331Samw * pointer to NULL if it is known that the last reference is being released. 4305331Samw * 4315331Samw * If the passed-in address of the smb_node pointer belongs to a local variable, 4325331Samw * then the caller with the local variable should call smb_node_release() 4335331Samw * directly. 4345331Samw * 4355331Samw * smb_node_release() itself will call smb_node_release() on a node's dir_snode, 4365331Samw * as smb_node_lookup() takes a hold on dir_snode. 4375331Samw */ 4385331Samw void 4395331Samw smb_node_release(smb_node_t *node) 4405331Samw { 4415331Samw ASSERT(node); 4425331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 4435331Samw 4445331Samw smb_rwx_xenter(&node->n_lock); 4455331Samw ASSERT(node->n_refcnt); 4465331Samw DTRACE_PROBE1(smb_node_release, smb_node_t *, node); 4475331Samw if (--node->n_refcnt == 0) { 4485331Samw switch (node->n_state) { 4495331Samw 4505331Samw case SMB_NODE_STATE_AVAILABLE: 4515331Samw node->n_state = SMB_NODE_STATE_DESTROYING; 4525331Samw smb_rwx_xexit(&node->n_lock); 4535331Samw 4545331Samw smb_llist_enter(node->n_hash_bucket, RW_WRITER); 4555331Samw smb_llist_remove(node->n_hash_bucket, node); 4565331Samw smb_llist_exit(node->n_hash_bucket); 4575331Samw 4585331Samw /* 4595331Samw * Check if the file was deleted 4605331Samw */ 4615331Samw smb_node_delete_on_close(node); 4625331Samw node->n_magic = (uint32_t)~SMB_NODE_MAGIC; 4635331Samw 4645331Samw /* These lists should be empty. */ 4655331Samw smb_llist_destructor(&node->n_ofile_list); 4665331Samw smb_llist_destructor(&node->n_lock_list); 4675331Samw 4685331Samw if (node->dir_snode) { 4695331Samw ASSERT(node->dir_snode->n_magic == 4705331Samw SMB_NODE_MAGIC); 4715331Samw smb_node_release(node->dir_snode); 4725331Samw } 4735331Samw 4745331Samw if (node->unnamed_stream_node) { 4755331Samw ASSERT(node->unnamed_stream_node->n_magic == 4765331Samw SMB_NODE_MAGIC); 4775331Samw smb_node_release(node->unnamed_stream_node); 4785331Samw } 4795331Samw 4805331Samw ASSERT(node->vp); 4815331Samw VN_RELE(node->vp); 4825331Samw 4835331Samw smb_audit_buf_node_destroy(node); 4845331Samw smb_rwx_destroy(&node->n_lock); 4856139Sjb150015 kmem_cache_free(node->n_cache, node); 4865331Samw return; 4875331Samw 4885331Samw default: 4895331Samw ASSERT(0); 4905331Samw break; 4915331Samw } 4925331Samw } 4935331Samw smb_audit_node(node); 4945331Samw smb_rwx_xexit(&node->n_lock); 4955331Samw } 4965331Samw 4975331Samw static void 4985331Samw smb_node_delete_on_close(smb_node_t *node) 4995331Samw { 5005331Samw smb_node_t *d_snode; 5015331Samw int rc = 0; 5025331Samw 5035331Samw d_snode = node->dir_snode; 5045331Samw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 5055331Samw 5065331Samw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 5075331Samw ASSERT(node->od_name != NULL); 5085331Samw if (node->attr.sa_vattr.va_type == VDIR) 5095331Samw rc = smb_fsop_rmdir(0, node->delete_on_close_cred, 5105331Samw d_snode, node->od_name, 1); 5115331Samw else 5125331Samw rc = smb_fsop_remove(0, node->delete_on_close_cred, 5135331Samw d_snode, node->od_name, 1); 5145331Samw smb_cred_rele(node->delete_on_close_cred); 5155331Samw } 5165331Samw if (rc != 0) 5175331Samw cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", 5185331Samw node->od_name, rc); 5195331Samw DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); 5205331Samw } 5215331Samw 5225331Samw /* 5235331Samw * smb_node_rename() 5245331Samw * 5255331Samw */ 5265331Samw int 5275331Samw smb_node_rename( 5285331Samw smb_node_t *from_dir_snode, 5295331Samw smb_node_t *ret_snode, 5305331Samw smb_node_t *to_dir_snode, 5315331Samw char *to_name) 5325331Samw { 5335331Samw ASSERT(from_dir_snode); 5345331Samw ASSERT(to_dir_snode); 5355331Samw ASSERT(ret_snode); 5365331Samw ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); 5375331Samw ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); 5385331Samw ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC); 5395331Samw ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5405331Samw ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5415331Samw ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5425331Samw 5435331Samw smb_node_ref(to_dir_snode); 5445331Samw smb_rwx_xenter(&ret_snode->n_lock); 5455331Samw ret_snode->dir_snode = to_dir_snode; 5465331Samw smb_rwx_xexit(&ret_snode->n_lock); 5475331Samw ASSERT(to_dir_snode->dir_snode != ret_snode); 5485331Samw ASSERT((to_dir_snode->vp->v_xattrdir) || 5495331Samw (to_dir_snode->vp->v_type == VDIR)); 5505331Samw smb_node_release(from_dir_snode); 5515331Samw 5525331Samw (void) strcpy(ret_snode->od_name, to_name); 5535331Samw 5545331Samw /* 5555331Samw * XXX Need to update attributes? 5565331Samw */ 5575331Samw 5585331Samw return (0); 5595331Samw } 5605331Samw 5615331Samw int 5626139Sjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root) 5635331Samw { 5646139Sjb150015 smb_attr_t va; 5656139Sjb150015 int error; 5666139Sjb150015 uint32_t hashkey; 5676139Sjb150015 smb_llist_t *node_hdr; 5686139Sjb150015 smb_node_t *node; 5695331Samw 5705331Samw /* 5715331Samw * Take an explicit hold on rootdir. This goes with the 5725331Samw * corresponding release in smb_node_root_fini()/smb_node_release(). 5735331Samw */ 5746139Sjb150015 VN_HOLD(vp); 5755331Samw 5766139Sjb150015 va.sa_mask = SMB_AT_ALL; 5776139Sjb150015 error = smb_vop_getattr(vp, NULL, &va, 0, kcred); 5786139Sjb150015 if (error) { 5796139Sjb150015 VN_RELE(vp); 5806139Sjb150015 return (error); 5816139Sjb150015 } 5826139Sjb150015 5836139Sjb150015 hashkey = vp->v_vfsp->vfs_fsid.val[0] + va.sa_vattr.va_nodeid; 5846139Sjb150015 hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 5856139Sjb150015 node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]; 5866139Sjb150015 5876139Sjb150015 node = kmem_cache_alloc(sv->si_cache_node, KM_SLEEP); 5886139Sjb150015 bzero(node, sizeof (smb_node_t)); 5895331Samw 5906139Sjb150015 node->n_state = SMB_NODE_STATE_AVAILABLE; 5916139Sjb150015 node->n_hash_bucket = node_hdr; 5926139Sjb150015 node->vp = vp; 5936139Sjb150015 node->n_hashkey = hashkey; 5946139Sjb150015 node->n_refcnt = 1; 5956139Sjb150015 node->attr = va; 5966139Sjb150015 node->flags |= NODE_FLAGS_ATTR_VALID; 5976139Sjb150015 node->n_size = node->attr.sa_vattr.va_size; 5986139Sjb150015 node->n_cache = sv->si_cache_node; 5996139Sjb150015 (void) strlcpy(node->od_name, ROOTVOL, sizeof (node->od_name)); 6006139Sjb150015 6016139Sjb150015 smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 6026139Sjb150015 offsetof(smb_ofile_t, f_nnd)); 6036139Sjb150015 smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 6046139Sjb150015 offsetof(smb_lock_t, l_lnd)); 6056139Sjb150015 6066139Sjb150015 smb_rwx_init(&node->n_lock); 6076139Sjb150015 node->n_magic = SMB_NODE_MAGIC; 6086139Sjb150015 smb_audit_buf_node_create(node); 6096139Sjb150015 6106139Sjb150015 sv->si_root_smb_node = node; 6116139Sjb150015 6126139Sjb150015 smb_audit_node(node); 6136139Sjb150015 smb_llist_enter(node_hdr, RW_WRITER); 6146139Sjb150015 smb_llist_insert_head(node_hdr, node); 6156139Sjb150015 smb_llist_exit(node_hdr); 6166139Sjb150015 6176139Sjb150015 *root = node; 6186139Sjb150015 6196139Sjb150015 return (0); 6205331Samw } 6215331Samw 6225331Samw /* 6235331Samw * smb_node_get_size 6245331Samw */ 6256432Sas200622 u_offset_t 6266432Sas200622 smb_node_get_size(smb_node_t *node, smb_attr_t *attr) 6275331Samw { 6286432Sas200622 u_offset_t size; 6295331Samw 6305331Samw if (attr->sa_vattr.va_type == VDIR) 6315331Samw return (0); 6325331Samw 6335331Samw smb_rwx_xenter(&node->n_lock); 6345331Samw if (node && (node->flags & NODE_FLAGS_SET_SIZE)) 6355331Samw size = node->n_size; 6365331Samw else 6375331Samw size = attr->sa_vattr.va_size; 6385331Samw smb_rwx_xexit(&node->n_lock); 6395331Samw return (size); 6405331Samw } 6415331Samw 6425331Samw static int 6435331Samw timeval_cmp(timestruc_t *a, timestruc_t *b) 6445331Samw { 6455331Samw if (a->tv_sec < b->tv_sec) 6465331Samw return (-1); 6475331Samw if (a->tv_sec > b->tv_sec) 6485331Samw return (1); 6495331Samw /* Seconds are equal compare tv_nsec */ 6505331Samw if (a->tv_nsec < b->tv_nsec) 6515331Samw return (-1); 6525331Samw return (a->tv_nsec > b->tv_nsec); 6535331Samw } 6545331Samw 6555331Samw /* 6565331Samw * smb_node_set_time 6575331Samw * 6585331Samw * This function will update the time stored in the node and 6597348SJose.Borrego@Sun.COM * set the appropriate flags. If there is nothing to update, 6607348SJose.Borrego@Sun.COM * the function will return without any updates. The update 6617348SJose.Borrego@Sun.COM * is only in the node level and the attribute in the file system 6627348SJose.Borrego@Sun.COM * will be updated when client close the file. 6635331Samw */ 6645331Samw void 6655331Samw smb_node_set_time(struct smb_node *node, struct timestruc *crtime, 6665331Samw struct timestruc *mtime, struct timestruc *atime, 6675331Samw struct timestruc *ctime, unsigned int what) 6685331Samw { 6697348SJose.Borrego@Sun.COM if (what == 0) 6705331Samw return; 6715331Samw 6725331Samw if ((what & SMB_AT_CRTIME && crtime == 0) || 6735331Samw (what & SMB_AT_MTIME && mtime == 0) || 6745331Samw (what & SMB_AT_ATIME && atime == 0) || 6757348SJose.Borrego@Sun.COM (what & SMB_AT_CTIME && ctime == 0)) 6765331Samw return; 6777348SJose.Borrego@Sun.COM 6787348SJose.Borrego@Sun.COM smb_rwx_xenter(&node->n_lock); 6795331Samw 6805331Samw if ((what & SMB_AT_CRTIME) && 6815331Samw timeval_cmp((timestruc_t *)&node->attr.sa_crtime, 6825331Samw crtime) != 0) { 6835331Samw node->what |= SMB_AT_CRTIME; 6845331Samw node->attr.sa_crtime = *((timestruc_t *)crtime); 6855331Samw } 6865331Samw 6875331Samw if ((what & SMB_AT_MTIME) && 6885331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime, 6895331Samw mtime) != 0) { 6905331Samw node->what |= SMB_AT_MTIME; 6915331Samw node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime); 6925331Samw } 6935331Samw 6945331Samw if ((what & SMB_AT_ATIME) && 6955331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime, 6965331Samw atime) != 0) { 6975331Samw node->what |= SMB_AT_ATIME; 6985331Samw node->attr.sa_vattr.va_atime = *((timestruc_t *)atime); 6995331Samw } 7005331Samw 7015331Samw /* 7025331Samw * The ctime handling is trickier. It has three scenarios. 7035331Samw * 1. Only ctime need to be set and it is the same as the ctime 7045331Samw * stored in the node. (update not necessary) 7055331Samw * 2. The ctime is the same as the ctime stored in the node but 7065331Samw * is not the only time need to be set. (update required) 7075331Samw * 3. The ctime need to be set and is not the same as the ctime 7085331Samw * stored in the node. (update required) 7095331Samw * Unlike other time setting, the ctime needs to be set even when 7105331Samw * it is the same as the ctime in the node if there are other time 7115331Samw * needs to be set (#2). This will ensure the ctime not being 7125331Samw * updated when other times are being updated in the file system. 7135331Samw * 7145331Samw * Retained file rules: 7155331Samw * 7165331Samw * 1. Don't add SMB_AT_CTIME to node->what by default because the 7175331Samw * request will be rejected by filesystem 7185331Samw * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e. 7195331Samw * any request for changing ctime on these files should have 7205331Samw * been already rejected 7215331Samw */ 7225331Samw node->what |= SMB_AT_CTIME; 7235331Samw if (what & SMB_AT_CTIME) { 7245331Samw if ((what == SMB_AT_CTIME) && 7255331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime, 7265331Samw ctime) == 0) { 7275331Samw node->what &= ~SMB_AT_CTIME; 7285331Samw } else { 7295331Samw gethrestime(&node->attr.sa_vattr.va_ctime); 7305331Samw } 7315331Samw } else { 7325331Samw gethrestime(&node->attr.sa_vattr.va_ctime); 7335331Samw } 7345331Samw smb_rwx_xexit(&node->n_lock); 7355331Samw } 7365331Samw 7375331Samw 7385331Samw timestruc_t * 7395331Samw smb_node_get_crtime(smb_node_t *node) 7405331Samw { 7415331Samw return ((timestruc_t *)&node->attr.sa_crtime); 7425331Samw } 7435331Samw 7445331Samw timestruc_t * 7455331Samw smb_node_get_atime(smb_node_t *node) 7465331Samw { 7475331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_atime); 7485331Samw } 7495331Samw 7505331Samw timestruc_t * 7515331Samw smb_node_get_ctime(smb_node_t *node) 7525331Samw { 7535331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_ctime); 7545331Samw } 7555331Samw 7565331Samw timestruc_t * 7575331Samw smb_node_get_mtime(smb_node_t *node) 7585331Samw { 7595331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_mtime); 7605331Samw } 7615331Samw 7625331Samw /* 7635331Samw * smb_node_set_dosattr 7645331Samw * 7655331Samw * Parse the specified DOS attributes and, if they have been modified, 7665331Samw * update the node cache. This call should be followed by a 7675331Samw * smb_sync_fsattr() call to write the attribute changes to filesystem. 7685331Samw */ 7695331Samw void 7707052Samw smb_node_set_dosattr(smb_node_t *node, uint32_t dosattr) 7715331Samw { 7727052Samw uint32_t mode = dosattr & (FILE_ATTRIBUTE_ARCHIVE | 7737052Samw FILE_ATTRIBUTE_READONLY | 7747052Samw FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); 7755331Samw 7765331Samw smb_rwx_xenter(&node->n_lock); 7775331Samw if (node->attr.sa_dosattr != mode) { 7785331Samw node->attr.sa_dosattr = mode; 7795331Samw node->what |= SMB_AT_DOSATTR; 7805331Samw } 7815331Samw smb_rwx_xexit(&node->n_lock); 7825331Samw } 7835331Samw 7845331Samw /* 7857348SJose.Borrego@Sun.COM * smb_node_get_dosattr() 7865331Samw * 7877348SJose.Borrego@Sun.COM * This function is used to provide clients with information as to whether 7887348SJose.Borrego@Sun.COM * the readonly bit is set. Hence both the node attribute cache (which 7897348SJose.Borrego@Sun.COM * reflects the on-disk attributes) and node->readonly_creator (which 7907348SJose.Borrego@Sun.COM * reflects whether a readonly set is pending from a readonly create) are 7917348SJose.Borrego@Sun.COM * checked. In the latter case, the readonly attribute should be visible to 7927348SJose.Borrego@Sun.COM * all clients even though the readonly creator fid is immune to the readonly 7937348SJose.Borrego@Sun.COM * bit until close. 7945331Samw */ 7957348SJose.Borrego@Sun.COM 7965331Samw uint32_t 7975331Samw smb_node_get_dosattr(smb_node_t *node) 7985331Samw { 7997348SJose.Borrego@Sun.COM uint32_t dosattr = node->attr.sa_dosattr; 8007348SJose.Borrego@Sun.COM 8017348SJose.Borrego@Sun.COM if (node->readonly_creator) 8027348SJose.Borrego@Sun.COM dosattr |= FILE_ATTRIBUTE_READONLY; 8037348SJose.Borrego@Sun.COM 8047348SJose.Borrego@Sun.COM if (!dosattr) 8057348SJose.Borrego@Sun.COM dosattr = FILE_ATTRIBUTE_NORMAL; 8067348SJose.Borrego@Sun.COM 8077348SJose.Borrego@Sun.COM return (dosattr); 8085331Samw } 8095331Samw 8105331Samw int 8115331Samw smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr) 8125331Samw { 8135331Samw int rc = -1; 8145331Samw 8155331Samw smb_rwx_xenter(&node->n_lock); 8165331Samw if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) && 8175331Samw !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) { 8185331Samw crhold(cr); 8195331Samw node->delete_on_close_cred = cr; 8205331Samw node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; 8215331Samw rc = 0; 8225331Samw } 8235331Samw smb_rwx_xexit(&node->n_lock); 8245331Samw return (rc); 8255331Samw } 8265331Samw 8275331Samw void 8285331Samw smb_node_reset_delete_on_close(smb_node_t *node) 8295331Samw { 8305331Samw smb_rwx_xenter(&node->n_lock); 8315331Samw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 8325331Samw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 8335331Samw crfree(node->delete_on_close_cred); 8345331Samw node->delete_on_close_cred = NULL; 8355331Samw } 8365331Samw smb_rwx_xexit(&node->n_lock); 8375331Samw } 8385772Sas200622 8395772Sas200622 /* 8406771Sjb150015 * smb_node_open_check 8415772Sas200622 * 8425772Sas200622 * check file sharing rules for current open request 8435772Sas200622 * against all existing opens for a file. 8445772Sas200622 * 8455772Sas200622 * Returns NT_STATUS_SHARING_VIOLATION if there is any 8465772Sas200622 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 8475772Sas200622 */ 8485772Sas200622 uint32_t 8495772Sas200622 smb_node_open_check(struct smb_node *node, cred_t *cr, 8505772Sas200622 uint32_t desired_access, uint32_t share_access) 8515772Sas200622 { 8525772Sas200622 smb_ofile_t *of; 8535772Sas200622 uint32_t status; 8545772Sas200622 8555772Sas200622 ASSERT(node); 8565772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 8575772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 8585772Sas200622 8595772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 8605772Sas200622 of = smb_llist_head(&node->n_ofile_list); 8615772Sas200622 while (of) { 8626771Sjb150015 status = smb_ofile_open_check(of, cr, desired_access, 8636771Sjb150015 share_access); 8646771Sjb150015 8656771Sjb150015 switch (status) { 8666771Sjb150015 case NT_STATUS_INVALID_HANDLE: 8676771Sjb150015 case NT_STATUS_SUCCESS: 8686771Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 8696771Sjb150015 break; 8706771Sjb150015 default: 8716771Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 8725772Sas200622 smb_llist_exit(&node->n_ofile_list); 8735772Sas200622 return (status); 8745772Sas200622 } 8755772Sas200622 } 8766771Sjb150015 8775772Sas200622 smb_llist_exit(&node->n_ofile_list); 8785772Sas200622 return (NT_STATUS_SUCCESS); 8795772Sas200622 } 8805772Sas200622 8815772Sas200622 uint32_t 8825772Sas200622 smb_node_rename_check(struct smb_node *node) 8835772Sas200622 { 8846771Sjb150015 struct smb_ofile *of; 8856771Sjb150015 uint32_t status; 8865772Sas200622 8875772Sas200622 ASSERT(node); 8885772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 8895772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 8905772Sas200622 8915772Sas200622 /* 8925772Sas200622 * Intra-CIFS check 8935772Sas200622 */ 8945772Sas200622 8955772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 8966771Sjb150015 of = smb_llist_head(&node->n_ofile_list); 8976771Sjb150015 while (of) { 8986771Sjb150015 status = smb_ofile_rename_check(of); 8995772Sas200622 9006771Sjb150015 switch (status) { 9016771Sjb150015 case NT_STATUS_INVALID_HANDLE: 9026771Sjb150015 case NT_STATUS_SUCCESS: 9036771Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 9046771Sjb150015 break; 9056771Sjb150015 default: 9066771Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 9076771Sjb150015 smb_llist_exit(&node->n_ofile_list); 9086771Sjb150015 return (status); 9095772Sas200622 } 9105772Sas200622 } 9115772Sas200622 smb_llist_exit(&node->n_ofile_list); 9125772Sas200622 9135772Sas200622 /* 9145772Sas200622 * system-wide share check 9155772Sas200622 */ 9165772Sas200622 9175772Sas200622 if (nbl_share_conflict(node->vp, NBL_RENAME, NULL)) 9185772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 9195772Sas200622 else 9205772Sas200622 return (NT_STATUS_SUCCESS); 9215772Sas200622 } 9225772Sas200622 9236771Sjb150015 uint32_t 9245772Sas200622 smb_node_delete_check(smb_node_t *node) 9255772Sas200622 { 9266771Sjb150015 smb_ofile_t *of; 9276771Sjb150015 uint32_t status; 9285772Sas200622 9295772Sas200622 ASSERT(node); 9305772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 9315772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 9325772Sas200622 9335772Sas200622 if (node->attr.sa_vattr.va_type == VDIR) 9345772Sas200622 return (NT_STATUS_SUCCESS); 9355772Sas200622 9365772Sas200622 /* 9375772Sas200622 * intra-CIFS check 9385772Sas200622 */ 9395772Sas200622 9405772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 9416771Sjb150015 of = smb_llist_head(&node->n_ofile_list); 9426771Sjb150015 while (of) { 9436771Sjb150015 status = smb_ofile_delete_check(of); 9446771Sjb150015 9456771Sjb150015 switch (status) { 9466771Sjb150015 case NT_STATUS_INVALID_HANDLE: 9476771Sjb150015 case NT_STATUS_SUCCESS: 9486771Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 9496771Sjb150015 break; 9506771Sjb150015 default: 9516771Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 9525772Sas200622 smb_llist_exit(&node->n_ofile_list); 9536771Sjb150015 return (status); 9545772Sas200622 } 9555772Sas200622 } 9565772Sas200622 smb_llist_exit(&node->n_ofile_list); 9575772Sas200622 9585772Sas200622 /* 9595772Sas200622 * system-wide share check 9605772Sas200622 */ 9615772Sas200622 9625772Sas200622 if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL)) 9635772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 9645772Sas200622 else 9655772Sas200622 return (NT_STATUS_SUCCESS); 9665772Sas200622 } 9675772Sas200622 9685772Sas200622 /* 9695772Sas200622 * smb_node_start_crit() 9705772Sas200622 * 9715772Sas200622 * Enter critical region for share reservations. 9725772Sas200622 * See comments above smb_fsop_shrlock(). 9735772Sas200622 */ 9745772Sas200622 9755772Sas200622 void 9765772Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode) 9775772Sas200622 { 9785772Sas200622 rw_enter(&node->n_share_lock, mode); 9795772Sas200622 nbl_start_crit(node->vp, mode); 9805772Sas200622 } 9815772Sas200622 9825772Sas200622 /* 9835772Sas200622 * smb_node_end_crit() 9845772Sas200622 * 9855772Sas200622 * Exit critical region for share reservations. 9865772Sas200622 */ 9875772Sas200622 9885772Sas200622 void 9895772Sas200622 smb_node_end_crit(smb_node_t *node) 9905772Sas200622 { 9915772Sas200622 nbl_end_crit(node->vp); 9925772Sas200622 rw_exit(&node->n_share_lock); 9935772Sas200622 } 9945772Sas200622 9955772Sas200622 int 9965772Sas200622 smb_node_in_crit(smb_node_t *node) 9975772Sas200622 { 9985772Sas200622 return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock)); 9995772Sas200622 } 1000