1da6c28aaSamw /* 2da6c28aaSamw * CDDL HEADER START 3da6c28aaSamw * 4da6c28aaSamw * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 7da6c28aaSamw * 8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10da6c28aaSamw * See the License for the specific language governing permissions 11da6c28aaSamw * and limitations under the License. 12da6c28aaSamw * 13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18da6c28aaSamw * 19da6c28aaSamw * CDDL HEADER END 20da6c28aaSamw */ 21da6c28aaSamw /* 22*7f667e74Sjose borrego * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23da6c28aaSamw * Use is subject to license terms. 24da6c28aaSamw */ 25da6c28aaSamw 26c8ec8eeaSjose borrego #pragma ident "@(#)smb_node.c 1.9 08/08/07 SMI" 27da6c28aaSamw 28da6c28aaSamw /* 29da6c28aaSamw * SMB Node State Machine 30da6c28aaSamw * ---------------------- 31da6c28aaSamw * 32da6c28aaSamw * +----------------------------+ T0 33da6c28aaSamw * | SMB_NODE_STATE_AVAILABLE |<----------- Creation/Allocation 34da6c28aaSamw * +----------------------------+ 35da6c28aaSamw * | 36da6c28aaSamw * | T1 37da6c28aaSamw * | 38da6c28aaSamw * v 39da6c28aaSamw * +-----------------------------+ T2 40da6c28aaSamw * | SMB_NODE_STATE_DESTROYING |----------> Deletion/Free 41da6c28aaSamw * +-----------------------------+ 42da6c28aaSamw * 43da6c28aaSamw * Transition T0 44da6c28aaSamw * 45da6c28aaSamw * This transition occurs in smb_node_lookup(). If the node looked for is 46da6c28aaSamw * not found in the has table a new node is created. The reference count is 47da6c28aaSamw * initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE. 48da6c28aaSamw * 49da6c28aaSamw * Transition T1 50da6c28aaSamw * 51da6c28aaSamw * This transition occurs in smb_node_release(). If the reference count 52da6c28aaSamw * drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more 53da6c28aaSamw * reference count will be given out for that node. 54da6c28aaSamw * 55da6c28aaSamw * Transition T2 56da6c28aaSamw * 57da6c28aaSamw * This transition occurs in smb_node_release(). The structure is deleted. 58da6c28aaSamw * 59da6c28aaSamw * Comments 60da6c28aaSamw * -------- 61da6c28aaSamw * 62da6c28aaSamw * The reason the smb node has 2 states is the following synchronization 63da6c28aaSamw * rule: 64da6c28aaSamw * 65da6c28aaSamw * There's a mutex embedded in the node used to protect its fields and 66da6c28aaSamw * there's a lock embedded in the bucket of the hash table the node belongs 67da6c28aaSamw * to. To increment or to decrement the reference count the mutex must be 68da6c28aaSamw * entered. To insert the node into the bucket and to remove it from the 69da6c28aaSamw * bucket the lock must be entered in RW_WRITER mode. When both (mutex and 70da6c28aaSamw * lock) have to be entered, the lock has always to be entered first then 71da6c28aaSamw * the mutex. This prevents a deadlock between smb_node_lookup() and 72da6c28aaSamw * smb_node_release() from occurring. However, in smb_node_release() when the 73da6c28aaSamw * reference count drops to zero and triggers the deletion of the node, the 74da6c28aaSamw * mutex has to be released before entering the lock of the bucket (to 75da6c28aaSamw * remove the node). This creates a window during which the node that is 76da6c28aaSamw * about to be freed could be given out by smb_node_lookup(). To close that 77da6c28aaSamw * window the node is moved to the state SMB_NODE_STATE_DESTROYING before 78da6c28aaSamw * releasing the mutex. That way, even if smb_node_lookup() finds it, the 79da6c28aaSamw * state will indicate that the node should be treated as non existent (of 80da6c28aaSamw * course the state of the node should be tested/updated under the 81da6c28aaSamw * protection of the mutex). 82da6c28aaSamw */ 83da6c28aaSamw #include <smbsrv/smb_incl.h> 84da6c28aaSamw #include <smbsrv/smb_fsops.h> 85da6c28aaSamw #include <sys/pathname.h> 86da6c28aaSamw #include <sys/sdt.h> 87dc20a302Sas200622 #include <sys/nbmlock.h> 88da6c28aaSamw 89da6c28aaSamw uint32_t smb_is_executable(char *path); 90da6c28aaSamw static void smb_node_delete_on_close(smb_node_t *node); 91da6c28aaSamw 92da6c28aaSamw #define VALIDATE_DIR_NODE(_dir_, _node_) \ 93da6c28aaSamw ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \ 94da6c28aaSamw ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \ 95da6c28aaSamw ASSERT((_dir_)->dir_snode != (_node_)); 96da6c28aaSamw 97faa1795aSjb150015 static boolean_t smb_node_initialized = B_FALSE; 98faa1795aSjb150015 static smb_llist_t smb_node_hash_table[SMBND_HASH_MASK+1]; 99faa1795aSjb150015 100faa1795aSjb150015 /* 101faa1795aSjb150015 * smb_node_init 102faa1795aSjb150015 * 103faa1795aSjb150015 * Initialization of the SMB node layer. 104faa1795aSjb150015 * 105faa1795aSjb150015 * This function is not multi-thread safe. The caller must make sure only one 106faa1795aSjb150015 * thread makes the call. 107faa1795aSjb150015 */ 108faa1795aSjb150015 int 109faa1795aSjb150015 smb_node_init(void) 110faa1795aSjb150015 { 111faa1795aSjb150015 int i; 112faa1795aSjb150015 113faa1795aSjb150015 if (smb_node_initialized) 114faa1795aSjb150015 return (0); 115faa1795aSjb150015 116faa1795aSjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 117faa1795aSjb150015 smb_llist_constructor(&smb_node_hash_table[i], 118faa1795aSjb150015 sizeof (smb_node_t), offsetof(smb_node_t, n_lnd)); 119faa1795aSjb150015 } 120faa1795aSjb150015 smb_node_initialized = B_TRUE; 121faa1795aSjb150015 return (0); 122faa1795aSjb150015 } 123faa1795aSjb150015 124faa1795aSjb150015 /* 125faa1795aSjb150015 * smb_node_fini 126faa1795aSjb150015 * 127faa1795aSjb150015 * This function is not multi-thread safe. The caller must make sure only one 128faa1795aSjb150015 * thread makes the call. 129faa1795aSjb150015 */ 130faa1795aSjb150015 void 131faa1795aSjb150015 smb_node_fini(void) 132faa1795aSjb150015 { 133faa1795aSjb150015 int i; 134faa1795aSjb150015 135faa1795aSjb150015 if (!smb_node_initialized) 136faa1795aSjb150015 return; 137faa1795aSjb150015 138faa1795aSjb150015 #ifdef DEBUG 139faa1795aSjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 140faa1795aSjb150015 smb_node_t *node; 141faa1795aSjb150015 142faa1795aSjb150015 /* 143faa1795aSjb150015 * The following sequence is just intended for sanity check. 144faa1795aSjb150015 * This will have to be modified when the code goes into 145faa1795aSjb150015 * production. 146faa1795aSjb150015 * 147faa1795aSjb150015 * The SMB node hash table should be emtpy at this point. If the 148faa1795aSjb150015 * hash table is not empty a panic will be triggered. 149faa1795aSjb150015 * 150faa1795aSjb150015 * The reason why SMB nodes are still remaining in the hash 151faa1795aSjb150015 * table is problably due to a mismatch between calls to 152faa1795aSjb150015 * smb_node_lookup() and smb_node_release(). You must track that 153faa1795aSjb150015 * down. 154faa1795aSjb150015 */ 155faa1795aSjb150015 node = smb_llist_head(&smb_node_hash_table[i]); 156faa1795aSjb150015 ASSERT(node == NULL); 157faa1795aSjb150015 } 158faa1795aSjb150015 #endif 159faa1795aSjb150015 160faa1795aSjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 161faa1795aSjb150015 smb_llist_destructor(&smb_node_hash_table[i]); 162faa1795aSjb150015 } 163faa1795aSjb150015 smb_node_initialized = B_FALSE; 164faa1795aSjb150015 } 165faa1795aSjb150015 166da6c28aaSamw /* 167da6c28aaSamw * smb_node_lookup() 168da6c28aaSamw * 169da6c28aaSamw * NOTE: This routine should only be called by the file system interface layer, 170da6c28aaSamw * and not by SMB. 171da6c28aaSamw * 172da6c28aaSamw * smb_node_lookup() is called upon successful lookup, mkdir, and create 173da6c28aaSamw * (for both non-streams and streams). In each of these cases, a held vnode is 174*7f667e74Sjose borrego * passed into this routine. If a new smb_node is created it will take its 175*7f667e74Sjose borrego * own hold on the vnode. The caller's hold therefore still belongs to, and 176*7f667e74Sjose borrego * should be released by, the caller. 177da6c28aaSamw * 178da6c28aaSamw * A reference is taken on the smb_node whether found in the hash table 179da6c28aaSamw * or newly created. 180da6c28aaSamw * 181da6c28aaSamw * If an smb_node needs to be created, a reference is also taken on the 182da6c28aaSamw * dir_snode (if passed in). 183da6c28aaSamw * 184da6c28aaSamw * See smb_node_release() for details on the release of these references. 185da6c28aaSamw */ 186da6c28aaSamw 187da6c28aaSamw /*ARGSUSED*/ 188da6c28aaSamw smb_node_t * 189da6c28aaSamw smb_node_lookup( 190da6c28aaSamw struct smb_request *sr, 191da6c28aaSamw struct open_param *op, 192da6c28aaSamw cred_t *cred, 193da6c28aaSamw vnode_t *vp, 194da6c28aaSamw char *od_name, 195da6c28aaSamw smb_node_t *dir_snode, 196da6c28aaSamw smb_node_t *unnamed_node, 197da6c28aaSamw smb_attr_t *attr) 198da6c28aaSamw { 199da6c28aaSamw smb_llist_t *node_hdr; 200da6c28aaSamw smb_node_t *node; 201da6c28aaSamw uint32_t hashkey = 0; 202c8ec8eeaSjose borrego fsid_t fsid; 203da6c28aaSamw int error; 204da6c28aaSamw krw_t lock_mode; 205da6c28aaSamw vnode_t *unnamed_vp = NULL; 206da6c28aaSamw 207da6c28aaSamw /* 208da6c28aaSamw * smb_vop_getattr() is called here instead of smb_fsop_getattr(), 209da6c28aaSamw * because the node may not yet exist. We also do not want to call 210da6c28aaSamw * it with the list lock held. 211da6c28aaSamw */ 212da6c28aaSamw 213da6c28aaSamw if (unnamed_node) 214da6c28aaSamw unnamed_vp = unnamed_node->vp; 215da6c28aaSamw 216da6c28aaSamw /* 217da6c28aaSamw * This getattr is performed on behalf of the server 218da6c28aaSamw * that's why kcred is used not the user's cred 219da6c28aaSamw */ 220da6c28aaSamw attr->sa_mask = SMB_AT_ALL; 221dc20a302Sas200622 error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred); 222da6c28aaSamw if (error) 223da6c28aaSamw return (NULL); 224da6c28aaSamw 225c8ec8eeaSjose borrego if (sr && sr->tid_tree) { 226da6c28aaSamw /* 227c8ec8eeaSjose borrego * The fsid for a file is that of the tree, even 228da6c28aaSamw * if the file resides in a different mountpoint 229da6c28aaSamw * under the share. 230da6c28aaSamw */ 231c8ec8eeaSjose borrego fsid = SMB_TREE_FSID(sr->tid_tree); 232da6c28aaSamw } else { 233da6c28aaSamw /* 234da6c28aaSamw * This should be getting executed only for the 235c8ec8eeaSjose borrego * tree root smb_node. 236da6c28aaSamw */ 237c8ec8eeaSjose borrego fsid = vp->v_vfsp->vfs_fsid; 238da6c28aaSamw } 239da6c28aaSamw 240c8ec8eeaSjose borrego hashkey = fsid.val[0] + attr->sa_vattr.va_nodeid; 241da6c28aaSamw hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 242faa1795aSjb150015 node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]; 243da6c28aaSamw lock_mode = RW_READER; 244da6c28aaSamw 245da6c28aaSamw smb_llist_enter(node_hdr, lock_mode); 246da6c28aaSamw for (;;) { 247da6c28aaSamw node = list_head(&node_hdr->ll_list); 248da6c28aaSamw while (node) { 249da6c28aaSamw ASSERT(node->n_magic == SMB_NODE_MAGIC); 250da6c28aaSamw ASSERT(node->n_hash_bucket == node_hdr); 251da6c28aaSamw if ((node->n_hashkey == hashkey) && (node->vp == vp)) { 252da6c28aaSamw smb_rwx_xenter(&node->n_lock); 253da6c28aaSamw DTRACE_PROBE1(smb_node_lookup_hit, 254da6c28aaSamw smb_node_t *, node); 255da6c28aaSamw switch (node->n_state) { 256da6c28aaSamw case SMB_NODE_STATE_AVAILABLE: 257da6c28aaSamw /* The node was found. */ 258da6c28aaSamw node->n_refcnt++; 259da6c28aaSamw if ((node->dir_snode == NULL) && 260da6c28aaSamw (dir_snode != NULL) && 261da6c28aaSamw (strcmp(od_name, "..") != 0) && 262da6c28aaSamw (strcmp(od_name, ".") != 0)) { 263da6c28aaSamw VALIDATE_DIR_NODE(dir_snode, 264da6c28aaSamw node); 265da6c28aaSamw node->dir_snode = dir_snode; 266da6c28aaSamw smb_node_ref(dir_snode); 267da6c28aaSamw } 268da6c28aaSamw node->attr = *attr; 269dc20a302Sas200622 node->n_size = attr->sa_vattr.va_size; 270da6c28aaSamw 271da6c28aaSamw smb_audit_node(node); 272da6c28aaSamw smb_rwx_xexit(&node->n_lock); 273da6c28aaSamw smb_llist_exit(node_hdr); 274da6c28aaSamw return (node); 275da6c28aaSamw 276da6c28aaSamw case SMB_NODE_STATE_DESTROYING: 277da6c28aaSamw /* 278da6c28aaSamw * Although the node exists it is about 279da6c28aaSamw * to be destroyed. We act as it hasn't 280da6c28aaSamw * been found. 281da6c28aaSamw */ 282da6c28aaSamw smb_rwx_xexit(&node->n_lock); 283da6c28aaSamw break; 284da6c28aaSamw default: 285da6c28aaSamw /* 286da6c28aaSamw * Although the node exists it is in an 287da6c28aaSamw * unknown state. We act as it hasn't 288da6c28aaSamw * been found. 289da6c28aaSamw */ 290da6c28aaSamw ASSERT(0); 291da6c28aaSamw smb_rwx_xexit(&node->n_lock); 292da6c28aaSamw break; 293da6c28aaSamw } 294da6c28aaSamw } 295da6c28aaSamw node = smb_llist_next(node_hdr, node); 296da6c28aaSamw } 297da6c28aaSamw if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) { 298da6c28aaSamw lock_mode = RW_WRITER; 299da6c28aaSamw continue; 300da6c28aaSamw } 301da6c28aaSamw break; 302da6c28aaSamw } 303faa1795aSjb150015 node = kmem_cache_alloc(sr->sr_server->si_cache_node, KM_SLEEP); 304da6c28aaSamw bzero(node, sizeof (smb_node_t)); 305da6c28aaSamw 306da6c28aaSamw node->n_state = SMB_NODE_STATE_AVAILABLE; 307da6c28aaSamw node->n_hash_bucket = node_hdr; 308faa1795aSjb150015 node->n_sr = sr; 309faa1795aSjb150015 node->vp = vp; 310*7f667e74Sjose borrego VN_HOLD(node->vp); 311faa1795aSjb150015 node->n_hashkey = hashkey; 312faa1795aSjb150015 node->n_refcnt = 1; 313faa1795aSjb150015 node->attr = *attr; 314faa1795aSjb150015 node->flags |= NODE_FLAGS_ATTR_VALID; 315faa1795aSjb150015 node->n_size = node->attr.sa_vattr.va_size; 316faa1795aSjb150015 node->n_orig_session_id = sr->session->s_kid; 317faa1795aSjb150015 node->n_orig_uid = crgetuid(sr->user_cr); 318faa1795aSjb150015 node->n_cache = sr->sr_server->si_cache_node; 319da6c28aaSamw 320faa1795aSjb150015 ASSERT(od_name); 321faa1795aSjb150015 (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); 322faa1795aSjb150015 323da6c28aaSamw smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 324da6c28aaSamw offsetof(smb_ofile_t, f_nnd)); 325da6c28aaSamw smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 326da6c28aaSamw offsetof(smb_lock_t, l_lnd)); 327da6c28aaSamw 328da6c28aaSamw 329cbfb650aScp160787 if (strcmp(od_name, XATTR_DIR) == 0) 330cbfb650aScp160787 node->flags |= NODE_XATTR_DIR; 331da6c28aaSamw if (op) 332da6c28aaSamw node->flags |= smb_is_executable(op->fqi.last_comp); 333da6c28aaSamw 334da6c28aaSamw if (dir_snode) { 335da6c28aaSamw smb_node_ref(dir_snode); 336da6c28aaSamw node->dir_snode = dir_snode; 337da6c28aaSamw ASSERT(dir_snode->dir_snode != node); 338da6c28aaSamw ASSERT((dir_snode->vp->v_xattrdir) || 339da6c28aaSamw (dir_snode->vp->v_type == VDIR)); 340da6c28aaSamw } 341da6c28aaSamw 342da6c28aaSamw if (unnamed_node) { 343da6c28aaSamw smb_node_ref(unnamed_node); 344da6c28aaSamw node->unnamed_stream_node = unnamed_node; 345da6c28aaSamw } 346da6c28aaSamw 347da6c28aaSamw smb_rwx_init(&node->n_lock); 348da6c28aaSamw node->n_magic = SMB_NODE_MAGIC; 349da6c28aaSamw smb_audit_buf_node_create(node); 350da6c28aaSamw DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); 351da6c28aaSamw smb_audit_node(node); 352da6c28aaSamw smb_llist_insert_head(node_hdr, node); 353faa1795aSjb150015 354da6c28aaSamw smb_llist_exit(node_hdr); 355da6c28aaSamw return (node); 356da6c28aaSamw } 357da6c28aaSamw 358da6c28aaSamw /* 359da6c28aaSamw * smb_stream_node_lookup() 360da6c28aaSamw * 361da6c28aaSamw * Note: stream_name (the name that will be stored in the "od_name" field 362da6c28aaSamw * of a stream's smb_node) is the same as the on-disk name for the stream 363da6c28aaSamw * except that it does not have SMB_STREAM_PREFIX prepended. 364da6c28aaSamw */ 365da6c28aaSamw 366da6c28aaSamw smb_node_t * 367da6c28aaSamw smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, 368da6c28aaSamw vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr) 369da6c28aaSamw { 370da6c28aaSamw smb_node_t *xattrdir_node; 371da6c28aaSamw smb_node_t *snode; 372da6c28aaSamw smb_attr_t tmp_attr; 373da6c28aaSamw 374da6c28aaSamw xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, 375da6c28aaSamw fnode, NULL, &tmp_attr); 376da6c28aaSamw 377da6c28aaSamw if (xattrdir_node == NULL) 378da6c28aaSamw return (NULL); 379da6c28aaSamw 380da6c28aaSamw snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, 381da6c28aaSamw fnode, ret_attr); 382da6c28aaSamw 383da6c28aaSamw (void) smb_node_release(xattrdir_node); 384da6c28aaSamw return (snode); 385da6c28aaSamw } 386da6c28aaSamw 387da6c28aaSamw 388da6c28aaSamw /* 389da6c28aaSamw * This function should be called whenever a reference is needed on an 390da6c28aaSamw * smb_node pointer. The copy of an smb_node pointer from one non-local 391da6c28aaSamw * data structure to another requires a reference to be taken on the smb_node 392da6c28aaSamw * (unless the usage is localized). Each data structure deallocation routine 393da6c28aaSamw * will call smb_node_release() on its smb_node pointers. 394da6c28aaSamw * 395da6c28aaSamw * In general, an smb_node pointer residing in a structure should never be 396da6c28aaSamw * stale. A node pointer may be NULL, however, and care should be taken 397da6c28aaSamw * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. 398da6c28aaSamw * Care also needs to be taken with respect to racing deallocations of a 399da6c28aaSamw * structure. 400da6c28aaSamw */ 401da6c28aaSamw 402da6c28aaSamw void 403da6c28aaSamw smb_node_ref(smb_node_t *node) 404da6c28aaSamw { 405da6c28aaSamw ASSERT(node); 406da6c28aaSamw ASSERT(node->n_magic == SMB_NODE_MAGIC); 407da6c28aaSamw ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 408da6c28aaSamw 409da6c28aaSamw smb_rwx_xenter(&node->n_lock); 410da6c28aaSamw node->n_refcnt++; 411da6c28aaSamw ASSERT(node->n_refcnt); 412da6c28aaSamw DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); 413da6c28aaSamw smb_audit_node(node); 414da6c28aaSamw smb_rwx_xexit(&node->n_lock); 415da6c28aaSamw } 416da6c28aaSamw 417da6c28aaSamw /* 418da6c28aaSamw * smb_node_lookup() takes a hold on an smb_node, whether found in the 419da6c28aaSamw * hash table or newly created. This hold is expected to be released 420da6c28aaSamw * in the following manner. 421da6c28aaSamw * 422da6c28aaSamw * smb_node_lookup() takes an address of an smb_node pointer. This should 423da6c28aaSamw * be getting passed down via a lookup (whether path name or component), mkdir, 424da6c28aaSamw * create. If the original smb_node pointer resides in a data structure, then 425da6c28aaSamw * the deallocation routine for the data structure is responsible for calling 426da6c28aaSamw * smb_node_release() on the smb_node pointer. Alternatively, 427da6c28aaSamw * smb_node_release() can be called as soon as the smb_node pointer is no longer 428da6c28aaSamw * needed. In this case, callers are responsible for setting an embedded 429da6c28aaSamw * pointer to NULL if it is known that the last reference is being released. 430da6c28aaSamw * 431da6c28aaSamw * If the passed-in address of the smb_node pointer belongs to a local variable, 432da6c28aaSamw * then the caller with the local variable should call smb_node_release() 433da6c28aaSamw * directly. 434da6c28aaSamw * 435da6c28aaSamw * smb_node_release() itself will call smb_node_release() on a node's dir_snode, 436da6c28aaSamw * as smb_node_lookup() takes a hold on dir_snode. 437da6c28aaSamw */ 438da6c28aaSamw void 439da6c28aaSamw smb_node_release(smb_node_t *node) 440da6c28aaSamw { 441da6c28aaSamw ASSERT(node); 442da6c28aaSamw ASSERT(node->n_magic == SMB_NODE_MAGIC); 443da6c28aaSamw 444da6c28aaSamw smb_rwx_xenter(&node->n_lock); 445da6c28aaSamw ASSERT(node->n_refcnt); 446da6c28aaSamw DTRACE_PROBE1(smb_node_release, smb_node_t *, node); 447da6c28aaSamw if (--node->n_refcnt == 0) { 448da6c28aaSamw switch (node->n_state) { 449da6c28aaSamw 450da6c28aaSamw case SMB_NODE_STATE_AVAILABLE: 451da6c28aaSamw node->n_state = SMB_NODE_STATE_DESTROYING; 452da6c28aaSamw smb_rwx_xexit(&node->n_lock); 453da6c28aaSamw 454da6c28aaSamw smb_llist_enter(node->n_hash_bucket, RW_WRITER); 455da6c28aaSamw smb_llist_remove(node->n_hash_bucket, node); 456da6c28aaSamw smb_llist_exit(node->n_hash_bucket); 457da6c28aaSamw 458da6c28aaSamw /* 459da6c28aaSamw * Check if the file was deleted 460da6c28aaSamw */ 461da6c28aaSamw smb_node_delete_on_close(node); 462da6c28aaSamw node->n_magic = (uint32_t)~SMB_NODE_MAGIC; 463da6c28aaSamw 464da6c28aaSamw /* These lists should be empty. */ 465da6c28aaSamw smb_llist_destructor(&node->n_ofile_list); 466da6c28aaSamw smb_llist_destructor(&node->n_lock_list); 467da6c28aaSamw 468da6c28aaSamw if (node->dir_snode) { 469da6c28aaSamw ASSERT(node->dir_snode->n_magic == 470da6c28aaSamw SMB_NODE_MAGIC); 471da6c28aaSamw smb_node_release(node->dir_snode); 472da6c28aaSamw } 473da6c28aaSamw 474da6c28aaSamw if (node->unnamed_stream_node) { 475da6c28aaSamw ASSERT(node->unnamed_stream_node->n_magic == 476da6c28aaSamw SMB_NODE_MAGIC); 477da6c28aaSamw smb_node_release(node->unnamed_stream_node); 478da6c28aaSamw } 479da6c28aaSamw 480da6c28aaSamw ASSERT(node->vp); 481da6c28aaSamw VN_RELE(node->vp); 482da6c28aaSamw 483da6c28aaSamw smb_audit_buf_node_destroy(node); 484da6c28aaSamw smb_rwx_destroy(&node->n_lock); 485faa1795aSjb150015 kmem_cache_free(node->n_cache, node); 486da6c28aaSamw return; 487da6c28aaSamw 488da6c28aaSamw default: 489da6c28aaSamw ASSERT(0); 490da6c28aaSamw break; 491da6c28aaSamw } 492da6c28aaSamw } 493da6c28aaSamw smb_audit_node(node); 494da6c28aaSamw smb_rwx_xexit(&node->n_lock); 495da6c28aaSamw } 496da6c28aaSamw 497da6c28aaSamw static void 498da6c28aaSamw smb_node_delete_on_close(smb_node_t *node) 499da6c28aaSamw { 500da6c28aaSamw smb_node_t *d_snode; 501da6c28aaSamw int rc = 0; 502da6c28aaSamw 503da6c28aaSamw d_snode = node->dir_snode; 504da6c28aaSamw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 505da6c28aaSamw 506da6c28aaSamw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 507da6c28aaSamw ASSERT(node->od_name != NULL); 508da6c28aaSamw if (node->attr.sa_vattr.va_type == VDIR) 509da6c28aaSamw rc = smb_fsop_rmdir(0, node->delete_on_close_cred, 510da6c28aaSamw d_snode, node->od_name, 1); 511da6c28aaSamw else 512da6c28aaSamw rc = smb_fsop_remove(0, node->delete_on_close_cred, 513da6c28aaSamw d_snode, node->od_name, 1); 514da6c28aaSamw smb_cred_rele(node->delete_on_close_cred); 515da6c28aaSamw } 516da6c28aaSamw if (rc != 0) 517da6c28aaSamw cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", 518da6c28aaSamw node->od_name, rc); 519da6c28aaSamw DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); 520da6c28aaSamw } 521da6c28aaSamw 522da6c28aaSamw /* 523da6c28aaSamw * smb_node_rename() 524da6c28aaSamw * 525da6c28aaSamw */ 526da6c28aaSamw int 527da6c28aaSamw smb_node_rename( 528da6c28aaSamw smb_node_t *from_dir_snode, 529da6c28aaSamw smb_node_t *ret_snode, 530da6c28aaSamw smb_node_t *to_dir_snode, 531da6c28aaSamw char *to_name) 532da6c28aaSamw { 533da6c28aaSamw ASSERT(from_dir_snode); 534da6c28aaSamw ASSERT(to_dir_snode); 535da6c28aaSamw ASSERT(ret_snode); 536da6c28aaSamw ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); 537da6c28aaSamw ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); 538da6c28aaSamw ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC); 539da6c28aaSamw ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 540da6c28aaSamw ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 541da6c28aaSamw ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE); 542da6c28aaSamw 543da6c28aaSamw smb_node_ref(to_dir_snode); 544da6c28aaSamw smb_rwx_xenter(&ret_snode->n_lock); 545da6c28aaSamw ret_snode->dir_snode = to_dir_snode; 546da6c28aaSamw smb_rwx_xexit(&ret_snode->n_lock); 547da6c28aaSamw ASSERT(to_dir_snode->dir_snode != ret_snode); 548da6c28aaSamw ASSERT((to_dir_snode->vp->v_xattrdir) || 549da6c28aaSamw (to_dir_snode->vp->v_type == VDIR)); 550da6c28aaSamw smb_node_release(from_dir_snode); 551da6c28aaSamw 552da6c28aaSamw (void) strcpy(ret_snode->od_name, to_name); 553da6c28aaSamw 554da6c28aaSamw /* 555da6c28aaSamw * XXX Need to update attributes? 556da6c28aaSamw */ 557da6c28aaSamw 558da6c28aaSamw return (0); 559da6c28aaSamw } 560da6c28aaSamw 561da6c28aaSamw int 562faa1795aSjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root) 563da6c28aaSamw { 564da6c28aaSamw smb_attr_t va; 565faa1795aSjb150015 int error; 566faa1795aSjb150015 uint32_t hashkey; 567faa1795aSjb150015 smb_llist_t *node_hdr; 568faa1795aSjb150015 smb_node_t *node; 569da6c28aaSamw 570da6c28aaSamw /* 571da6c28aaSamw * Take an explicit hold on rootdir. This goes with the 572da6c28aaSamw * corresponding release in smb_node_root_fini()/smb_node_release(). 573da6c28aaSamw */ 574faa1795aSjb150015 VN_HOLD(vp); 575da6c28aaSamw 576faa1795aSjb150015 va.sa_mask = SMB_AT_ALL; 577faa1795aSjb150015 error = smb_vop_getattr(vp, NULL, &va, 0, kcred); 578faa1795aSjb150015 if (error) { 579faa1795aSjb150015 VN_RELE(vp); 580faa1795aSjb150015 return (error); 581faa1795aSjb150015 } 582da6c28aaSamw 583faa1795aSjb150015 hashkey = vp->v_vfsp->vfs_fsid.val[0] + va.sa_vattr.va_nodeid; 584faa1795aSjb150015 hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 585faa1795aSjb150015 node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]; 586faa1795aSjb150015 587faa1795aSjb150015 node = kmem_cache_alloc(sv->si_cache_node, KM_SLEEP); 588faa1795aSjb150015 bzero(node, sizeof (smb_node_t)); 589faa1795aSjb150015 590faa1795aSjb150015 node->n_state = SMB_NODE_STATE_AVAILABLE; 591faa1795aSjb150015 node->n_hash_bucket = node_hdr; 592faa1795aSjb150015 node->vp = vp; 593faa1795aSjb150015 node->n_hashkey = hashkey; 594faa1795aSjb150015 node->n_refcnt = 1; 595faa1795aSjb150015 node->attr = va; 596faa1795aSjb150015 node->flags |= NODE_FLAGS_ATTR_VALID; 597faa1795aSjb150015 node->n_size = node->attr.sa_vattr.va_size; 598faa1795aSjb150015 node->n_cache = sv->si_cache_node; 599faa1795aSjb150015 (void) strlcpy(node->od_name, ROOTVOL, sizeof (node->od_name)); 600faa1795aSjb150015 601faa1795aSjb150015 smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 602faa1795aSjb150015 offsetof(smb_ofile_t, f_nnd)); 603faa1795aSjb150015 smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 604faa1795aSjb150015 offsetof(smb_lock_t, l_lnd)); 605faa1795aSjb150015 606faa1795aSjb150015 smb_rwx_init(&node->n_lock); 607faa1795aSjb150015 node->n_magic = SMB_NODE_MAGIC; 608faa1795aSjb150015 smb_audit_buf_node_create(node); 609faa1795aSjb150015 610faa1795aSjb150015 sv->si_root_smb_node = node; 611faa1795aSjb150015 612faa1795aSjb150015 smb_audit_node(node); 613faa1795aSjb150015 smb_llist_enter(node_hdr, RW_WRITER); 614faa1795aSjb150015 smb_llist_insert_head(node_hdr, node); 615faa1795aSjb150015 smb_llist_exit(node_hdr); 616faa1795aSjb150015 617faa1795aSjb150015 *root = node; 618faa1795aSjb150015 619da6c28aaSamw return (0); 620da6c28aaSamw } 621da6c28aaSamw 622da6c28aaSamw /* 623da6c28aaSamw * smb_node_get_size 624da6c28aaSamw */ 6256537f381Sas200622 u_offset_t 6266537f381Sas200622 smb_node_get_size(smb_node_t *node, smb_attr_t *attr) 627da6c28aaSamw { 6286537f381Sas200622 u_offset_t size; 629da6c28aaSamw 630da6c28aaSamw if (attr->sa_vattr.va_type == VDIR) 631da6c28aaSamw return (0); 632da6c28aaSamw 633da6c28aaSamw smb_rwx_xenter(&node->n_lock); 634da6c28aaSamw if (node && (node->flags & NODE_FLAGS_SET_SIZE)) 635da6c28aaSamw size = node->n_size; 636da6c28aaSamw else 637da6c28aaSamw size = attr->sa_vattr.va_size; 638da6c28aaSamw smb_rwx_xexit(&node->n_lock); 639da6c28aaSamw return (size); 640da6c28aaSamw } 641da6c28aaSamw 642da6c28aaSamw static int 643da6c28aaSamw timeval_cmp(timestruc_t *a, timestruc_t *b) 644da6c28aaSamw { 645da6c28aaSamw if (a->tv_sec < b->tv_sec) 646da6c28aaSamw return (-1); 647da6c28aaSamw if (a->tv_sec > b->tv_sec) 648da6c28aaSamw return (1); 649da6c28aaSamw /* Seconds are equal compare tv_nsec */ 650da6c28aaSamw if (a->tv_nsec < b->tv_nsec) 651da6c28aaSamw return (-1); 652da6c28aaSamw return (a->tv_nsec > b->tv_nsec); 653da6c28aaSamw } 654da6c28aaSamw 655da6c28aaSamw /* 656da6c28aaSamw * smb_node_set_time 657da6c28aaSamw * 658da6c28aaSamw * This function will update the time stored in the node and 659c8ec8eeaSjose borrego * set the appropriate flags. If there is nothing to update, 660c8ec8eeaSjose borrego * the function will return without any updates. The update 661c8ec8eeaSjose borrego * is only in the node level and the attribute in the file system 662c8ec8eeaSjose borrego * will be updated when client close the file. 663da6c28aaSamw */ 664da6c28aaSamw void 665da6c28aaSamw smb_node_set_time(struct smb_node *node, struct timestruc *crtime, 666da6c28aaSamw struct timestruc *mtime, struct timestruc *atime, 667da6c28aaSamw struct timestruc *ctime, unsigned int what) 668da6c28aaSamw { 669c8ec8eeaSjose borrego if (what == 0) 670da6c28aaSamw return; 671da6c28aaSamw 672da6c28aaSamw if ((what & SMB_AT_CRTIME && crtime == 0) || 673da6c28aaSamw (what & SMB_AT_MTIME && mtime == 0) || 674da6c28aaSamw (what & SMB_AT_ATIME && atime == 0) || 675c8ec8eeaSjose borrego (what & SMB_AT_CTIME && ctime == 0)) 676da6c28aaSamw return; 677c8ec8eeaSjose borrego 678c8ec8eeaSjose borrego smb_rwx_xenter(&node->n_lock); 679da6c28aaSamw 680da6c28aaSamw if ((what & SMB_AT_CRTIME) && 681da6c28aaSamw timeval_cmp((timestruc_t *)&node->attr.sa_crtime, 682da6c28aaSamw crtime) != 0) { 683da6c28aaSamw node->what |= SMB_AT_CRTIME; 684da6c28aaSamw node->attr.sa_crtime = *((timestruc_t *)crtime); 685da6c28aaSamw } 686da6c28aaSamw 687da6c28aaSamw if ((what & SMB_AT_MTIME) && 688da6c28aaSamw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime, 689da6c28aaSamw mtime) != 0) { 690da6c28aaSamw node->what |= SMB_AT_MTIME; 691da6c28aaSamw node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime); 692da6c28aaSamw } 693da6c28aaSamw 694da6c28aaSamw if ((what & SMB_AT_ATIME) && 695da6c28aaSamw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime, 696da6c28aaSamw atime) != 0) { 697da6c28aaSamw node->what |= SMB_AT_ATIME; 698da6c28aaSamw node->attr.sa_vattr.va_atime = *((timestruc_t *)atime); 699da6c28aaSamw } 700da6c28aaSamw 701da6c28aaSamw /* 702da6c28aaSamw * The ctime handling is trickier. It has three scenarios. 703da6c28aaSamw * 1. Only ctime need to be set and it is the same as the ctime 704da6c28aaSamw * stored in the node. (update not necessary) 705da6c28aaSamw * 2. The ctime is the same as the ctime stored in the node but 706da6c28aaSamw * is not the only time need to be set. (update required) 707da6c28aaSamw * 3. The ctime need to be set and is not the same as the ctime 708da6c28aaSamw * stored in the node. (update required) 709da6c28aaSamw * Unlike other time setting, the ctime needs to be set even when 710da6c28aaSamw * it is the same as the ctime in the node if there are other time 711da6c28aaSamw * needs to be set (#2). This will ensure the ctime not being 712da6c28aaSamw * updated when other times are being updated in the file system. 713da6c28aaSamw * 714da6c28aaSamw * Retained file rules: 715da6c28aaSamw * 716da6c28aaSamw * 1. Don't add SMB_AT_CTIME to node->what by default because the 717da6c28aaSamw * request will be rejected by filesystem 718da6c28aaSamw * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e. 719da6c28aaSamw * any request for changing ctime on these files should have 720da6c28aaSamw * been already rejected 721da6c28aaSamw */ 722da6c28aaSamw node->what |= SMB_AT_CTIME; 723da6c28aaSamw if (what & SMB_AT_CTIME) { 724da6c28aaSamw if ((what == SMB_AT_CTIME) && 725da6c28aaSamw timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime, 726da6c28aaSamw ctime) == 0) { 727da6c28aaSamw node->what &= ~SMB_AT_CTIME; 728da6c28aaSamw } else { 729da6c28aaSamw gethrestime(&node->attr.sa_vattr.va_ctime); 730da6c28aaSamw } 731da6c28aaSamw } else { 732da6c28aaSamw gethrestime(&node->attr.sa_vattr.va_ctime); 733da6c28aaSamw } 734da6c28aaSamw smb_rwx_xexit(&node->n_lock); 735da6c28aaSamw } 736da6c28aaSamw 737da6c28aaSamw 738da6c28aaSamw timestruc_t * 739da6c28aaSamw smb_node_get_crtime(smb_node_t *node) 740da6c28aaSamw { 741da6c28aaSamw return ((timestruc_t *)&node->attr.sa_crtime); 742da6c28aaSamw } 743da6c28aaSamw 744da6c28aaSamw timestruc_t * 745da6c28aaSamw smb_node_get_atime(smb_node_t *node) 746da6c28aaSamw { 747da6c28aaSamw return ((timestruc_t *)&node->attr.sa_vattr.va_atime); 748da6c28aaSamw } 749da6c28aaSamw 750da6c28aaSamw timestruc_t * 751da6c28aaSamw smb_node_get_ctime(smb_node_t *node) 752da6c28aaSamw { 753da6c28aaSamw return ((timestruc_t *)&node->attr.sa_vattr.va_ctime); 754da6c28aaSamw } 755da6c28aaSamw 756da6c28aaSamw timestruc_t * 757da6c28aaSamw smb_node_get_mtime(smb_node_t *node) 758da6c28aaSamw { 759da6c28aaSamw return ((timestruc_t *)&node->attr.sa_vattr.va_mtime); 760da6c28aaSamw } 761da6c28aaSamw 762da6c28aaSamw /* 763da6c28aaSamw * smb_node_set_dosattr 764da6c28aaSamw * 765da6c28aaSamw * Parse the specified DOS attributes and, if they have been modified, 766da6c28aaSamw * update the node cache. This call should be followed by a 767da6c28aaSamw * smb_sync_fsattr() call to write the attribute changes to filesystem. 768da6c28aaSamw */ 769da6c28aaSamw void 7703db3f65cSamw smb_node_set_dosattr(smb_node_t *node, uint32_t dosattr) 771da6c28aaSamw { 7723db3f65cSamw uint32_t mode = dosattr & (FILE_ATTRIBUTE_ARCHIVE | 7733db3f65cSamw FILE_ATTRIBUTE_READONLY | 7743db3f65cSamw FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); 775da6c28aaSamw 776da6c28aaSamw smb_rwx_xenter(&node->n_lock); 777da6c28aaSamw if (node->attr.sa_dosattr != mode) { 778da6c28aaSamw node->attr.sa_dosattr = mode; 779da6c28aaSamw node->what |= SMB_AT_DOSATTR; 780da6c28aaSamw } 781da6c28aaSamw smb_rwx_xexit(&node->n_lock); 782da6c28aaSamw } 783da6c28aaSamw 784da6c28aaSamw /* 785c8ec8eeaSjose borrego * smb_node_get_dosattr() 786da6c28aaSamw * 787c8ec8eeaSjose borrego * This function is used to provide clients with information as to whether 788c8ec8eeaSjose borrego * the readonly bit is set. Hence both the node attribute cache (which 789c8ec8eeaSjose borrego * reflects the on-disk attributes) and node->readonly_creator (which 790c8ec8eeaSjose borrego * reflects whether a readonly set is pending from a readonly create) are 791c8ec8eeaSjose borrego * checked. In the latter case, the readonly attribute should be visible to 792c8ec8eeaSjose borrego * all clients even though the readonly creator fid is immune to the readonly 793c8ec8eeaSjose borrego * bit until close. 794da6c28aaSamw */ 795c8ec8eeaSjose borrego 796da6c28aaSamw uint32_t 797da6c28aaSamw smb_node_get_dosattr(smb_node_t *node) 798da6c28aaSamw { 799c8ec8eeaSjose borrego uint32_t dosattr = node->attr.sa_dosattr; 800c8ec8eeaSjose borrego 801c8ec8eeaSjose borrego if (node->readonly_creator) 802c8ec8eeaSjose borrego dosattr |= FILE_ATTRIBUTE_READONLY; 803c8ec8eeaSjose borrego 804c8ec8eeaSjose borrego if (!dosattr) 805c8ec8eeaSjose borrego dosattr = FILE_ATTRIBUTE_NORMAL; 806c8ec8eeaSjose borrego 807c8ec8eeaSjose borrego return (dosattr); 808da6c28aaSamw } 809da6c28aaSamw 810da6c28aaSamw int 811da6c28aaSamw smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr) 812da6c28aaSamw { 813da6c28aaSamw int rc = -1; 814da6c28aaSamw 815da6c28aaSamw smb_rwx_xenter(&node->n_lock); 816da6c28aaSamw if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) && 817da6c28aaSamw !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) { 818da6c28aaSamw crhold(cr); 819da6c28aaSamw node->delete_on_close_cred = cr; 820da6c28aaSamw node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; 821da6c28aaSamw rc = 0; 822da6c28aaSamw } 823da6c28aaSamw smb_rwx_xexit(&node->n_lock); 824da6c28aaSamw return (rc); 825da6c28aaSamw } 826da6c28aaSamw 827da6c28aaSamw void 828da6c28aaSamw smb_node_reset_delete_on_close(smb_node_t *node) 829da6c28aaSamw { 830da6c28aaSamw smb_rwx_xenter(&node->n_lock); 831da6c28aaSamw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 832da6c28aaSamw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 833da6c28aaSamw crfree(node->delete_on_close_cred); 834da6c28aaSamw node->delete_on_close_cred = NULL; 835da6c28aaSamw } 836da6c28aaSamw smb_rwx_xexit(&node->n_lock); 837da6c28aaSamw } 838dc20a302Sas200622 839dc20a302Sas200622 /* 8403ad684d6Sjb150015 * smb_node_open_check 841dc20a302Sas200622 * 842dc20a302Sas200622 * check file sharing rules for current open request 843dc20a302Sas200622 * against all existing opens for a file. 844dc20a302Sas200622 * 845dc20a302Sas200622 * Returns NT_STATUS_SHARING_VIOLATION if there is any 846dc20a302Sas200622 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 847dc20a302Sas200622 */ 848dc20a302Sas200622 uint32_t 849dc20a302Sas200622 smb_node_open_check(struct smb_node *node, cred_t *cr, 850dc20a302Sas200622 uint32_t desired_access, uint32_t share_access) 851dc20a302Sas200622 { 852dc20a302Sas200622 smb_ofile_t *of; 853dc20a302Sas200622 uint32_t status; 854dc20a302Sas200622 855dc20a302Sas200622 ASSERT(node); 856dc20a302Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 857dc20a302Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 858dc20a302Sas200622 859dc20a302Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 860dc20a302Sas200622 of = smb_llist_head(&node->n_ofile_list); 861dc20a302Sas200622 while (of) { 8623ad684d6Sjb150015 status = smb_ofile_open_check(of, cr, desired_access, 8633ad684d6Sjb150015 share_access); 8643ad684d6Sjb150015 8653ad684d6Sjb150015 switch (status) { 8663ad684d6Sjb150015 case NT_STATUS_INVALID_HANDLE: 8673ad684d6Sjb150015 case NT_STATUS_SUCCESS: 8683ad684d6Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 8693ad684d6Sjb150015 break; 8703ad684d6Sjb150015 default: 8713ad684d6Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 872dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 873dc20a302Sas200622 return (status); 874dc20a302Sas200622 } 875dc20a302Sas200622 } 8763ad684d6Sjb150015 877dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 878dc20a302Sas200622 return (NT_STATUS_SUCCESS); 879dc20a302Sas200622 } 880dc20a302Sas200622 881dc20a302Sas200622 uint32_t 882dc20a302Sas200622 smb_node_rename_check(struct smb_node *node) 883dc20a302Sas200622 { 8843ad684d6Sjb150015 struct smb_ofile *of; 8853ad684d6Sjb150015 uint32_t status; 886dc20a302Sas200622 887dc20a302Sas200622 ASSERT(node); 888dc20a302Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 889dc20a302Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 890dc20a302Sas200622 891dc20a302Sas200622 /* 892dc20a302Sas200622 * Intra-CIFS check 893dc20a302Sas200622 */ 894dc20a302Sas200622 895dc20a302Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 8963ad684d6Sjb150015 of = smb_llist_head(&node->n_ofile_list); 8973ad684d6Sjb150015 while (of) { 8983ad684d6Sjb150015 status = smb_ofile_rename_check(of); 899dc20a302Sas200622 9003ad684d6Sjb150015 switch (status) { 9013ad684d6Sjb150015 case NT_STATUS_INVALID_HANDLE: 9023ad684d6Sjb150015 case NT_STATUS_SUCCESS: 9033ad684d6Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 9043ad684d6Sjb150015 break; 9053ad684d6Sjb150015 default: 9063ad684d6Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 907dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 9083ad684d6Sjb150015 return (status); 909dc20a302Sas200622 } 910dc20a302Sas200622 } 911dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 912dc20a302Sas200622 913dc20a302Sas200622 /* 914dc20a302Sas200622 * system-wide share check 915dc20a302Sas200622 */ 916dc20a302Sas200622 917dc20a302Sas200622 if (nbl_share_conflict(node->vp, NBL_RENAME, NULL)) 918dc20a302Sas200622 return (NT_STATUS_SHARING_VIOLATION); 919dc20a302Sas200622 else 920dc20a302Sas200622 return (NT_STATUS_SUCCESS); 921dc20a302Sas200622 } 922dc20a302Sas200622 9233ad684d6Sjb150015 uint32_t 924dc20a302Sas200622 smb_node_delete_check(smb_node_t *node) 925dc20a302Sas200622 { 9263ad684d6Sjb150015 smb_ofile_t *of; 9273ad684d6Sjb150015 uint32_t status; 928dc20a302Sas200622 929dc20a302Sas200622 ASSERT(node); 930dc20a302Sas200622 ASSERT(node->n_magic == SMB_NODE_MAGIC); 931dc20a302Sas200622 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 932dc20a302Sas200622 933dc20a302Sas200622 if (node->attr.sa_vattr.va_type == VDIR) 934dc20a302Sas200622 return (NT_STATUS_SUCCESS); 935dc20a302Sas200622 936dc20a302Sas200622 /* 937dc20a302Sas200622 * intra-CIFS check 938dc20a302Sas200622 */ 939dc20a302Sas200622 940dc20a302Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 9413ad684d6Sjb150015 of = smb_llist_head(&node->n_ofile_list); 9423ad684d6Sjb150015 while (of) { 9433ad684d6Sjb150015 status = smb_ofile_delete_check(of); 9443ad684d6Sjb150015 9453ad684d6Sjb150015 switch (status) { 9463ad684d6Sjb150015 case NT_STATUS_INVALID_HANDLE: 9473ad684d6Sjb150015 case NT_STATUS_SUCCESS: 9483ad684d6Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 9493ad684d6Sjb150015 break; 9503ad684d6Sjb150015 default: 9513ad684d6Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 952dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 9533ad684d6Sjb150015 return (status); 954dc20a302Sas200622 } 955dc20a302Sas200622 } 956dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 957dc20a302Sas200622 958dc20a302Sas200622 /* 959dc20a302Sas200622 * system-wide share check 960dc20a302Sas200622 */ 961dc20a302Sas200622 962dc20a302Sas200622 if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL)) 963dc20a302Sas200622 return (NT_STATUS_SHARING_VIOLATION); 964dc20a302Sas200622 else 965dc20a302Sas200622 return (NT_STATUS_SUCCESS); 966dc20a302Sas200622 } 967dc20a302Sas200622 968dc20a302Sas200622 /* 969dc20a302Sas200622 * smb_node_start_crit() 970dc20a302Sas200622 * 971dc20a302Sas200622 * Enter critical region for share reservations. 972dc20a302Sas200622 * See comments above smb_fsop_shrlock(). 973dc20a302Sas200622 */ 974dc20a302Sas200622 975dc20a302Sas200622 void 976dc20a302Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode) 977dc20a302Sas200622 { 978dc20a302Sas200622 rw_enter(&node->n_share_lock, mode); 979dc20a302Sas200622 nbl_start_crit(node->vp, mode); 980dc20a302Sas200622 } 981dc20a302Sas200622 982dc20a302Sas200622 /* 983dc20a302Sas200622 * smb_node_end_crit() 984dc20a302Sas200622 * 985dc20a302Sas200622 * Exit critical region for share reservations. 986dc20a302Sas200622 */ 987dc20a302Sas200622 988dc20a302Sas200622 void 989dc20a302Sas200622 smb_node_end_crit(smb_node_t *node) 990dc20a302Sas200622 { 991dc20a302Sas200622 nbl_end_crit(node->vp); 992dc20a302Sas200622 rw_exit(&node->n_share_lock); 993dc20a302Sas200622 } 994dc20a302Sas200622 995dc20a302Sas200622 int 996dc20a302Sas200622 smb_node_in_crit(smb_node_t *node) 997dc20a302Sas200622 { 998dc20a302Sas200622 return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock)); 999dc20a302Sas200622 } 1000