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*5772Sas200622 * 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> 87*5772Sas200622 #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; 158*5772Sas200622 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; 210*5772Sas200622 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)); 2735331Samw node->tree_fsd = fsd; 2745331Samw 2755331Samw if (op) 2765331Samw node->flags |= smb_is_executable(op->fqi.last_comp); 2775331Samw 2785331Samw if (dir_snode) { 2795331Samw smb_node_ref(dir_snode); 2805331Samw node->dir_snode = dir_snode; 2815331Samw ASSERT(dir_snode->dir_snode != node); 2825331Samw ASSERT((dir_snode->vp->v_xattrdir) || 2835331Samw (dir_snode->vp->v_type == VDIR)); 2845331Samw } 2855331Samw 2865331Samw if (unnamed_node) { 2875331Samw smb_node_ref(unnamed_node); 2885331Samw node->unnamed_stream_node = unnamed_node; 2895331Samw } 2905331Samw 2915331Samw node->attr = *attr; 2925331Samw node->flags |= NODE_FLAGS_ATTR_VALID; 2935331Samw node->n_size = node->attr.sa_vattr.va_size; 2945331Samw 2955331Samw smb_rwx_init(&node->n_lock); 2965331Samw node->n_magic = SMB_NODE_MAGIC; 2975331Samw smb_audit_buf_node_create(node); 2985331Samw 2995331Samw DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); 3005331Samw smb_audit_node(node); 3015331Samw smb_llist_insert_head(node_hdr, node); 3025331Samw smb_llist_exit(node_hdr); 3035331Samw return (node); 3045331Samw } 3055331Samw 3065331Samw /* 3075331Samw * smb_stream_node_lookup() 3085331Samw * 3095331Samw * Note: stream_name (the name that will be stored in the "od_name" field 3105331Samw * of a stream's smb_node) is the same as the on-disk name for the stream 3115331Samw * except that it does not have SMB_STREAM_PREFIX prepended. 3125331Samw */ 3135331Samw 3145331Samw smb_node_t * 3155331Samw smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, 3165331Samw vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr) 3175331Samw { 3185331Samw smb_node_t *xattrdir_node; 3195331Samw smb_node_t *snode; 3205331Samw smb_attr_t tmp_attr; 3215331Samw 3225331Samw xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, 3235331Samw fnode, NULL, &tmp_attr); 3245331Samw 3255331Samw if (xattrdir_node == NULL) 3265331Samw return (NULL); 3275331Samw 3285331Samw snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, 3295331Samw fnode, ret_attr); 3305331Samw 3315331Samw /* 3325331Samw * The following VN_HOLD is necessary because the caller will VN_RELE 3335331Samw * xattrdirvp in the case of an error. (xattrdir_node has the original 3345331Samw * hold on the vnode, which the smb_node_release() call below will 3355331Samw * release.) 3365331Samw */ 3375331Samw if (snode == NULL) { 3385331Samw VN_HOLD(xattrdirvp); 3395331Samw } 3405331Samw (void) smb_node_release(xattrdir_node); 3415331Samw return (snode); 3425331Samw } 3435331Samw 3445331Samw 3455331Samw /* 3465331Samw * This function should be called whenever a reference is needed on an 3475331Samw * smb_node pointer. The copy of an smb_node pointer from one non-local 3485331Samw * data structure to another requires a reference to be taken on the smb_node 3495331Samw * (unless the usage is localized). Each data structure deallocation routine 3505331Samw * will call smb_node_release() on its smb_node pointers. 3515331Samw * 3525331Samw * In general, an smb_node pointer residing in a structure should never be 3535331Samw * stale. A node pointer may be NULL, however, and care should be taken 3545331Samw * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. 3555331Samw * Care also needs to be taken with respect to racing deallocations of a 3565331Samw * structure. 3575331Samw */ 3585331Samw 3595331Samw void 3605331Samw smb_node_ref(smb_node_t *node) 3615331Samw { 3625331Samw ASSERT(node); 3635331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 3645331Samw ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 3655331Samw 3665331Samw smb_rwx_xenter(&node->n_lock); 3675331Samw node->n_refcnt++; 3685331Samw ASSERT(node->n_refcnt); 3695331Samw DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); 3705331Samw smb_audit_node(node); 3715331Samw smb_rwx_xexit(&node->n_lock); 3725331Samw } 3735331Samw 3745331Samw /* 3755331Samw * smb_node_lookup() takes a hold on an smb_node, whether found in the 3765331Samw * hash table or newly created. This hold is expected to be released 3775331Samw * in the following manner. 3785331Samw * 3795331Samw * smb_node_lookup() takes an address of an smb_node pointer. This should 3805331Samw * be getting passed down via a lookup (whether path name or component), mkdir, 3815331Samw * create. If the original smb_node pointer resides in a data structure, then 3825331Samw * the deallocation routine for the data structure is responsible for calling 3835331Samw * smb_node_release() on the smb_node pointer. Alternatively, 3845331Samw * smb_node_release() can be called as soon as the smb_node pointer is no longer 3855331Samw * needed. In this case, callers are responsible for setting an embedded 3865331Samw * pointer to NULL if it is known that the last reference is being released. 3875331Samw * 3885331Samw * If the passed-in address of the smb_node pointer belongs to a local variable, 3895331Samw * then the caller with the local variable should call smb_node_release() 3905331Samw * directly. 3915331Samw * 3925331Samw * smb_node_release() itself will call smb_node_release() on a node's dir_snode, 3935331Samw * as smb_node_lookup() takes a hold on dir_snode. 3945331Samw */ 3955331Samw 3965331Samw void 3975331Samw smb_node_release(smb_node_t *node) 3985331Samw { 3995331Samw ASSERT(node); 4005331Samw ASSERT(node->n_magic == SMB_NODE_MAGIC); 4015331Samw 4025331Samw smb_rwx_xenter(&node->n_lock); 4035331Samw ASSERT(node->n_refcnt); 4045331Samw DTRACE_PROBE1(smb_node_release, smb_node_t *, node); 4055331Samw if (--node->n_refcnt == 0) { 4065331Samw switch (node->n_state) { 4075331Samw 4085331Samw case SMB_NODE_STATE_AVAILABLE: 4095331Samw node->n_state = SMB_NODE_STATE_DESTROYING; 4105331Samw smb_rwx_xexit(&node->n_lock); 4115331Samw 4125331Samw smb_llist_enter(node->n_hash_bucket, RW_WRITER); 4135331Samw smb_llist_remove(node->n_hash_bucket, node); 4145331Samw smb_llist_exit(node->n_hash_bucket); 4155331Samw 4165331Samw /* 4175331Samw * Check if the file was deleted 4185331Samw */ 4195331Samw smb_node_delete_on_close(node); 4205331Samw node->n_magic = (uint32_t)~SMB_NODE_MAGIC; 4215331Samw 4225331Samw /* These lists should be empty. */ 4235331Samw smb_llist_destructor(&node->n_ofile_list); 4245331Samw smb_llist_destructor(&node->n_lock_list); 4255331Samw 4265331Samw if (node->dir_snode) { 4275331Samw ASSERT(node->dir_snode->n_magic == 4285331Samw SMB_NODE_MAGIC); 4295331Samw smb_node_release(node->dir_snode); 4305331Samw } 4315331Samw 4325331Samw if (node->unnamed_stream_node) { 4335331Samw ASSERT(node->unnamed_stream_node->n_magic == 4345331Samw SMB_NODE_MAGIC); 4355331Samw smb_node_release(node->unnamed_stream_node); 4365331Samw } 4375331Samw 4385331Samw ASSERT(node->vp); 4395331Samw VN_RELE(node->vp); 4405331Samw 4415331Samw smb_audit_buf_node_destroy(node); 4425331Samw smb_rwx_destroy(&node->n_lock); 4435331Samw kmem_cache_free(smb_info.si_cache_node, node); 4445331Samw ++smb_node_free; 4455331Samw return; 4465331Samw 4475331Samw default: 4485331Samw ASSERT(0); 4495331Samw break; 4505331Samw } 4515331Samw } 4525331Samw smb_audit_node(node); 4535331Samw smb_rwx_xexit(&node->n_lock); 4545331Samw } 4555331Samw 4565331Samw static void 4575331Samw smb_node_delete_on_close(smb_node_t *node) 4585331Samw { 4595331Samw smb_node_t *d_snode; 4605331Samw int rc = 0; 4615331Samw 4625331Samw d_snode = node->dir_snode; 4635331Samw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 4645331Samw 4655331Samw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 4665331Samw ASSERT(node->od_name != NULL); 4675331Samw if (node->attr.sa_vattr.va_type == VDIR) 4685331Samw rc = smb_fsop_rmdir(0, node->delete_on_close_cred, 4695331Samw d_snode, node->od_name, 1); 4705331Samw else 4715331Samw rc = smb_fsop_remove(0, node->delete_on_close_cred, 4725331Samw d_snode, node->od_name, 1); 4735331Samw smb_cred_rele(node->delete_on_close_cred); 4745331Samw } 4755331Samw if (rc != 0) 4765331Samw cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", 4775331Samw node->od_name, rc); 4785331Samw DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); 4795331Samw } 4805331Samw 4815331Samw /* 4825331Samw * smb_node_rename() 4835331Samw * 4845331Samw */ 4855331Samw int 4865331Samw smb_node_rename( 4875331Samw smb_node_t *from_dir_snode, 4885331Samw smb_node_t *ret_snode, 4895331Samw smb_node_t *to_dir_snode, 4905331Samw char *to_name) 4915331Samw { 4925331Samw ASSERT(from_dir_snode); 4935331Samw ASSERT(to_dir_snode); 4945331Samw ASSERT(ret_snode); 4955331Samw ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); 4965331Samw ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); 4975331Samw ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC); 4985331Samw ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 4995331Samw ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5005331Samw ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE); 5015331Samw 5025331Samw smb_node_ref(to_dir_snode); 5035331Samw smb_rwx_xenter(&ret_snode->n_lock); 5045331Samw ret_snode->dir_snode = to_dir_snode; 5055331Samw smb_rwx_xexit(&ret_snode->n_lock); 5065331Samw ASSERT(to_dir_snode->dir_snode != ret_snode); 5075331Samw ASSERT((to_dir_snode->vp->v_xattrdir) || 5085331Samw (to_dir_snode->vp->v_type == VDIR)); 5095331Samw smb_node_release(from_dir_snode); 5105331Samw 5115331Samw (void) strcpy(ret_snode->od_name, to_name); 5125331Samw 5135331Samw /* 5145331Samw * XXX Need to update attributes? 5155331Samw */ 5165331Samw 5175331Samw return (0); 5185331Samw } 5195331Samw 5205331Samw int 5215331Samw smb_node_root_init() 5225331Samw { 5235331Samw smb_attr_t va; 5245331Samw 5255331Samw /* 5265331Samw * Take an explicit hold on rootdir. This goes with the 5275331Samw * corresponding release in smb_node_root_fini()/smb_node_release(). 5285331Samw */ 5295331Samw 5305331Samw VN_HOLD(rootdir); 5315331Samw 5325331Samw if ((smb_info.si_root_smb_node = smb_node_lookup(NULL, NULL, kcred, 5335331Samw rootdir, ROOTVOL, NULL, NULL, &va)) == NULL) 5345331Samw return (-1); 5355331Samw else 5365331Samw return (0); 5375331Samw } 5385331Samw 5395331Samw void 5405331Samw smb_node_root_fini() 5415331Samw { 5425331Samw if (smb_info.si_root_smb_node) { 5435331Samw smb_node_release(smb_info.si_root_smb_node); 5445331Samw smb_info.si_root_smb_node = NULL; 5455331Samw } 5465331Samw } 5475331Samw 5485331Samw /* 5495331Samw * smb_node_get_size 5505331Samw */ 5515331Samw uint64_t 5525331Samw smb_node_get_size( 5535331Samw smb_node_t *node, 5545331Samw smb_attr_t *attr) 5555331Samw { 5565331Samw uint64_t size; 5575331Samw 5585331Samw if (attr->sa_vattr.va_type == VDIR) 5595331Samw return (0); 5605331Samw 5615331Samw smb_rwx_xenter(&node->n_lock); 5625331Samw if (node && (node->flags & NODE_FLAGS_SET_SIZE)) 5635331Samw size = node->n_size; 5645331Samw else 5655331Samw size = attr->sa_vattr.va_size; 5665331Samw smb_rwx_xexit(&node->n_lock); 5675331Samw return (size); 5685331Samw } 5695331Samw 5705331Samw static int 5715331Samw timeval_cmp(timestruc_t *a, timestruc_t *b) 5725331Samw { 5735331Samw if (a->tv_sec < b->tv_sec) 5745331Samw return (-1); 5755331Samw if (a->tv_sec > b->tv_sec) 5765331Samw return (1); 5775331Samw /* Seconds are equal compare tv_nsec */ 5785331Samw if (a->tv_nsec < b->tv_nsec) 5795331Samw return (-1); 5805331Samw return (a->tv_nsec > b->tv_nsec); 5815331Samw } 5825331Samw 5835331Samw /* 5845331Samw * smb_node_set_time 5855331Samw * 5865331Samw * This function will update the time stored in the node and 5875331Samw * set the appropriate flags. If there is nothing to update 5885331Samw * or the node is readonly, the function would return without 5895331Samw * any updates. The update is only in the node level and the 5905331Samw * attribute in the file system will be updated when client 5915331Samw * close the file. 5925331Samw */ 5935331Samw void 5945331Samw smb_node_set_time(struct smb_node *node, struct timestruc *crtime, 5955331Samw struct timestruc *mtime, struct timestruc *atime, 5965331Samw struct timestruc *ctime, unsigned int what) 5975331Samw { 5985331Samw smb_rwx_xenter(&node->n_lock); 5995331Samw if (node->flags & NODE_READ_ONLY || what == 0) { 6005331Samw smb_rwx_xexit(&node->n_lock); 6015331Samw return; 6025331Samw } 6035331Samw 6045331Samw if ((what & SMB_AT_CRTIME && crtime == 0) || 6055331Samw (what & SMB_AT_MTIME && mtime == 0) || 6065331Samw (what & SMB_AT_ATIME && atime == 0) || 6075331Samw (what & SMB_AT_CTIME && ctime == 0)) { 6085331Samw smb_rwx_xexit(&node->n_lock); 6095331Samw return; 6105331Samw } 6115331Samw 6125331Samw if ((what & SMB_AT_CRTIME) && 6135331Samw timeval_cmp((timestruc_t *)&node->attr.sa_crtime, 6145331Samw crtime) != 0) { 6155331Samw node->what |= SMB_AT_CRTIME; 6165331Samw node->attr.sa_crtime = *((timestruc_t *)crtime); 6175331Samw } 6185331Samw 6195331Samw if ((what & SMB_AT_MTIME) && 6205331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime, 6215331Samw mtime) != 0) { 6225331Samw node->what |= SMB_AT_MTIME; 6235331Samw node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime); 6245331Samw } 6255331Samw 6265331Samw if ((what & SMB_AT_ATIME) && 6275331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime, 6285331Samw atime) != 0) { 6295331Samw node->what |= SMB_AT_ATIME; 6305331Samw node->attr.sa_vattr.va_atime = *((timestruc_t *)atime); 6315331Samw } 6325331Samw 6335331Samw /* 6345331Samw * The ctime handling is trickier. It has three scenarios. 6355331Samw * 1. Only ctime need to be set and it is the same as the ctime 6365331Samw * stored in the node. (update not necessary) 6375331Samw * 2. The ctime is the same as the ctime stored in the node but 6385331Samw * is not the only time need to be set. (update required) 6395331Samw * 3. The ctime need to be set and is not the same as the ctime 6405331Samw * stored in the node. (update required) 6415331Samw * Unlike other time setting, the ctime needs to be set even when 6425331Samw * it is the same as the ctime in the node if there are other time 6435331Samw * needs to be set (#2). This will ensure the ctime not being 6445331Samw * updated when other times are being updated in the file system. 6455331Samw * 6465331Samw * Retained file rules: 6475331Samw * 6485331Samw * 1. Don't add SMB_AT_CTIME to node->what by default because the 6495331Samw * request will be rejected by filesystem 6505331Samw * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e. 6515331Samw * any request for changing ctime on these files should have 6525331Samw * been already rejected 6535331Samw */ 6545331Samw node->what |= SMB_AT_CTIME; 6555331Samw if (what & SMB_AT_CTIME) { 6565331Samw if ((what == SMB_AT_CTIME) && 6575331Samw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime, 6585331Samw ctime) == 0) { 6595331Samw node->what &= ~SMB_AT_CTIME; 6605331Samw } else { 6615331Samw gethrestime(&node->attr.sa_vattr.va_ctime); 6625331Samw } 6635331Samw } else { 6645331Samw gethrestime(&node->attr.sa_vattr.va_ctime); 6655331Samw } 6665331Samw smb_rwx_xexit(&node->n_lock); 6675331Samw } 6685331Samw 6695331Samw 6705331Samw timestruc_t * 6715331Samw smb_node_get_crtime(smb_node_t *node) 6725331Samw { 6735331Samw return ((timestruc_t *)&node->attr.sa_crtime); 6745331Samw } 6755331Samw 6765331Samw timestruc_t * 6775331Samw smb_node_get_atime(smb_node_t *node) 6785331Samw { 6795331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_atime); 6805331Samw } 6815331Samw 6825331Samw timestruc_t * 6835331Samw smb_node_get_ctime(smb_node_t *node) 6845331Samw { 6855331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_ctime); 6865331Samw } 6875331Samw 6885331Samw timestruc_t * 6895331Samw smb_node_get_mtime(smb_node_t *node) 6905331Samw { 6915331Samw return ((timestruc_t *)&node->attr.sa_vattr.va_mtime); 6925331Samw } 6935331Samw 6945331Samw /* 6955331Samw * smb_node_set_dosattr 6965331Samw * 6975331Samw * Parse the specified DOS attributes and, if they have been modified, 6985331Samw * update the node cache. This call should be followed by a 6995331Samw * smb_sync_fsattr() call to write the attribute changes to filesystem. 7005331Samw */ 7015331Samw void 7025331Samw smb_node_set_dosattr(smb_node_t *node, uint32_t dos_attr) 7035331Samw { 7045331Samw unsigned int mode; /* New mode */ 7055331Samw 7065331Samw mode = 0; 7075331Samw 7085331Samw /* Handle the archive bit */ 7095331Samw if (dos_attr & SMB_FA_ARCHIVE) 7105331Samw mode |= FILE_ATTRIBUTE_ARCHIVE; 7115331Samw 7125331Samw /* Handle the readonly bit */ 7135331Samw if (dos_attr & SMB_FA_READONLY) 7145331Samw mode |= FILE_ATTRIBUTE_READONLY; 7155331Samw 7165331Samw /* Handle the hidden bit */ 7175331Samw if (dos_attr & SMB_FA_HIDDEN) 7185331Samw mode |= FILE_ATTRIBUTE_HIDDEN; 7195331Samw 7205331Samw /* Handle the system bit */ 7215331Samw if (dos_attr & SMB_FA_SYSTEM) 7225331Samw mode |= FILE_ATTRIBUTE_SYSTEM; 7235331Samw 7245331Samw smb_rwx_xenter(&node->n_lock); 7255331Samw if (node->attr.sa_dosattr != mode) { 7265331Samw node->attr.sa_dosattr = mode; 7275331Samw node->what |= SMB_AT_DOSATTR; 7285331Samw } 7295331Samw smb_rwx_xexit(&node->n_lock); 7305331Samw } 7315331Samw 7325331Samw /* 7335331Samw * smb_node_get_dosattr 7345331Samw * 7355331Samw * This function will get dos attribute using the node. 7365331Samw */ 7375331Samw uint32_t 7385331Samw smb_node_get_dosattr(smb_node_t *node) 7395331Samw { 7405331Samw return (smb_mode_to_dos_attributes(&node->attr)); 7415331Samw } 7425331Samw 7435331Samw int 7445331Samw smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr) 7455331Samw { 7465331Samw int rc = -1; 7475331Samw 7485331Samw smb_rwx_xenter(&node->n_lock); 7495331Samw if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) && 7505331Samw !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) { 7515331Samw crhold(cr); 7525331Samw node->delete_on_close_cred = cr; 7535331Samw node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; 7545331Samw rc = 0; 7555331Samw } 7565331Samw smb_rwx_xexit(&node->n_lock); 7575331Samw return (rc); 7585331Samw } 7595331Samw 7605331Samw void 7615331Samw smb_node_reset_delete_on_close(smb_node_t *node) 7625331Samw { 7635331Samw smb_rwx_xenter(&node->n_lock); 7645331Samw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 7655331Samw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 7665331Samw crfree(node->delete_on_close_cred); 7675331Samw node->delete_on_close_cred = NULL; 7685331Samw } 7695331Samw smb_rwx_xexit(&node->n_lock); 7705331Samw } 771*5772Sas200622 772*5772Sas200622 /* 773*5772Sas200622 * smb_node_share_check 774*5772Sas200622 * 775*5772Sas200622 * check file sharing rules for current open request 776*5772Sas200622 * against all existing opens for a file. 777*5772Sas200622 * 778*5772Sas200622 * Returns NT_STATUS_SHARING_VIOLATION if there is any 779*5772Sas200622 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 780*5772Sas200622 */ 781*5772Sas200622 uint32_t 782*5772Sas200622 smb_node_open_check(struct smb_node *node, cred_t *cr, 783*5772Sas200622 uint32_t desired_access, uint32_t share_access) 784*5772Sas200622 { 785*5772Sas200622 smb_ofile_t *of; 786*5772Sas200622 uint32_t status; 787*5772Sas200622 788*5772Sas200622 ASSERT(node); 789*5772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 790*5772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 791*5772Sas200622 792*5772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 793*5772Sas200622 of = smb_llist_head(&node->n_ofile_list); 794*5772Sas200622 while (of) { 795*5772Sas200622 status = smb_node_share_check(node, cr, desired_access, 796*5772Sas200622 share_access, of); 797*5772Sas200622 if (status == NT_STATUS_SHARING_VIOLATION) { 798*5772Sas200622 smb_llist_exit(&node->n_ofile_list); 799*5772Sas200622 return (status); 800*5772Sas200622 } 801*5772Sas200622 of = smb_llist_next(&node->n_ofile_list, of); 802*5772Sas200622 } 803*5772Sas200622 smb_llist_exit(&node->n_ofile_list); 804*5772Sas200622 805*5772Sas200622 return (NT_STATUS_SUCCESS); 806*5772Sas200622 } 807*5772Sas200622 808*5772Sas200622 /* 809*5772Sas200622 * smb_open_share_check 810*5772Sas200622 * 811*5772Sas200622 * check file sharing rules for current open request 812*5772Sas200622 * against the given existing open. 813*5772Sas200622 * 814*5772Sas200622 * Returns NT_STATUS_SHARING_VIOLATION if there is any 815*5772Sas200622 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 816*5772Sas200622 */ 817*5772Sas200622 uint32_t 818*5772Sas200622 smb_node_share_check( 819*5772Sas200622 struct smb_node *node, 820*5772Sas200622 cred_t *cr, 821*5772Sas200622 uint32_t desired_access, 822*5772Sas200622 uint32_t share_access, 823*5772Sas200622 smb_ofile_t *of) 824*5772Sas200622 { 825*5772Sas200622 /* 826*5772Sas200622 * It appears that share modes are not relevant to 827*5772Sas200622 * directories, but this check will remain as it is not 828*5772Sas200622 * clear whether it was originally put here for a reason. 829*5772Sas200622 */ 830*5772Sas200622 if (node->attr.sa_vattr.va_type == VDIR) { 831*5772Sas200622 if (SMB_DENY_RW(of->f_share_access) && 832*5772Sas200622 (node->n_orig_uid != crgetuid(cr))) { 833*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 834*5772Sas200622 } 835*5772Sas200622 836*5772Sas200622 return (NT_STATUS_SUCCESS); 837*5772Sas200622 } 838*5772Sas200622 839*5772Sas200622 /* if it's just meta data */ 840*5772Sas200622 if ((of->f_granted_access & FILE_DATA_ALL) == 0) 841*5772Sas200622 return (NT_STATUS_SUCCESS); 842*5772Sas200622 843*5772Sas200622 /* 844*5772Sas200622 * Check requested share access against the 845*5772Sas200622 * open granted (desired) access 846*5772Sas200622 */ 847*5772Sas200622 if (SMB_DENY_DELETE(share_access) && (of->f_granted_access & DELETE)) 848*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 849*5772Sas200622 850*5772Sas200622 if (SMB_DENY_READ(share_access) && 851*5772Sas200622 (of->f_granted_access & (FILE_READ_DATA | FILE_EXECUTE))) 852*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 853*5772Sas200622 854*5772Sas200622 if (SMB_DENY_WRITE(share_access) && 855*5772Sas200622 (of->f_granted_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) 856*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 857*5772Sas200622 858*5772Sas200622 /* check requested desired access against the open share access */ 859*5772Sas200622 if (SMB_DENY_DELETE(of->f_share_access) && (desired_access & DELETE)) 860*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 861*5772Sas200622 862*5772Sas200622 if (SMB_DENY_READ(of->f_share_access) && 863*5772Sas200622 (desired_access & (FILE_READ_DATA | FILE_EXECUTE))) 864*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 865*5772Sas200622 866*5772Sas200622 if (SMB_DENY_WRITE(of->f_share_access) && 867*5772Sas200622 (desired_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) 868*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 869*5772Sas200622 870*5772Sas200622 return (NT_STATUS_SUCCESS); 871*5772Sas200622 } 872*5772Sas200622 873*5772Sas200622 /* 874*5772Sas200622 * smb_rename_share_check 875*5772Sas200622 * 876*5772Sas200622 * An open file can be renamed if 877*5772Sas200622 * 878*5772Sas200622 * 1. isn't opened for data writing or deleting 879*5772Sas200622 * 880*5772Sas200622 * 2. Opened with "Deny Delete" share mode 881*5772Sas200622 * But not opened for data reading or executing 882*5772Sas200622 * (opened for accessing meta data) 883*5772Sas200622 */ 884*5772Sas200622 885*5772Sas200622 DWORD 886*5772Sas200622 smb_node_rename_check(struct smb_node *node) 887*5772Sas200622 { 888*5772Sas200622 struct smb_ofile *open; 889*5772Sas200622 890*5772Sas200622 ASSERT(node); 891*5772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 892*5772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 893*5772Sas200622 894*5772Sas200622 /* 895*5772Sas200622 * Intra-CIFS check 896*5772Sas200622 */ 897*5772Sas200622 898*5772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 899*5772Sas200622 open = smb_llist_head(&node->n_ofile_list); 900*5772Sas200622 while (open) { 901*5772Sas200622 if (open->f_granted_access & 902*5772Sas200622 (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) { 903*5772Sas200622 smb_llist_exit(&node->n_ofile_list); 904*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 905*5772Sas200622 } 906*5772Sas200622 907*5772Sas200622 if ((open->f_share_access & FILE_SHARE_DELETE) == 0) { 908*5772Sas200622 if (open->f_granted_access & 909*5772Sas200622 (FILE_READ_DATA | FILE_EXECUTE)) { 910*5772Sas200622 smb_llist_exit(&node->n_ofile_list); 911*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 912*5772Sas200622 } 913*5772Sas200622 } 914*5772Sas200622 open = smb_llist_next(&node->n_ofile_list, open); 915*5772Sas200622 } 916*5772Sas200622 smb_llist_exit(&node->n_ofile_list); 917*5772Sas200622 918*5772Sas200622 /* 919*5772Sas200622 * system-wide share check 920*5772Sas200622 */ 921*5772Sas200622 922*5772Sas200622 if (nbl_share_conflict(node->vp, NBL_RENAME, NULL)) 923*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 924*5772Sas200622 else 925*5772Sas200622 return (NT_STATUS_SUCCESS); 926*5772Sas200622 } 927*5772Sas200622 928*5772Sas200622 /* 929*5772Sas200622 * smb_node_delete_check 930*5772Sas200622 * 931*5772Sas200622 * An open file can be deleted only if opened for 932*5772Sas200622 * accessing meta data. Share modes aren't important 933*5772Sas200622 * in this case. 934*5772Sas200622 * 935*5772Sas200622 * NOTE: there is another mechanism for deleting an 936*5772Sas200622 * open file that NT clients usually use. 937*5772Sas200622 * That's setting "Delete on close" flag for an open 938*5772Sas200622 * file. In this way the file will be deleted after 939*5772Sas200622 * last close. This flag can be set by SmbTrans2SetFileInfo 940*5772Sas200622 * with FILE_DISPOSITION_INFO information level. 941*5772Sas200622 * For setting this flag, the file should be opened by 942*5772Sas200622 * DELETE access in the FID that is passed in the Trans2 943*5772Sas200622 * request. 944*5772Sas200622 */ 945*5772Sas200622 DWORD 946*5772Sas200622 smb_node_delete_check(smb_node_t *node) 947*5772Sas200622 { 948*5772Sas200622 smb_ofile_t *file; 949*5772Sas200622 950*5772Sas200622 ASSERT(node); 951*5772Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 952*5772Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 953*5772Sas200622 954*5772Sas200622 if (node->attr.sa_vattr.va_type == VDIR) 955*5772Sas200622 return (NT_STATUS_SUCCESS); 956*5772Sas200622 957*5772Sas200622 /* 958*5772Sas200622 * intra-CIFS check 959*5772Sas200622 */ 960*5772Sas200622 961*5772Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 962*5772Sas200622 file = smb_llist_head(&node->n_ofile_list); 963*5772Sas200622 while (file) { 964*5772Sas200622 ASSERT(file->f_magic == SMB_OFILE_MAGIC); 965*5772Sas200622 if (file->f_granted_access & 966*5772Sas200622 (FILE_READ_DATA | 967*5772Sas200622 FILE_WRITE_DATA | 968*5772Sas200622 FILE_APPEND_DATA | 969*5772Sas200622 FILE_EXECUTE | 970*5772Sas200622 DELETE)) { 971*5772Sas200622 smb_llist_exit(&node->n_ofile_list); 972*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 973*5772Sas200622 } 974*5772Sas200622 file = smb_llist_next(&node->n_ofile_list, file); 975*5772Sas200622 } 976*5772Sas200622 smb_llist_exit(&node->n_ofile_list); 977*5772Sas200622 978*5772Sas200622 /* 979*5772Sas200622 * system-wide share check 980*5772Sas200622 */ 981*5772Sas200622 982*5772Sas200622 if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL)) 983*5772Sas200622 return (NT_STATUS_SHARING_VIOLATION); 984*5772Sas200622 else 985*5772Sas200622 return (NT_STATUS_SUCCESS); 986*5772Sas200622 } 987*5772Sas200622 988*5772Sas200622 /* 989*5772Sas200622 * smb_node_start_crit() 990*5772Sas200622 * 991*5772Sas200622 * Enter critical region for share reservations. 992*5772Sas200622 * See comments above smb_fsop_shrlock(). 993*5772Sas200622 */ 994*5772Sas200622 995*5772Sas200622 void 996*5772Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode) 997*5772Sas200622 { 998*5772Sas200622 rw_enter(&node->n_share_lock, mode); 999*5772Sas200622 nbl_start_crit(node->vp, mode); 1000*5772Sas200622 } 1001*5772Sas200622 1002*5772Sas200622 /* 1003*5772Sas200622 * smb_node_end_crit() 1004*5772Sas200622 * 1005*5772Sas200622 * Exit critical region for share reservations. 1006*5772Sas200622 */ 1007*5772Sas200622 1008*5772Sas200622 void 1009*5772Sas200622 smb_node_end_crit(smb_node_t *node) 1010*5772Sas200622 { 1011*5772Sas200622 nbl_end_crit(node->vp); 1012*5772Sas200622 rw_exit(&node->n_share_lock); 1013*5772Sas200622 } 1014*5772Sas200622 1015*5772Sas200622 int 1016*5772Sas200622 smb_node_in_crit(smb_node_t *node) 1017*5772Sas200622 { 1018*5772Sas200622 return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock)); 1019*5772Sas200622 } 1020