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 /* 225772Sas200622 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 235331Samw * Use is subject to license terms. 245331Samw */ 255331Samw 265331Samw #pragma ident "%Z%%M% %I% %E% 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 uint32_t smb_node_hit = 0; 935331Samw uint32_t smb_node_miss = 0; 945331Samw uint32_t smb_node_alloc = 0; 955331Samw uint32_t smb_node_free = 0; 965331Samw 975331Samw #define VALIDATE_DIR_NODE(_dir_, _node_) \ 985331Samw ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \ 995331Samw ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \ 1005331Samw ASSERT((_dir_)->dir_snode != (_node_)); 1015331Samw 1025331Samw /* 1035331Samw * smb_node_lookup() 1045331Samw * 1055331Samw * NOTE: This routine should only be called by the file system interface layer, 1065331Samw * and not by SMB. 1075331Samw * 1085331Samw * smb_node_lookup() is called upon successful lookup, mkdir, and create 1095331Samw * (for both non-streams and streams). In each of these cases, a held vnode is 1105331Samw * passed into this routine. If an smb_node already exists for this vnode, 1115331Samw * the vp is released. Otherwise, a new smb_node will be created and the 1125331Samw * reference will be held until the refcnt on the node goes to 0 (see 1135331Samw * smb_node_release()). 1145331Samw * 1155331Samw * A reference is taken on the smb_node whether found in the hash table 1165331Samw * or newly created. 1175331Samw * 1185331Samw * If an smb_node needs to be created, a reference is also taken on the 1195331Samw * dir_snode (if passed in). 1205331Samw * 1215331Samw * See smb_node_release() for details on the release of these references. 1225331Samw */ 1235331Samw 1245331Samw /*ARGSUSED*/ 1255331Samw smb_node_t * 1265331Samw smb_node_lookup( 1275331Samw struct smb_request *sr, 1285331Samw struct open_param *op, 1295331Samw cred_t *cred, 1305331Samw vnode_t *vp, 1315331Samw char *od_name, 1325331Samw smb_node_t *dir_snode, 1335331Samw smb_node_t *unnamed_node, 1345331Samw smb_attr_t *attr) 1355331Samw { 1365331Samw smb_llist_t *node_hdr; 1375331Samw smb_node_t *node; 1385331Samw uint32_t hashkey = 0; 1395331Samw fs_desc_t fsd; 1405331Samw int error; 1415331Samw krw_t lock_mode; 1425331Samw vnode_t *unnamed_vp = NULL; 1435331Samw 1445331Samw /* 1455331Samw * smb_vop_getattr() is called here instead of smb_fsop_getattr(), 1465331Samw * because the node may not yet exist. We also do not want to call 1475331Samw * it with the list lock held. 1485331Samw */ 1495331Samw 1505331Samw if (unnamed_node) 1515331Samw unnamed_vp = unnamed_node->vp; 1525331Samw 1535331Samw /* 1545331Samw * This getattr is performed on behalf of the server 1555331Samw * that's why kcred is used not the user's cred 1565331Samw */ 1575331Samw attr->sa_mask = SMB_AT_ALL; 1585772Sas200622 error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred); 1595331Samw if (error) 1605331Samw return (NULL); 1615331Samw 1625331Samw if (sr) { 1635331Samw if (sr->tid_tree) { 1645331Samw /* 1655331Samw * The fsd for a file is that of the tree, even 1665331Samw * if the file resides in a different mountpoint 1675331Samw * under the share. 1685331Samw */ 1695331Samw fsd = sr->tid_tree->t_fsd; 1705331Samw } else { 1715331Samw /* 1725331Samw * This should be getting executed only for the 1735331Samw * tree's root smb_node. 1745331Samw */ 1755331Samw fsd = vp->v_vfsp->vfs_fsid; 1765331Samw } 1775331Samw } else { 1785331Samw fsd = vp->v_vfsp->vfs_fsid; 1795331Samw } 1805331Samw 1815331Samw hashkey = fsd.val[0] + attr->sa_vattr.va_nodeid; 1825331Samw hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 1835331Samw node_hdr = &smb_info.node_hash_table[(hashkey & SMBND_HASH_MASK)]; 1845331Samw lock_mode = RW_READER; 1855331Samw 1865331Samw smb_llist_enter(node_hdr, lock_mode); 1875331Samw for (;;) { 1885331Samw node = list_head(&node_hdr->ll_list); 1895331Samw while (node) { 1905331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 1915331Samw ASSERT(node->n_hash_bucket == node_hdr); 1925331Samw if ((node->n_hashkey == hashkey) && (node->vp == vp)) { 1935331Samw smb_rwx_xenter(&node->n_lock); 1945331Samw DTRACE_PROBE1(smb_node_lookup_hit, 1955331Samw smb_node_t *, node); 1965331Samw switch (node->n_state) { 1975331Samw case SMB_NODE_STATE_AVAILABLE: 1985331Samw /* The node was found. */ 1995331Samw node->n_refcnt++; 2005331Samw if ((node->dir_snode == NULL) && 2015331Samw (dir_snode != NULL) && 2025331Samw (strcmp(od_name, "..") != 0) && 2035331Samw (strcmp(od_name, ".") != 0)) { 2045331Samw VALIDATE_DIR_NODE(dir_snode, 2055331Samw node); 2065331Samw node->dir_snode = dir_snode; 2075331Samw smb_node_ref(dir_snode); 2085331Samw } 2095331Samw node->attr = *attr; 2105772Sas200622 node->n_size = attr->sa_vattr.va_size; 2115331Samw 2125331Samw smb_audit_node(node); 2135331Samw smb_rwx_xexit(&node->n_lock); 2145331Samw smb_llist_exit(node_hdr); 2155331Samw VN_RELE(vp); 2165331Samw return (node); 2175331Samw 2185331Samw case SMB_NODE_STATE_DESTROYING: 2195331Samw /* 2205331Samw * Although the node exists it is about 2215331Samw * to be destroyed. We act as it hasn't 2225331Samw * been found. 2235331Samw */ 2245331Samw smb_rwx_xexit(&node->n_lock); 2255331Samw break; 2265331Samw default: 2275331Samw /* 2285331Samw * Although the node exists it is in an 2295331Samw * unknown state. We act as it hasn't 2305331Samw * been found. 2315331Samw */ 2325331Samw ASSERT(0); 2335331Samw smb_rwx_xexit(&node->n_lock); 2345331Samw break; 2355331Samw } 2365331Samw } 2375331Samw node = smb_llist_next(node_hdr, node); 2385331Samw } 2395331Samw if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) { 2405331Samw lock_mode = RW_WRITER; 2415331Samw continue; 2425331Samw } 2435331Samw break; 2445331Samw } 2455331Samw node = kmem_cache_alloc(smb_info.si_cache_node, KM_SLEEP); 2465331Samw smb_node_alloc++; 2475331Samw 2485331Samw bzero(node, sizeof (smb_node_t)); 2495331Samw 2505331Samw node->n_state = SMB_NODE_STATE_AVAILABLE; 2515331Samw node->n_hash_bucket = node_hdr; 2525331Samw 2535331Samw if (fsd_chkcap(&fsd, FSOLF_READONLY) > 0) { 2545331Samw node->flags |= NODE_READ_ONLY; 2555331Samw } 2565331Samw 2575331Samw smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 2585331Samw offsetof(smb_ofile_t, f_nnd)); 2595331Samw smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 2605331Samw offsetof(smb_lock_t, l_lnd)); 2615331Samw node->n_hashkey = hashkey; 2625331Samw node->n_refcnt = 1; 2635331Samw 2645331Samw if (sr) { 2655331Samw node->n_orig_session_id = sr->session->s_kid; 2665331Samw node->n_orig_uid = crgetuid(sr->user_cr); 2675331Samw } 2685331Samw 2695331Samw node->vp = vp; 2705331Samw 2715331Samw ASSERT(od_name); 2725331Samw (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); 273*5967Scp160787 if (strcmp(od_name, XATTR_DIR) == 0) 274*5967Scp160787 node->flags |= NODE_XATTR_DIR; 2755331Samw node->tree_fsd = fsd; 2765331Samw 2775331Samw if (op) 2785331Samw node->flags |= smb_is_executable(op->fqi.last_comp); 2795331Samw 2805331Samw if (dir_snode) { 2815331Samw smb_node_ref(dir_snode); 2825331Samw node->dir_snode = dir_snode; 2835331Samw ASSERT(dir_snode->dir_snode != node); 2845331Samw ASSERT((dir_snode->vp->v_xattrdir) || 2855331Samw (dir_snode->vp->v_type == VDIR)); 2865331Samw } 2875331Samw 2885331Samw if (unnamed_node) { 2895331Samw smb_node_ref(unnamed_node); 2905331Samw node->unnamed_stream_node = unnamed_node; 2915331Samw } 2925331Samw 2935331Samw node->attr = *attr; 2945331Samw node->flags |= NODE_FLAGS_ATTR_VALID; 2955331Samw node->n_size = node->attr.sa_vattr.va_size; 2965331Samw 2975331Samw smb_rwx_init(&node->n_lock); 2985331Samw node->n_magic = SMB_NODE_MAGIC; 2995331Samw smb_audit_buf_node_create(node); 3005331Samw 3015331Samw DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); 3025331Samw smb_audit_node(node); 3035331Samw smb_llist_insert_head(node_hdr, node); 3045331Samw smb_llist_exit(node_hdr); 3055331Samw return (node); 3065331Samw } 3075331Samw 3085331Samw /* 3095331Samw * smb_stream_node_lookup() 3105331Samw * 3115331Samw * Note: stream_name (the name that will be stored in the "od_name" field 3125331Samw * of a stream's smb_node) is the same as the on-disk name for the stream 3135331Samw * except that it does not have SMB_STREAM_PREFIX prepended. 3145331Samw */ 3155331Samw 3165331Samw smb_node_t * 3175331Samw smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, 3185331Samw vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr) 3195331Samw { 3205331Samw smb_node_t *xattrdir_node; 3215331Samw smb_node_t *snode; 3225331Samw smb_attr_t tmp_attr; 3235331Samw 3245331Samw xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, 3255331Samw fnode, NULL, &tmp_attr); 3265331Samw 3275331Samw if (xattrdir_node == NULL) 3285331Samw return (NULL); 3295331Samw 3305331Samw snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, 3315331Samw fnode, ret_attr); 3325331Samw 3335331Samw /* 3345331Samw * The following VN_HOLD is necessary because the caller will VN_RELE 3355331Samw * xattrdirvp in the case of an error. (xattrdir_node has the original 3365331Samw * hold on the vnode, which the smb_node_release() call below will 3375331Samw * release.) 3385331Samw */ 3395331Samw if (snode == NULL) { 3405331Samw VN_HOLD(xattrdirvp); 3415331Samw } 3425331Samw (void) smb_node_release(xattrdir_node); 3435331Samw return (snode); 3445331Samw } 3455331Samw 3465331Samw 3475331Samw /* 3485331Samw * This function should be called whenever a reference is needed on an 3495331Samw * smb_node pointer. The copy of an smb_node pointer from one non-local 3505331Samw * data structure to another requires a reference to be taken on the smb_node 3515331Samw * (unless the usage is localized). Each data structure deallocation routine 3525331Samw * will call smb_node_release() on its smb_node pointers. 3535331Samw * 3545331Samw * In general, an smb_node pointer residing in a structure should never be 3555331Samw * stale. A node pointer may be NULL, however, and care should be taken 3565331Samw * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. 3575331Samw * Care also needs to be taken with respect to racing deallocations of a 3585331Samw * structure. 3595331Samw */ 3605331Samw 3615331Samw void 3625331Samw smb_node_ref(smb_node_t *node) 3635331Samw { 3645331Samw ASSERT(node); 3655331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 3665331Samw ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 3675331Samw 3685331Samw smb_rwx_xenter(&node->n_lock); 3695331Samw node->n_refcnt++; 3705331Samw ASSERT(node->n_refcnt); 3715331Samw DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); 3725331Samw smb_audit_node(node); 3735331Samw smb_rwx_xexit(&node->n_lock); 3745331Samw } 3755331Samw 3765331Samw /* 3775331Samw * smb_node_lookup() takes a hold on an smb_node, whether found in the 3785331Samw * hash table or newly created. This hold is expected to be released 3795331Samw * in the following manner. 3805331Samw * 3815331Samw * smb_node_lookup() takes an address of an smb_node pointer. This should 3825331Samw * be getting passed down via a lookup (whether path name or component), mkdir, 3835331Samw * create. If the original smb_node pointer resides in a data structure, then 3845331Samw * the deallocation routine for the data structure is responsible for calling 3855331Samw * smb_node_release() on the smb_node pointer. Alternatively, 3865331Samw * smb_node_release() can be called as soon as the smb_node pointer is no longer 3875331Samw * needed. In this case, callers are responsible for setting an embedded 3885331Samw * pointer to NULL if it is known that the last reference is being released. 3895331Samw * 3905331Samw * If the passed-in address of the smb_node pointer belongs to a local variable, 3915331Samw * then the caller with the local variable should call smb_node_release() 3925331Samw * directly. 3935331Samw * 3945331Samw * smb_node_release() itself will call smb_node_release() on a node's dir_snode, 3955331Samw * as smb_node_lookup() takes a hold on dir_snode. 3965331Samw */ 3975331Samw 3985331Samw void 3995331Samw smb_node_release(smb_node_t *node) 4005331Samw { 4015331Samw ASSERT(node); 4025331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 4035331Samw 4045331Samw smb_rwx_xenter(&node->n_lock); 4055331Samw ASSERT(node->n_refcnt); 4065331Samw DTRACE_PROBE1(smb_node_release, smb_node_t *, node); 4075331Samw if (--node->n_refcnt == 0) { 4085331Samw switch (node->n_state) { 4095331Samw 4105331Samw case SMB_NODE_STATE_AVAILABLE: 4115331Samw node->n_state = SMB_NODE_STATE_DESTROYING; 4125331Samw smb_rwx_xexit(&node->n_lock); 4135331Samw 4145331Samw smb_llist_enter(node->n_hash_bucket, RW_WRITER); 4155331Samw smb_llist_remove(node->n_hash_bucket, node); 4165331Samw smb_llist_exit(node->n_hash_bucket); 4175331Samw 4185331Samw /* 4195331Samw * Check if the file was deleted 4205331Samw */ 4215331Samw smb_node_delete_on_close(node); 4225331Samw node->n_magic = (uint32_t)~SMB_NODE_MAGIC; 4235331Samw 4245331Samw /* These lists should be empty. */ 4255331Samw smb_llist_destructor(&node->n_ofile_list); 4265331Samw smb_llist_destructor(&node->n_lock_list); 4275331Samw 4285331Samw if (node->dir_snode) { 4295331Samw ASSERT(node->dir_snode->n_magic == 4305331Samw SMB_NODE_MAGIC); 4315331Samw smb_node_release(node->dir_snode); 4325331Samw } 4335331Samw 4345331Samw if (node->unnamed_stream_node) { 4355331Samw ASSERT(node->unnamed_stream_node->n_magic == 4365331Samw SMB_NODE_MAGIC); 4375331Samw smb_node_release(node->unnamed_stream_node); 4385331Samw } 4395331Samw 4405331Samw ASSERT(node->vp); 4415331Samw VN_RELE(node->vp); 4425331Samw 4435331Samw smb_audit_buf_node_destroy(node); 4445331Samw smb_rwx_destroy(&node->n_lock); 4455331Samw kmem_cache_free(smb_info.si_cache_node, node); 4465331Samw ++smb_node_free; 4475331Samw return; 4485331Samw 4495331Samw default: 4505331Samw ASSERT(0); 4515331Samw break; 4525331Samw } 4535331Samw } 4545331Samw smb_audit_node(node); 4555331Samw smb_rwx_xexit(&node->n_lock); 4565331Samw } 4575331Samw 4585331Samw static void 4595331Samw smb_node_delete_on_close(smb_node_t *node) 4605331Samw { 4615331Samw smb_node_t *d_snode; 4625331Samw int rc = 0; 4635331Samw 4645331Samw d_snode = node->dir_snode; 4655331Samw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 4665331Samw 4675331Samw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 4685331Samw ASSERT(node->od_name != NULL); 4695331Samw if (node->attr.sa_vattr.va_type == VDIR) 4705331Samw rc = smb_fsop_rmdir(0, node->delete_on_close_cred, 4715331Samw d_snode, node->od_name, 1); 4725331Samw else 4735331Samw rc = smb_fsop_remove(0, node->delete_on_close_cred, 4745331Samw d_snode, node->od_name, 1); 4755331Samw smb_cred_rele(node->delete_on_close_cred); 4765331Samw } 4775331Samw if (rc != 0) 4785331Samw cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", 4795331Samw node->od_name, rc); 4805331Samw DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); 4815331Samw } 4825331Samw 4835331Samw /* 4845331Samw * smb_node_rename() 4855331Samw * 4865331Samw */ 4875331Samw int 4885331Samw smb_node_rename( 4895331Samw smb_node_t *from_dir_snode, 4905331Samw smb_node_t *ret_snode, 4915331Samw smb_node_t *to_dir_snode, 4925331Samw char *to_name) 4935331Samw { 4945331Samw ASSERT(from_dir_snode); 4955331Samw ASSERT(to_dir_snode); 4965331Samw ASSERT(ret_snode); 4975331Samw ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); 4985331Samw ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); 4995331Samw ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC); 5005331Samw ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5015331Samw ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5025331Samw ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5035331Samw 5045331Samw smb_node_ref(to_dir_snode); 5055331Samw smb_rwx_xenter(&ret_snode->n_lock); 5065331Samw ret_snode->dir_snode = to_dir_snode; 5075331Samw smb_rwx_xexit(&ret_snode->n_lock); 5085331Samw ASSERT(to_dir_snode->dir_snode != ret_snode); 5095331Samw ASSERT((to_dir_snode->vp->v_xattrdir) || 5105331Samw (to_dir_snode->vp->v_type == VDIR)); 5115331Samw smb_node_release(from_dir_snode); 5125331Samw 5135331Samw (void) strcpy(ret_snode->od_name, to_name); 5145331Samw 5155331Samw /* 5165331Samw * XXX Need to update attributes? 5175331Samw */ 5185331Samw 5195331Samw return (0); 5205331Samw } 5215331Samw 5225331Samw int 5235331Samw smb_node_root_init() 5245331Samw { 5255331Samw smb_attr_t va; 5265331Samw 5275331Samw /* 5285331Samw * Take an explicit hold on rootdir. This goes with the 5295331Samw * corresponding release in smb_node_root_fini()/smb_node_release(). 5305331Samw */ 5315331Samw 5325331Samw VN_HOLD(rootdir); 5335331Samw 5345331Samw if ((smb_info.si_root_smb_node = smb_node_lookup(NULL, NULL, kcred, 5355331Samw rootdir, ROOTVOL, NULL, NULL, &va)) == NULL) 5365331Samw return (-1); 5375331Samw else 5385331Samw return (0); 5395331Samw } 5405331Samw 5415331Samw void 5425331Samw smb_node_root_fini() 5435331Samw { 5445331Samw if (smb_info.si_root_smb_node) { 5455331Samw smb_node_release(smb_info.si_root_smb_node); 5465331Samw smb_info.si_root_smb_node = NULL; 5475331Samw } 5485331Samw } 5495331Samw 5505331Samw /* 5515331Samw * smb_node_get_size 5525331Samw */ 5535331Samw uint64_t 5545331Samw smb_node_get_size( 5555331Samw smb_node_t *node, 5565331Samw smb_attr_t *attr) 5575331Samw { 5585331Samw uint64_t size; 5595331Samw 5605331Samw if (attr->sa_vattr.va_type == VDIR) 5615331Samw return (0); 5625331Samw 5635331Samw smb_rwx_xenter(&node->n_lock); 5645331Samw if (node && (node->flags & NODE_FLAGS_SET_SIZE)) 5655331Samw size = node->n_size; 5665331Samw else 5675331Samw size = attr->sa_vattr.va_size; 5685331Samw smb_rwx_xexit(&node->n_lock); 5695331Samw return (size); 5705331Samw } 5715331Samw 5725331Samw static int 5735331Samw timeval_cmp(timestruc_t *a, timestruc_t *b) 5745331Samw { 5755331Samw if (a->tv_sec < b->tv_sec) 5765331Samw return (-1); 5775331Samw if (a->tv_sec > b->tv_sec) 5785331Samw return (1); 5795331Samw /* Seconds are equal compare tv_nsec */ 5805331Samw if (a->tv_nsec < b->tv_nsec) 5815331Samw return (-1); 5825331Samw return (a->tv_nsec > b->tv_nsec); 5835331Samw } 5845331Samw 5855331Samw /* 5865331Samw * smb_node_set_time 5875331Samw * 5885331Samw * This function will update the time stored in the node and 5895331Samw * set the appropriate flags. If there is nothing to update 5905331Samw * or the node is readonly, the function would return without 5915331Samw * any updates. The update is only in the node level and the 5925331Samw * attribute in the file system will be updated when client 5935331Samw * close the file. 5945331Samw */ 5955331Samw void 5965331Samw smb_node_set_time(struct smb_node *node, struct timestruc *crtime, 5975331Samw struct timestruc *mtime, struct timestruc *atime, 5985331Samw struct timestruc *ctime, unsigned int what) 5995331Samw { 6005331Samw smb_rwx_xenter(&node->n_lock); 6015331Samw if (node->flags & NODE_READ_ONLY || what == 0) { 6025331Samw smb_rwx_xexit(&node->n_lock); 6035331Samw return; 6045331Samw } 6055331Samw 6065331Samw if ((what & SMB_AT_CRTIME && crtime == 0) || 6075331Samw (what & SMB_AT_MTIME && mtime == 0) || 6085331Samw (what & SMB_AT_ATIME && atime == 0) || 6095331Samw (what & SMB_AT_CTIME && ctime == 0)) { 6105331Samw smb_rwx_xexit(&node->n_lock); 6115331Samw return; 6125331Samw } 6135331Samw 6145331Samw if ((what & SMB_AT_CRTIME) && 6155331Samw timeval_cmp((timestruc_t *)&node->attr.sa_crtime, 6165331Samw crtime) != 0) { 6175331Samw node->what |= SMB_AT_CRTIME; 6185331Samw node->attr.sa_crtime = *((timestruc_t *)crtime); 6195331Samw } 6205331Samw 6215331Samw if ((what & SMB_AT_MTIME) && 6225331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime, 6235331Samw mtime) != 0) { 6245331Samw node->what |= SMB_AT_MTIME; 6255331Samw node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime); 6265331Samw } 6275331Samw 6285331Samw if ((what & SMB_AT_ATIME) && 6295331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime, 6305331Samw atime) != 0) { 6315331Samw node->what |= SMB_AT_ATIME; 6325331Samw node->attr.sa_vattr.va_atime = *((timestruc_t *)atime); 6335331Samw } 6345331Samw 6355331Samw /* 6365331Samw * The ctime handling is trickier. It has three scenarios. 6375331Samw * 1. Only ctime need to be set and it is the same as the ctime 6385331Samw * stored in the node. (update not necessary) 6395331Samw * 2. The ctime is the same as the ctime stored in the node but 6405331Samw * is not the only time need to be set. (update required) 6415331Samw * 3. The ctime need to be set and is not the same as the ctime 6425331Samw * stored in the node. (update required) 6435331Samw * Unlike other time setting, the ctime needs to be set even when 6445331Samw * it is the same as the ctime in the node if there are other time 6455331Samw * needs to be set (#2). This will ensure the ctime not being 6465331Samw * updated when other times are being updated in the file system. 6475331Samw * 6485331Samw * Retained file rules: 6495331Samw * 6505331Samw * 1. Don't add SMB_AT_CTIME to node->what by default because the 6515331Samw * request will be rejected by filesystem 6525331Samw * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e. 6535331Samw * any request for changing ctime on these files should have 6545331Samw * been already rejected 6555331Samw */ 6565331Samw node->what |= SMB_AT_CTIME; 6575331Samw if (what & SMB_AT_CTIME) { 6585331Samw if ((what == SMB_AT_CTIME) && 6595331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime, 6605331Samw ctime) == 0) { 6615331Samw node->what &= ~SMB_AT_CTIME; 6625331Samw } else { 6635331Samw gethrestime(&node->attr.sa_vattr.va_ctime); 6645331Samw } 6655331Samw } else { 6665331Samw gethrestime(&node->attr.sa_vattr.va_ctime); 6675331Samw } 6685331Samw smb_rwx_xexit(&node->n_lock); 6695331Samw } 6705331Samw 6715331Samw 6725331Samw timestruc_t * 6735331Samw smb_node_get_crtime(smb_node_t *node) 6745331Samw { 6755331Samw return ((timestruc_t *)&node->attr.sa_crtime); 6765331Samw } 6775331Samw 6785331Samw timestruc_t * 6795331Samw smb_node_get_atime(smb_node_t *node) 6805331Samw { 6815331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_atime); 6825331Samw } 6835331Samw 6845331Samw timestruc_t * 6855331Samw smb_node_get_ctime(smb_node_t *node) 6865331Samw { 6875331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_ctime); 6885331Samw } 6895331Samw 6905331Samw timestruc_t * 6915331Samw smb_node_get_mtime(smb_node_t *node) 6925331Samw { 6935331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_mtime); 6945331Samw } 6955331Samw 6965331Samw /* 6975331Samw * smb_node_set_dosattr 6985331Samw * 6995331Samw * Parse the specified DOS attributes and, if they have been modified, 7005331Samw * update the node cache. This call should be followed by a 7015331Samw * smb_sync_fsattr() call to write the attribute changes to filesystem. 7025331Samw */ 7035331Samw void 7045331Samw smb_node_set_dosattr(smb_node_t *node, uint32_t dos_attr) 7055331Samw { 7065331Samw unsigned int mode; /* New mode */ 7075331Samw 7085331Samw mode = 0; 7095331Samw 7105331Samw /* Handle the archive bit */ 7115331Samw if (dos_attr & SMB_FA_ARCHIVE) 7125331Samw mode |= FILE_ATTRIBUTE_ARCHIVE; 7135331Samw 7145331Samw /* Handle the readonly bit */ 7155331Samw if (dos_attr & SMB_FA_READONLY) 7165331Samw mode |= FILE_ATTRIBUTE_READONLY; 7175331Samw 7185331Samw /* Handle the hidden bit */ 7195331Samw if (dos_attr & SMB_FA_HIDDEN) 7205331Samw mode |= FILE_ATTRIBUTE_HIDDEN; 7215331Samw 7225331Samw /* Handle the system bit */ 7235331Samw if (dos_attr & SMB_FA_SYSTEM) 7245331Samw mode |= FILE_ATTRIBUTE_SYSTEM; 7255331Samw 7265331Samw smb_rwx_xenter(&node->n_lock); 7275331Samw if (node->attr.sa_dosattr != mode) { 7285331Samw node->attr.sa_dosattr = mode; 7295331Samw node->what |= SMB_AT_DOSATTR; 7305331Samw } 7315331Samw smb_rwx_xexit(&node->n_lock); 7325331Samw } 7335331Samw 7345331Samw /* 7355331Samw * smb_node_get_dosattr 7365331Samw * 7375331Samw * This function will get dos attribute using the node. 7385331Samw */ 7395331Samw uint32_t 7405331Samw smb_node_get_dosattr(smb_node_t *node) 7415331Samw { 7425331Samw return (smb_mode_to_dos_attributes(&node->attr)); 7435331Samw } 7445331Samw 7455331Samw int 7465331Samw smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr) 7475331Samw { 7485331Samw int rc = -1; 7495331Samw 7505331Samw smb_rwx_xenter(&node->n_lock); 7515331Samw if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) && 7525331Samw !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) { 7535331Samw crhold(cr); 7545331Samw node->delete_on_close_cred = cr; 7555331Samw node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; 7565331Samw rc = 0; 7575331Samw } 7585331Samw smb_rwx_xexit(&node->n_lock); 7595331Samw return (rc); 7605331Samw } 7615331Samw 7625331Samw void 7635331Samw smb_node_reset_delete_on_close(smb_node_t *node) 7645331Samw { 7655331Samw smb_rwx_xenter(&node->n_lock); 7665331Samw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 7675331Samw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 7685331Samw crfree(node->delete_on_close_cred); 7695331Samw node->delete_on_close_cred = NULL; 7705331Samw } 7715331Samw smb_rwx_xexit(&node->n_lock); 7725331Samw } 7735772Sas200622 7745772Sas200622 /* 7755772Sas200622 * smb_node_share_check 7765772Sas200622 * 7775772Sas200622 * check file sharing rules for current open request 7785772Sas200622 * against all existing opens for a file. 7795772Sas200622 * 7805772Sas200622 * Returns NT_STATUS_SHARING_VIOLATION if there is any 7815772Sas200622 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 7825772Sas200622 */ 7835772Sas200622 uint32_t 7845772Sas200622 smb_node_open_check(struct smb_node *node, cred_t *cr, 7855772Sas200622 uint32_t desired_access, uint32_t share_access) 7865772Sas200622 { 7875772Sas200622 smb_ofile_t *of; 7885772Sas200622 uint32_t status; 7895772Sas200622 7905772Sas200622 ASSERT(node); 7915772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 7925772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 7935772Sas200622 7945772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 7955772Sas200622 of = smb_llist_head(&node->n_ofile_list); 7965772Sas200622 while (of) { 7975772Sas200622 status = smb_node_share_check(node, cr, desired_access, 7985772Sas200622 share_access, of); 7995772Sas200622 if (status == NT_STATUS_SHARING_VIOLATION) { 8005772Sas200622 smb_llist_exit(&node->n_ofile_list); 8015772Sas200622 return (status); 8025772Sas200622 } 8035772Sas200622 of = smb_llist_next(&node->n_ofile_list, of); 8045772Sas200622 } 8055772Sas200622 smb_llist_exit(&node->n_ofile_list); 8065772Sas200622 8075772Sas200622 return (NT_STATUS_SUCCESS); 8085772Sas200622 } 8095772Sas200622 8105772Sas200622 /* 8115772Sas200622 * smb_open_share_check 8125772Sas200622 * 8135772Sas200622 * check file sharing rules for current open request 8145772Sas200622 * against the given existing open. 8155772Sas200622 * 8165772Sas200622 * Returns NT_STATUS_SHARING_VIOLATION if there is any 8175772Sas200622 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 8185772Sas200622 */ 8195772Sas200622 uint32_t 8205772Sas200622 smb_node_share_check( 8215772Sas200622 struct smb_node *node, 8225772Sas200622 cred_t *cr, 8235772Sas200622 uint32_t desired_access, 8245772Sas200622 uint32_t share_access, 8255772Sas200622 smb_ofile_t *of) 8265772Sas200622 { 8275772Sas200622 /* 8285772Sas200622 * It appears that share modes are not relevant to 8295772Sas200622 * directories, but this check will remain as it is not 8305772Sas200622 * clear whether it was originally put here for a reason. 8315772Sas200622 */ 8325772Sas200622 if (node->attr.sa_vattr.va_type == VDIR) { 8335772Sas200622 if (SMB_DENY_RW(of->f_share_access) && 8345772Sas200622 (node->n_orig_uid != crgetuid(cr))) { 8355772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 8365772Sas200622 } 8375772Sas200622 8385772Sas200622 return (NT_STATUS_SUCCESS); 8395772Sas200622 } 8405772Sas200622 8415772Sas200622 /* if it's just meta data */ 8425772Sas200622 if ((of->f_granted_access & FILE_DATA_ALL) == 0) 8435772Sas200622 return (NT_STATUS_SUCCESS); 8445772Sas200622 8455772Sas200622 /* 8465772Sas200622 * Check requested share access against the 8475772Sas200622 * open granted (desired) access 8485772Sas200622 */ 8495772Sas200622 if (SMB_DENY_DELETE(share_access) && (of->f_granted_access & DELETE)) 8505772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 8515772Sas200622 8525772Sas200622 if (SMB_DENY_READ(share_access) && 8535772Sas200622 (of->f_granted_access & (FILE_READ_DATA | FILE_EXECUTE))) 8545772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 8555772Sas200622 8565772Sas200622 if (SMB_DENY_WRITE(share_access) && 8575772Sas200622 (of->f_granted_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) 8585772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 8595772Sas200622 8605772Sas200622 /* check requested desired access against the open share access */ 8615772Sas200622 if (SMB_DENY_DELETE(of->f_share_access) && (desired_access & DELETE)) 8625772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 8635772Sas200622 8645772Sas200622 if (SMB_DENY_READ(of->f_share_access) && 8655772Sas200622 (desired_access & (FILE_READ_DATA | FILE_EXECUTE))) 8665772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 8675772Sas200622 8685772Sas200622 if (SMB_DENY_WRITE(of->f_share_access) && 8695772Sas200622 (desired_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) 8705772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 8715772Sas200622 8725772Sas200622 return (NT_STATUS_SUCCESS); 8735772Sas200622 } 8745772Sas200622 8755772Sas200622 /* 8765772Sas200622 * smb_rename_share_check 8775772Sas200622 * 8785772Sas200622 * An open file can be renamed if 8795772Sas200622 * 8805772Sas200622 * 1. isn't opened for data writing or deleting 8815772Sas200622 * 8825772Sas200622 * 2. Opened with "Deny Delete" share mode 8835772Sas200622 * But not opened for data reading or executing 8845772Sas200622 * (opened for accessing meta data) 8855772Sas200622 */ 8865772Sas200622 8875772Sas200622 DWORD 8885772Sas200622 smb_node_rename_check(struct smb_node *node) 8895772Sas200622 { 8905772Sas200622 struct smb_ofile *open; 8915772Sas200622 8925772Sas200622 ASSERT(node); 8935772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 8945772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 8955772Sas200622 8965772Sas200622 /* 8975772Sas200622 * Intra-CIFS check 8985772Sas200622 */ 8995772Sas200622 9005772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 9015772Sas200622 open = smb_llist_head(&node->n_ofile_list); 9025772Sas200622 while (open) { 9035772Sas200622 if (open->f_granted_access & 9045772Sas200622 (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) { 9055772Sas200622 smb_llist_exit(&node->n_ofile_list); 9065772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 9075772Sas200622 } 9085772Sas200622 9095772Sas200622 if ((open->f_share_access & FILE_SHARE_DELETE) == 0) { 9105772Sas200622 if (open->f_granted_access & 9115772Sas200622 (FILE_READ_DATA | FILE_EXECUTE)) { 9125772Sas200622 smb_llist_exit(&node->n_ofile_list); 9135772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 9145772Sas200622 } 9155772Sas200622 } 9165772Sas200622 open = smb_llist_next(&node->n_ofile_list, open); 9175772Sas200622 } 9185772Sas200622 smb_llist_exit(&node->n_ofile_list); 9195772Sas200622 9205772Sas200622 /* 9215772Sas200622 * system-wide share check 9225772Sas200622 */ 9235772Sas200622 9245772Sas200622 if (nbl_share_conflict(node->vp, NBL_RENAME, NULL)) 9255772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 9265772Sas200622 else 9275772Sas200622 return (NT_STATUS_SUCCESS); 9285772Sas200622 } 9295772Sas200622 9305772Sas200622 /* 9315772Sas200622 * smb_node_delete_check 9325772Sas200622 * 9335772Sas200622 * An open file can be deleted only if opened for 9345772Sas200622 * accessing meta data. Share modes aren't important 9355772Sas200622 * in this case. 9365772Sas200622 * 9375772Sas200622 * NOTE: there is another mechanism for deleting an 9385772Sas200622 * open file that NT clients usually use. 9395772Sas200622 * That's setting "Delete on close" flag for an open 9405772Sas200622 * file. In this way the file will be deleted after 9415772Sas200622 * last close. This flag can be set by SmbTrans2SetFileInfo 9425772Sas200622 * with FILE_DISPOSITION_INFO information level. 9435772Sas200622 * For setting this flag, the file should be opened by 9445772Sas200622 * DELETE access in the FID that is passed in the Trans2 9455772Sas200622 * request. 9465772Sas200622 */ 9475772Sas200622 DWORD 9485772Sas200622 smb_node_delete_check(smb_node_t *node) 9495772Sas200622 { 9505772Sas200622 smb_ofile_t *file; 9515772Sas200622 9525772Sas200622 ASSERT(node); 9535772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 9545772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 9555772Sas200622 9565772Sas200622 if (node->attr.sa_vattr.va_type == VDIR) 9575772Sas200622 return (NT_STATUS_SUCCESS); 9585772Sas200622 9595772Sas200622 /* 9605772Sas200622 * intra-CIFS check 9615772Sas200622 */ 9625772Sas200622 9635772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 9645772Sas200622 file = smb_llist_head(&node->n_ofile_list); 9655772Sas200622 while (file) { 9665772Sas200622 ASSERT(file->f_magic == SMB_OFILE_MAGIC); 9675772Sas200622 if (file->f_granted_access & 9685772Sas200622 (FILE_READ_DATA | 9695772Sas200622 FILE_WRITE_DATA | 9705772Sas200622 FILE_APPEND_DATA | 9715772Sas200622 FILE_EXECUTE | 9725772Sas200622 DELETE)) { 9735772Sas200622 smb_llist_exit(&node->n_ofile_list); 9745772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 9755772Sas200622 } 9765772Sas200622 file = smb_llist_next(&node->n_ofile_list, file); 9775772Sas200622 } 9785772Sas200622 smb_llist_exit(&node->n_ofile_list); 9795772Sas200622 9805772Sas200622 /* 9815772Sas200622 * system-wide share check 9825772Sas200622 */ 9835772Sas200622 9845772Sas200622 if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL)) 9855772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 9865772Sas200622 else 9875772Sas200622 return (NT_STATUS_SUCCESS); 9885772Sas200622 } 9895772Sas200622 9905772Sas200622 /* 9915772Sas200622 * smb_node_start_crit() 9925772Sas200622 * 9935772Sas200622 * Enter critical region for share reservations. 9945772Sas200622 * See comments above smb_fsop_shrlock(). 9955772Sas200622 */ 9965772Sas200622 9975772Sas200622 void 9985772Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode) 9995772Sas200622 { 10005772Sas200622 rw_enter(&node->n_share_lock, mode); 10015772Sas200622 nbl_start_crit(node->vp, mode); 10025772Sas200622 } 10035772Sas200622 10045772Sas200622 /* 10055772Sas200622 * smb_node_end_crit() 10065772Sas200622 * 10075772Sas200622 * Exit critical region for share reservations. 10085772Sas200622 */ 10095772Sas200622 10105772Sas200622 void 10115772Sas200622 smb_node_end_crit(smb_node_t *node) 10125772Sas200622 { 10135772Sas200622 nbl_end_crit(node->vp); 10145772Sas200622 rw_exit(&node->n_share_lock); 10155772Sas200622 } 10165772Sas200622 10175772Sas200622 int 10185772Sas200622 smb_node_in_crit(smb_node_t *node) 10195772Sas200622 { 10205772Sas200622 return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock)); 10215772Sas200622 } 1022