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 /* 227f667e74Sjose borrego * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23da6c28aaSamw * Use is subject to license terms. 24da6c28aaSamw */ 25da6c28aaSamw /* 26da6c28aaSamw * SMB Node State Machine 27da6c28aaSamw * ---------------------- 28da6c28aaSamw * 29fc724630SAlan Wright * 30fc724630SAlan Wright * +----------- Creation/Allocation 31da6c28aaSamw * | 32fc724630SAlan Wright * | T0 33da6c28aaSamw * | 34da6c28aaSamw * v 35fc724630SAlan Wright * +----------------------------+ T1 36fc724630SAlan Wright * | SMB_NODE_STATE_AVAILABLE |--------------------+ 37fc724630SAlan Wright * +----------------------------+ | 38fc724630SAlan Wright * | ^ | 39fc724630SAlan Wright * | | v 40fc724630SAlan Wright * | | T2 +-------------------------------+ 41fc724630SAlan Wright * | |<---------| SMB_NODE_STATE_OPLOCK_GRANTED | 42fc724630SAlan Wright * | | +-------------------------------+ 43fc724630SAlan Wright * | T5 | | 44fc724630SAlan Wright * | | | T3 45fc724630SAlan Wright * | | v 46fc724630SAlan Wright * | | T4 +--------------------------------+ 47fc724630SAlan Wright * | +----------| SMB_NODE_STATE_OPLOCK_BREAKING | 48fc724630SAlan Wright * | +--------------------------------+ 49fc724630SAlan Wright * | 50fc724630SAlan Wright * v 51da6c28aaSamw * +-----------------------------+ 52fc724630SAlan Wright * | SMB_NODE_STATE_DESTROYING | 53fc724630SAlan Wright * +-----------------------------+ 54fc724630SAlan Wright * | 55fc724630SAlan Wright * | 56fc724630SAlan Wright * | T6 57fc724630SAlan Wright * | 58fc724630SAlan Wright * +----------> Deletion/Free 59da6c28aaSamw * 60da6c28aaSamw * Transition T0 61da6c28aaSamw * 62da6c28aaSamw * This transition occurs in smb_node_lookup(). If the node looked for is 63da6c28aaSamw * not found in the has table a new node is created. The reference count is 64da6c28aaSamw * initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE. 65da6c28aaSamw * 66da6c28aaSamw * Transition T1 67da6c28aaSamw * 68fc724630SAlan Wright * This transition occurs smb_oplock_acquire() during an OPEN. 69fc724630SAlan Wright * 70fc724630SAlan Wright * Transition T2 71fc724630SAlan Wright * 72fc724630SAlan Wright * This transition occurs in smb_oplock_release(). The events triggering 73fc724630SAlan Wright * it are: 74fc724630SAlan Wright * 75fc724630SAlan Wright * - LockingAndX sent by the client that was granted the oplock. 76fc724630SAlan Wright * - Closing of the file. 77fc724630SAlan Wright * 78fc724630SAlan Wright * Transition T3 79fc724630SAlan Wright * 80fc724630SAlan Wright * This transition occurs in smb_oplock_break(). The events triggering 81fc724630SAlan Wright * it are: 82fc724630SAlan Wright * 83fc724630SAlan Wright * - Another client wants to open the file. 84fc724630SAlan Wright * - A client is trying to delete the file. 85fc724630SAlan Wright * - A client is trying to rename the file. 86fc724630SAlan Wright * - A client is trying to set/modify the file attributes. 87fc724630SAlan Wright * 88fc724630SAlan Wright * Transition T4 89fc724630SAlan Wright * 90fc724630SAlan Wright * This transition occurs in smb_oplock_release or smb_oplock_break(). The 91fc724630SAlan Wright * events triggering it are: 92fc724630SAlan Wright * 93fc724630SAlan Wright * - The client that was granting the oplock releases it (close or 94fc724630SAlan Wright * LockingAndx). 95fc724630SAlan Wright * - The time alloted to release the oplock expired. 96fc724630SAlan Wright * 97fc724630SAlan Wright * Transition T5 98fc724630SAlan Wright * 99da6c28aaSamw * This transition occurs in smb_node_release(). If the reference count 100da6c28aaSamw * drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more 101da6c28aaSamw * reference count will be given out for that node. 102da6c28aaSamw * 103fc724630SAlan Wright * Transition T6 104da6c28aaSamw * 105da6c28aaSamw * This transition occurs in smb_node_release(). The structure is deleted. 106da6c28aaSamw * 107da6c28aaSamw * Comments 108da6c28aaSamw * -------- 109da6c28aaSamw * 110da6c28aaSamw * The reason the smb node has 2 states is the following synchronization 111da6c28aaSamw * rule: 112da6c28aaSamw * 113da6c28aaSamw * There's a mutex embedded in the node used to protect its fields and 114da6c28aaSamw * there's a lock embedded in the bucket of the hash table the node belongs 115da6c28aaSamw * to. To increment or to decrement the reference count the mutex must be 116da6c28aaSamw * entered. To insert the node into the bucket and to remove it from the 117da6c28aaSamw * bucket the lock must be entered in RW_WRITER mode. When both (mutex and 118da6c28aaSamw * lock) have to be entered, the lock has always to be entered first then 119da6c28aaSamw * the mutex. This prevents a deadlock between smb_node_lookup() and 120da6c28aaSamw * smb_node_release() from occurring. However, in smb_node_release() when the 121da6c28aaSamw * reference count drops to zero and triggers the deletion of the node, the 122da6c28aaSamw * mutex has to be released before entering the lock of the bucket (to 123da6c28aaSamw * remove the node). This creates a window during which the node that is 124da6c28aaSamw * about to be freed could be given out by smb_node_lookup(). To close that 125da6c28aaSamw * window the node is moved to the state SMB_NODE_STATE_DESTROYING before 126da6c28aaSamw * releasing the mutex. That way, even if smb_node_lookup() finds it, the 127da6c28aaSamw * state will indicate that the node should be treated as non existent (of 128da6c28aaSamw * course the state of the node should be tested/updated under the 129da6c28aaSamw * protection of the mutex). 130da6c28aaSamw */ 131da6c28aaSamw #include <smbsrv/smb_incl.h> 132da6c28aaSamw #include <smbsrv/smb_fsops.h> 1332c2961f8Sjose borrego #include <smbsrv/smb_kstat.h> 134da6c28aaSamw #include <sys/pathname.h> 135da6c28aaSamw #include <sys/sdt.h> 136dc20a302Sas200622 #include <sys/nbmlock.h> 137da6c28aaSamw 1382c2961f8Sjose borrego uint32_t smb_is_executable(char *); 1392c2961f8Sjose borrego static void smb_node_delete_on_close(smb_node_t *); 1402c2961f8Sjose borrego static void smb_node_create_audit_buf(smb_node_t *, int); 1412c2961f8Sjose borrego static void smb_node_destroy_audit_buf(smb_node_t *); 1422c2961f8Sjose borrego static void smb_node_audit(smb_node_t *); 143037cac00Sjoyce mcintosh static smb_node_t *smb_node_alloc(char *, vnode_t *, smb_llist_t *, uint32_t); 1442c2961f8Sjose borrego static void smb_node_free(smb_node_t *); 1452c2961f8Sjose borrego static int smb_node_constructor(void *, void *, int); 1462c2961f8Sjose borrego static void smb_node_destructor(void *, void *); 1472c2961f8Sjose borrego static smb_llist_t *smb_node_get_hash(fsid_t *, smb_attr_t *, uint32_t *); 148e3f2c991SKeyur Desai 149e3f2c991SKeyur Desai static void smb_node_init_cached_data(smb_node_t *); 150e3f2c991SKeyur Desai static void smb_node_clear_cached_data(smb_node_t *); 151e3f2c991SKeyur Desai 152e3f2c991SKeyur Desai static void smb_node_init_cached_timestamps(smb_node_t *, smb_attr_t *); 153037cac00Sjoyce mcintosh static void smb_node_clear_cached_timestamps(smb_node_t *); 154037cac00Sjoyce mcintosh static void smb_node_get_cached_timestamps(smb_node_t *, smb_attr_t *); 155037cac00Sjoyce mcintosh static void smb_node_set_cached_timestamps(smb_node_t *, smb_attr_t *); 156da6c28aaSamw 157e3f2c991SKeyur Desai static void smb_node_init_cached_allocsz(smb_node_t *, smb_attr_t *); 158e3f2c991SKeyur Desai static void smb_node_clear_cached_allocsz(smb_node_t *); 159e3f2c991SKeyur Desai static void smb_node_get_cached_allocsz(smb_node_t *, smb_attr_t *); 160e3f2c991SKeyur Desai static void smb_node_set_cached_allocsz(smb_node_t *, smb_attr_t *); 161e3f2c991SKeyur Desai 162da6c28aaSamw #define VALIDATE_DIR_NODE(_dir_, _node_) \ 163da6c28aaSamw ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \ 164da6c28aaSamw ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \ 1651fcced4cSJordan Brown ASSERT((_dir_)->n_dnode != (_node_)); 166da6c28aaSamw 167e3f2c991SKeyur Desai /* round sz to DEV_BSIZE block */ 168e3f2c991SKeyur Desai #define SMB_ALLOCSZ(sz) (((sz) + DEV_BSIZE-1) & ~(DEV_BSIZE-1)) 169e3f2c991SKeyur Desai 1702c2961f8Sjose borrego static kmem_cache_t *smb_node_cache = NULL; 171faa1795aSjb150015 static boolean_t smb_node_initialized = B_FALSE; 172faa1795aSjb150015 static smb_llist_t smb_node_hash_table[SMBND_HASH_MASK+1]; 173faa1795aSjb150015 174faa1795aSjb150015 /* 175faa1795aSjb150015 * smb_node_init 176faa1795aSjb150015 * 177faa1795aSjb150015 * Initialization of the SMB node layer. 178faa1795aSjb150015 * 179faa1795aSjb150015 * This function is not multi-thread safe. The caller must make sure only one 180faa1795aSjb150015 * thread makes the call. 181faa1795aSjb150015 */ 182faa1795aSjb150015 int 183faa1795aSjb150015 smb_node_init(void) 184faa1795aSjb150015 { 185faa1795aSjb150015 int i; 186faa1795aSjb150015 187faa1795aSjb150015 if (smb_node_initialized) 188faa1795aSjb150015 return (0); 1892c2961f8Sjose borrego smb_node_cache = kmem_cache_create(SMBSRV_KSTAT_NODE_CACHE, 1902c2961f8Sjose borrego sizeof (smb_node_t), 8, smb_node_constructor, smb_node_destructor, 1912c2961f8Sjose borrego NULL, NULL, NULL, 0); 192faa1795aSjb150015 193faa1795aSjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 194faa1795aSjb150015 smb_llist_constructor(&smb_node_hash_table[i], 195faa1795aSjb150015 sizeof (smb_node_t), offsetof(smb_node_t, n_lnd)); 196faa1795aSjb150015 } 197faa1795aSjb150015 smb_node_initialized = B_TRUE; 198faa1795aSjb150015 return (0); 199faa1795aSjb150015 } 200faa1795aSjb150015 201faa1795aSjb150015 /* 202faa1795aSjb150015 * smb_node_fini 203faa1795aSjb150015 * 204faa1795aSjb150015 * This function is not multi-thread safe. The caller must make sure only one 205faa1795aSjb150015 * thread makes the call. 206faa1795aSjb150015 */ 207faa1795aSjb150015 void 208faa1795aSjb150015 smb_node_fini(void) 209faa1795aSjb150015 { 210faa1795aSjb150015 int i; 211faa1795aSjb150015 212faa1795aSjb150015 if (!smb_node_initialized) 213faa1795aSjb150015 return; 214faa1795aSjb150015 215faa1795aSjb150015 #ifdef DEBUG 216faa1795aSjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 217faa1795aSjb150015 smb_node_t *node; 218faa1795aSjb150015 219faa1795aSjb150015 /* 220faa1795aSjb150015 * The following sequence is just intended for sanity check. 221faa1795aSjb150015 * This will have to be modified when the code goes into 222faa1795aSjb150015 * production. 223faa1795aSjb150015 * 224faa1795aSjb150015 * The SMB node hash table should be emtpy at this point. If the 225faa1795aSjb150015 * hash table is not empty a panic will be triggered. 226faa1795aSjb150015 * 227faa1795aSjb150015 * The reason why SMB nodes are still remaining in the hash 228faa1795aSjb150015 * table is problably due to a mismatch between calls to 229faa1795aSjb150015 * smb_node_lookup() and smb_node_release(). You must track that 230faa1795aSjb150015 * down. 231faa1795aSjb150015 */ 232faa1795aSjb150015 node = smb_llist_head(&smb_node_hash_table[i]); 233faa1795aSjb150015 ASSERT(node == NULL); 234faa1795aSjb150015 } 235faa1795aSjb150015 #endif 236faa1795aSjb150015 237faa1795aSjb150015 for (i = 0; i <= SMBND_HASH_MASK; i++) { 238faa1795aSjb150015 smb_llist_destructor(&smb_node_hash_table[i]); 239faa1795aSjb150015 } 2402c2961f8Sjose borrego kmem_cache_destroy(smb_node_cache); 2412c2961f8Sjose borrego smb_node_cache = NULL; 242faa1795aSjb150015 smb_node_initialized = B_FALSE; 243faa1795aSjb150015 } 244faa1795aSjb150015 245da6c28aaSamw /* 246da6c28aaSamw * smb_node_lookup() 247da6c28aaSamw * 248da6c28aaSamw * NOTE: This routine should only be called by the file system interface layer, 249da6c28aaSamw * and not by SMB. 250da6c28aaSamw * 251da6c28aaSamw * smb_node_lookup() is called upon successful lookup, mkdir, and create 252da6c28aaSamw * (for both non-streams and streams). In each of these cases, a held vnode is 2537f667e74Sjose borrego * passed into this routine. If a new smb_node is created it will take its 2547f667e74Sjose borrego * own hold on the vnode. The caller's hold therefore still belongs to, and 2557f667e74Sjose borrego * should be released by, the caller. 256da6c28aaSamw * 257da6c28aaSamw * A reference is taken on the smb_node whether found in the hash table 258da6c28aaSamw * or newly created. 259da6c28aaSamw * 260da6c28aaSamw * If an smb_node needs to be created, a reference is also taken on the 2611fcced4cSJordan Brown * dnode (if passed in). 262da6c28aaSamw * 263da6c28aaSamw * See smb_node_release() for details on the release of these references. 264da6c28aaSamw */ 265da6c28aaSamw 266da6c28aaSamw /*ARGSUSED*/ 267da6c28aaSamw smb_node_t * 268da6c28aaSamw smb_node_lookup( 269da6c28aaSamw struct smb_request *sr, 270da6c28aaSamw struct open_param *op, 271da6c28aaSamw cred_t *cred, 272da6c28aaSamw vnode_t *vp, 273da6c28aaSamw char *od_name, 2741fcced4cSJordan Brown smb_node_t *dnode, 2751fcced4cSJordan Brown smb_node_t *unode) 276da6c28aaSamw { 277da6c28aaSamw smb_llist_t *node_hdr; 278da6c28aaSamw smb_node_t *node; 279037cac00Sjoyce mcintosh smb_attr_t attr; 280da6c28aaSamw uint32_t hashkey = 0; 281c8ec8eeaSjose borrego fsid_t fsid; 282da6c28aaSamw int error; 283da6c28aaSamw krw_t lock_mode; 284da6c28aaSamw vnode_t *unnamed_vp = NULL; 285da6c28aaSamw 286da6c28aaSamw /* 287da6c28aaSamw * smb_vop_getattr() is called here instead of smb_fsop_getattr(), 288da6c28aaSamw * because the node may not yet exist. We also do not want to call 289da6c28aaSamw * it with the list lock held. 290da6c28aaSamw */ 291da6c28aaSamw 2921fcced4cSJordan Brown if (unode) 2931fcced4cSJordan Brown unnamed_vp = unode->vp; 294da6c28aaSamw 295da6c28aaSamw /* 296da6c28aaSamw * This getattr is performed on behalf of the server 297da6c28aaSamw * that's why kcred is used not the user's cred 298da6c28aaSamw */ 299037cac00Sjoyce mcintosh attr.sa_mask = SMB_AT_ALL; 300037cac00Sjoyce mcintosh error = smb_vop_getattr(vp, unnamed_vp, &attr, 0, kcred); 301da6c28aaSamw if (error) 302da6c28aaSamw return (NULL); 303da6c28aaSamw 304c8ec8eeaSjose borrego if (sr && sr->tid_tree) { 305da6c28aaSamw /* 306c8ec8eeaSjose borrego * The fsid for a file is that of the tree, even 307da6c28aaSamw * if the file resides in a different mountpoint 308da6c28aaSamw * under the share. 309da6c28aaSamw */ 310c8ec8eeaSjose borrego fsid = SMB_TREE_FSID(sr->tid_tree); 311da6c28aaSamw } else { 312da6c28aaSamw /* 313da6c28aaSamw * This should be getting executed only for the 314c8ec8eeaSjose borrego * tree root smb_node. 315da6c28aaSamw */ 316c8ec8eeaSjose borrego fsid = vp->v_vfsp->vfs_fsid; 317da6c28aaSamw } 318da6c28aaSamw 319037cac00Sjoyce mcintosh node_hdr = smb_node_get_hash(&fsid, &attr, &hashkey); 320da6c28aaSamw lock_mode = RW_READER; 321da6c28aaSamw 322da6c28aaSamw smb_llist_enter(node_hdr, lock_mode); 323da6c28aaSamw for (;;) { 324da6c28aaSamw node = list_head(&node_hdr->ll_list); 325da6c28aaSamw while (node) { 326da6c28aaSamw ASSERT(node->n_magic == SMB_NODE_MAGIC); 327da6c28aaSamw ASSERT(node->n_hash_bucket == node_hdr); 328da6c28aaSamw if ((node->n_hashkey == hashkey) && (node->vp == vp)) { 3292c2961f8Sjose borrego mutex_enter(&node->n_mutex); 330da6c28aaSamw DTRACE_PROBE1(smb_node_lookup_hit, 331da6c28aaSamw smb_node_t *, node); 332da6c28aaSamw switch (node->n_state) { 3332c2961f8Sjose borrego case SMB_NODE_STATE_OPLOCK_GRANTED: 3342c2961f8Sjose borrego case SMB_NODE_STATE_OPLOCK_BREAKING: 335da6c28aaSamw case SMB_NODE_STATE_AVAILABLE: 336da6c28aaSamw /* The node was found. */ 337da6c28aaSamw node->n_refcnt++; 3381fcced4cSJordan Brown if ((node->n_dnode == NULL) && 3391fcced4cSJordan Brown (dnode != NULL) && 340da6c28aaSamw (strcmp(od_name, "..") != 0) && 341da6c28aaSamw (strcmp(od_name, ".") != 0)) { 3421fcced4cSJordan Brown VALIDATE_DIR_NODE(dnode, node); 3431fcced4cSJordan Brown node->n_dnode = dnode; 3441fcced4cSJordan Brown smb_node_ref(dnode); 345da6c28aaSamw } 346da6c28aaSamw 3472c2961f8Sjose borrego smb_node_audit(node); 3482c2961f8Sjose borrego mutex_exit(&node->n_mutex); 349da6c28aaSamw smb_llist_exit(node_hdr); 350da6c28aaSamw return (node); 351da6c28aaSamw 352da6c28aaSamw case SMB_NODE_STATE_DESTROYING: 353da6c28aaSamw /* 354da6c28aaSamw * Although the node exists it is about 355da6c28aaSamw * to be destroyed. We act as it hasn't 356da6c28aaSamw * been found. 357da6c28aaSamw */ 3582c2961f8Sjose borrego mutex_exit(&node->n_mutex); 359da6c28aaSamw break; 360da6c28aaSamw default: 361da6c28aaSamw /* 362da6c28aaSamw * Although the node exists it is in an 363da6c28aaSamw * unknown state. We act as it hasn't 364da6c28aaSamw * been found. 365da6c28aaSamw */ 366da6c28aaSamw ASSERT(0); 3672c2961f8Sjose borrego mutex_exit(&node->n_mutex); 368da6c28aaSamw break; 369da6c28aaSamw } 370da6c28aaSamw } 371da6c28aaSamw node = smb_llist_next(node_hdr, node); 372da6c28aaSamw } 373da6c28aaSamw if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) { 374da6c28aaSamw lock_mode = RW_WRITER; 375da6c28aaSamw continue; 376da6c28aaSamw } 377da6c28aaSamw break; 378da6c28aaSamw } 379037cac00Sjoyce mcintosh node = smb_node_alloc(od_name, vp, node_hdr, hashkey); 380faa1795aSjb150015 node->n_orig_uid = crgetuid(sr->user_cr); 381da6c28aaSamw 382da6c28aaSamw if (op) 383eb1d736bSafshin salek ardakani - Sun Microsystems - Irvine United States node->flags |= smb_is_executable(op->fqi.fq_last_comp); 384da6c28aaSamw 3851fcced4cSJordan Brown if (dnode) { 3861fcced4cSJordan Brown smb_node_ref(dnode); 3871fcced4cSJordan Brown node->n_dnode = dnode; 3881fcced4cSJordan Brown ASSERT(dnode->n_dnode != node); 3891fcced4cSJordan Brown ASSERT((dnode->vp->v_xattrdir) || 3901fcced4cSJordan Brown (dnode->vp->v_type == VDIR)); 391da6c28aaSamw } 392da6c28aaSamw 3931fcced4cSJordan Brown if (unode) { 3941fcced4cSJordan Brown smb_node_ref(unode); 3951fcced4cSJordan Brown node->n_unode = unode; 396da6c28aaSamw } 397da6c28aaSamw 398da6c28aaSamw DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); 3992c2961f8Sjose borrego smb_node_audit(node); 400da6c28aaSamw smb_llist_insert_head(node_hdr, node); 401da6c28aaSamw smb_llist_exit(node_hdr); 402da6c28aaSamw return (node); 403da6c28aaSamw } 404da6c28aaSamw 405da6c28aaSamw /* 406da6c28aaSamw * smb_stream_node_lookup() 407da6c28aaSamw * 408da6c28aaSamw * Note: stream_name (the name that will be stored in the "od_name" field 409da6c28aaSamw * of a stream's smb_node) is the same as the on-disk name for the stream 410da6c28aaSamw * except that it does not have SMB_STREAM_PREFIX prepended. 411da6c28aaSamw */ 412da6c28aaSamw 413da6c28aaSamw smb_node_t * 4142c2961f8Sjose borrego smb_stream_node_lookup(smb_request_t *sr, cred_t *cr, smb_node_t *fnode, 415037cac00Sjoyce mcintosh vnode_t *xattrdirvp, vnode_t *vp, char *stream_name) 416da6c28aaSamw { 417da6c28aaSamw smb_node_t *xattrdir_node; 418da6c28aaSamw smb_node_t *snode; 419da6c28aaSamw 420da6c28aaSamw xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, 421037cac00Sjoyce mcintosh fnode, NULL); 422da6c28aaSamw 423da6c28aaSamw if (xattrdir_node == NULL) 424da6c28aaSamw return (NULL); 425da6c28aaSamw 426da6c28aaSamw snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, 427037cac00Sjoyce mcintosh fnode); 428da6c28aaSamw 429da6c28aaSamw (void) smb_node_release(xattrdir_node); 430da6c28aaSamw return (snode); 431da6c28aaSamw } 432da6c28aaSamw 433da6c28aaSamw 434da6c28aaSamw /* 435da6c28aaSamw * This function should be called whenever a reference is needed on an 436da6c28aaSamw * smb_node pointer. The copy of an smb_node pointer from one non-local 437da6c28aaSamw * data structure to another requires a reference to be taken on the smb_node 438da6c28aaSamw * (unless the usage is localized). Each data structure deallocation routine 439da6c28aaSamw * will call smb_node_release() on its smb_node pointers. 440da6c28aaSamw * 441da6c28aaSamw * In general, an smb_node pointer residing in a structure should never be 442da6c28aaSamw * stale. A node pointer may be NULL, however, and care should be taken 443da6c28aaSamw * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. 444da6c28aaSamw * Care also needs to be taken with respect to racing deallocations of a 445da6c28aaSamw * structure. 446da6c28aaSamw */ 447da6c28aaSamw void 448da6c28aaSamw smb_node_ref(smb_node_t *node) 449da6c28aaSamw { 4502c2961f8Sjose borrego SMB_NODE_VALID(node); 451da6c28aaSamw 4522c2961f8Sjose borrego mutex_enter(&node->n_mutex); 4532c2961f8Sjose borrego switch (node->n_state) { 4542c2961f8Sjose borrego case SMB_NODE_STATE_AVAILABLE: 4552c2961f8Sjose borrego case SMB_NODE_STATE_OPLOCK_GRANTED: 4562c2961f8Sjose borrego case SMB_NODE_STATE_OPLOCK_BREAKING: 457da6c28aaSamw node->n_refcnt++; 458da6c28aaSamw ASSERT(node->n_refcnt); 459da6c28aaSamw DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); 4602c2961f8Sjose borrego smb_node_audit(node); 4612c2961f8Sjose borrego break; 4622c2961f8Sjose borrego default: 4632c2961f8Sjose borrego SMB_PANIC(); 4642c2961f8Sjose borrego } 4652c2961f8Sjose borrego mutex_exit(&node->n_mutex); 466da6c28aaSamw } 467da6c28aaSamw 468da6c28aaSamw /* 469da6c28aaSamw * smb_node_lookup() takes a hold on an smb_node, whether found in the 470da6c28aaSamw * hash table or newly created. This hold is expected to be released 471da6c28aaSamw * in the following manner. 472da6c28aaSamw * 473da6c28aaSamw * smb_node_lookup() takes an address of an smb_node pointer. This should 474da6c28aaSamw * be getting passed down via a lookup (whether path name or component), mkdir, 475da6c28aaSamw * create. If the original smb_node pointer resides in a data structure, then 476da6c28aaSamw * the deallocation routine for the data structure is responsible for calling 477da6c28aaSamw * smb_node_release() on the smb_node pointer. Alternatively, 478da6c28aaSamw * smb_node_release() can be called as soon as the smb_node pointer is no longer 479da6c28aaSamw * needed. In this case, callers are responsible for setting an embedded 480da6c28aaSamw * pointer to NULL if it is known that the last reference is being released. 481da6c28aaSamw * 482da6c28aaSamw * If the passed-in address of the smb_node pointer belongs to a local variable, 483da6c28aaSamw * then the caller with the local variable should call smb_node_release() 484da6c28aaSamw * directly. 485da6c28aaSamw * 4861fcced4cSJordan Brown * smb_node_release() itself will call smb_node_release() on a node's n_dnode, 4871fcced4cSJordan Brown * as smb_node_lookup() takes a hold on dnode. 488da6c28aaSamw */ 489da6c28aaSamw void 490da6c28aaSamw smb_node_release(smb_node_t *node) 491da6c28aaSamw { 4922c2961f8Sjose borrego SMB_NODE_VALID(node); 493da6c28aaSamw 4942c2961f8Sjose borrego mutex_enter(&node->n_mutex); 495da6c28aaSamw ASSERT(node->n_refcnt); 496da6c28aaSamw DTRACE_PROBE1(smb_node_release, smb_node_t *, node); 497da6c28aaSamw if (--node->n_refcnt == 0) { 498da6c28aaSamw switch (node->n_state) { 499da6c28aaSamw 500da6c28aaSamw case SMB_NODE_STATE_AVAILABLE: 501da6c28aaSamw node->n_state = SMB_NODE_STATE_DESTROYING; 5022c2961f8Sjose borrego mutex_exit(&node->n_mutex); 503da6c28aaSamw 504da6c28aaSamw smb_llist_enter(node->n_hash_bucket, RW_WRITER); 505da6c28aaSamw smb_llist_remove(node->n_hash_bucket, node); 506da6c28aaSamw smb_llist_exit(node->n_hash_bucket); 507da6c28aaSamw 508da6c28aaSamw /* 509da6c28aaSamw * Check if the file was deleted 510da6c28aaSamw */ 511da6c28aaSamw smb_node_delete_on_close(node); 512da6c28aaSamw 5131fcced4cSJordan Brown if (node->n_dnode) { 5141fcced4cSJordan Brown ASSERT(node->n_dnode->n_magic == 515da6c28aaSamw SMB_NODE_MAGIC); 5161fcced4cSJordan Brown smb_node_release(node->n_dnode); 517da6c28aaSamw } 518da6c28aaSamw 5191fcced4cSJordan Brown if (node->n_unode) { 5201fcced4cSJordan Brown ASSERT(node->n_unode->n_magic == 521da6c28aaSamw SMB_NODE_MAGIC); 5221fcced4cSJordan Brown smb_node_release(node->n_unode); 523da6c28aaSamw } 524da6c28aaSamw 5252c2961f8Sjose borrego smb_node_free(node); 526da6c28aaSamw return; 527da6c28aaSamw 528da6c28aaSamw default: 5292c2961f8Sjose borrego SMB_PANIC(); 530da6c28aaSamw } 531da6c28aaSamw } 5322c2961f8Sjose borrego smb_node_audit(node); 5332c2961f8Sjose borrego mutex_exit(&node->n_mutex); 534da6c28aaSamw } 535da6c28aaSamw 536da6c28aaSamw static void 537da6c28aaSamw smb_node_delete_on_close(smb_node_t *node) 538da6c28aaSamw { 539da6c28aaSamw smb_node_t *d_snode; 540da6c28aaSamw int rc = 0; 5418b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States uint32_t flags = 0; 542da6c28aaSamw 5431fcced4cSJordan Brown d_snode = node->n_dnode; 544da6c28aaSamw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 545da6c28aaSamw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 5468b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States flags = node->n_delete_on_close_flags; 547da6c28aaSamw ASSERT(node->od_name != NULL); 5488b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States 549037cac00Sjoyce mcintosh if (node->vp->v_type == VDIR) 550da6c28aaSamw rc = smb_fsop_rmdir(0, node->delete_on_close_cred, 5518b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States d_snode, node->od_name, flags); 552da6c28aaSamw else 553da6c28aaSamw rc = smb_fsop_remove(0, node->delete_on_close_cred, 5548b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States d_snode, node->od_name, flags); 555da6c28aaSamw smb_cred_rele(node->delete_on_close_cred); 556da6c28aaSamw } 557da6c28aaSamw if (rc != 0) 558da6c28aaSamw cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", 559da6c28aaSamw node->od_name, rc); 560da6c28aaSamw DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); 561da6c28aaSamw } 562da6c28aaSamw 563da6c28aaSamw /* 564da6c28aaSamw * smb_node_rename() 565da6c28aaSamw * 566da6c28aaSamw */ 5672c2961f8Sjose borrego void 568da6c28aaSamw smb_node_rename( 5692c2961f8Sjose borrego smb_node_t *from_dnode, 5702c2961f8Sjose borrego smb_node_t *ret_node, 5712c2961f8Sjose borrego smb_node_t *to_dnode, 572da6c28aaSamw char *to_name) 573da6c28aaSamw { 5742c2961f8Sjose borrego SMB_NODE_VALID(from_dnode); 5752c2961f8Sjose borrego SMB_NODE_VALID(to_dnode); 5762c2961f8Sjose borrego SMB_NODE_VALID(ret_node); 577da6c28aaSamw 5782c2961f8Sjose borrego smb_node_ref(to_dnode); 5792c2961f8Sjose borrego mutex_enter(&ret_node->n_mutex); 5802c2961f8Sjose borrego switch (ret_node->n_state) { 5812c2961f8Sjose borrego case SMB_NODE_STATE_AVAILABLE: 5822c2961f8Sjose borrego case SMB_NODE_STATE_OPLOCK_GRANTED: 5832c2961f8Sjose borrego case SMB_NODE_STATE_OPLOCK_BREAKING: 5841fcced4cSJordan Brown ret_node->n_dnode = to_dnode; 5852c2961f8Sjose borrego mutex_exit(&ret_node->n_mutex); 5861fcced4cSJordan Brown ASSERT(to_dnode->n_dnode != ret_node); 5872c2961f8Sjose borrego ASSERT((to_dnode->vp->v_xattrdir) || 5882c2961f8Sjose borrego (to_dnode->vp->v_type == VDIR)); 5892c2961f8Sjose borrego smb_node_release(from_dnode); 5902c2961f8Sjose borrego (void) strcpy(ret_node->od_name, to_name); 591da6c28aaSamw /* 592da6c28aaSamw * XXX Need to update attributes? 593da6c28aaSamw */ 5942c2961f8Sjose borrego break; 5952c2961f8Sjose borrego default: 5962c2961f8Sjose borrego SMB_PANIC(); 5972c2961f8Sjose borrego } 598da6c28aaSamw } 599da6c28aaSamw 600da6c28aaSamw int 601faa1795aSjb150015 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root) 602da6c28aaSamw { 603037cac00Sjoyce mcintosh smb_attr_t attr; 604faa1795aSjb150015 int error; 605faa1795aSjb150015 uint32_t hashkey; 606faa1795aSjb150015 smb_llist_t *node_hdr; 607faa1795aSjb150015 smb_node_t *node; 608da6c28aaSamw 609037cac00Sjoyce mcintosh attr.sa_mask = SMB_AT_ALL; 610037cac00Sjoyce mcintosh error = smb_vop_getattr(vp, NULL, &attr, 0, kcred); 611faa1795aSjb150015 if (error) { 612faa1795aSjb150015 VN_RELE(vp); 613faa1795aSjb150015 return (error); 614faa1795aSjb150015 } 615da6c28aaSamw 616037cac00Sjoyce mcintosh node_hdr = smb_node_get_hash(&vp->v_vfsp->vfs_fsid, &attr, &hashkey); 617faa1795aSjb150015 618037cac00Sjoyce mcintosh node = smb_node_alloc(ROOTVOL, vp, node_hdr, hashkey); 619faa1795aSjb150015 620faa1795aSjb150015 sv->si_root_smb_node = node; 6212c2961f8Sjose borrego smb_node_audit(node); 622faa1795aSjb150015 smb_llist_enter(node_hdr, RW_WRITER); 623faa1795aSjb150015 smb_llist_insert_head(node_hdr, node); 624faa1795aSjb150015 smb_llist_exit(node_hdr); 625faa1795aSjb150015 *root = node; 626da6c28aaSamw return (0); 627da6c28aaSamw } 628da6c28aaSamw 629da6c28aaSamw /* 6308b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * When DeleteOnClose is set on an smb_node, the common open code will 6318b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * reject subsequent open requests for the file. Observation of Windows 6328b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * 2000 indicates that subsequent opens should be allowed (assuming 6338b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * there would be no sharing violation) until the file is closed using 6348b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * the fid on which the DeleteOnClose was requested. 6358b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * 6368b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * If there are multiple opens with delete-on-close create options, 6378b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * whichever the first file handle is closed will trigger the node to be 6388b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * marked as delete-on-close. The credentials of that ofile will be used 6398b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States * as the delete-on-close credentials of the node. 6408b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States */ 641da6c28aaSamw int 6428b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags) 643da6c28aaSamw { 644*a0aa776eSAlan Wright int rc = 0; 645037cac00Sjoyce mcintosh smb_attr_t attr; 646da6c28aaSamw 647*a0aa776eSAlan Wright if (node->readonly_creator) 648037cac00Sjoyce mcintosh return (-1); 649037cac00Sjoyce mcintosh 650037cac00Sjoyce mcintosh bzero(&attr, sizeof (smb_attr_t)); 651037cac00Sjoyce mcintosh attr.sa_mask = SMB_AT_DOSATTR; 652037cac00Sjoyce mcintosh rc = smb_fsop_getattr(NULL, kcred, node, &attr); 653037cac00Sjoyce mcintosh if ((rc != 0) || (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) { 654037cac00Sjoyce mcintosh return (-1); 655037cac00Sjoyce mcintosh } 656037cac00Sjoyce mcintosh 657*a0aa776eSAlan Wright mutex_enter(&node->n_mutex); 658*a0aa776eSAlan Wright if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 659*a0aa776eSAlan Wright rc = -1; 660*a0aa776eSAlan Wright } else { 661da6c28aaSamw crhold(cr); 662da6c28aaSamw node->delete_on_close_cred = cr; 6638b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States node->n_delete_on_close_flags = flags; 664da6c28aaSamw node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; 665*a0aa776eSAlan Wright rc = 0; 666*a0aa776eSAlan Wright } 6672c2961f8Sjose borrego mutex_exit(&node->n_mutex); 668*a0aa776eSAlan Wright return (rc); 669da6c28aaSamw } 670da6c28aaSamw 671da6c28aaSamw void 672da6c28aaSamw smb_node_reset_delete_on_close(smb_node_t *node) 673da6c28aaSamw { 6742c2961f8Sjose borrego mutex_enter(&node->n_mutex); 675da6c28aaSamw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 676da6c28aaSamw node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 677da6c28aaSamw crfree(node->delete_on_close_cred); 678da6c28aaSamw node->delete_on_close_cred = NULL; 6798b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States node->n_delete_on_close_flags = 0; 680da6c28aaSamw } 6812c2961f8Sjose borrego mutex_exit(&node->n_mutex); 682da6c28aaSamw } 683dc20a302Sas200622 684dc20a302Sas200622 /* 6853ad684d6Sjb150015 * smb_node_open_check 686dc20a302Sas200622 * 687dc20a302Sas200622 * check file sharing rules for current open request 688dc20a302Sas200622 * against all existing opens for a file. 689dc20a302Sas200622 * 690dc20a302Sas200622 * Returns NT_STATUS_SHARING_VIOLATION if there is any 691dc20a302Sas200622 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 692dc20a302Sas200622 */ 693dc20a302Sas200622 uint32_t 6942c2961f8Sjose borrego smb_node_open_check( 6952c2961f8Sjose borrego smb_node_t *node, 6962c2961f8Sjose borrego cred_t *cr, 6972c2961f8Sjose borrego uint32_t desired_access, 6982c2961f8Sjose borrego uint32_t share_access) 699dc20a302Sas200622 { 700dc20a302Sas200622 smb_ofile_t *of; 701dc20a302Sas200622 uint32_t status; 702dc20a302Sas200622 7032c2961f8Sjose borrego SMB_NODE_VALID(node); 704dc20a302Sas200622 705dc20a302Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 706dc20a302Sas200622 of = smb_llist_head(&node->n_ofile_list); 707dc20a302Sas200622 while (of) { 7083ad684d6Sjb150015 status = smb_ofile_open_check(of, cr, desired_access, 7093ad684d6Sjb150015 share_access); 7103ad684d6Sjb150015 7113ad684d6Sjb150015 switch (status) { 7123ad684d6Sjb150015 case NT_STATUS_INVALID_HANDLE: 7133ad684d6Sjb150015 case NT_STATUS_SUCCESS: 7143ad684d6Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 7153ad684d6Sjb150015 break; 7163ad684d6Sjb150015 default: 7173ad684d6Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 718dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 719dc20a302Sas200622 return (status); 720dc20a302Sas200622 } 721dc20a302Sas200622 } 7223ad684d6Sjb150015 723dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 724dc20a302Sas200622 return (NT_STATUS_SUCCESS); 725dc20a302Sas200622 } 726dc20a302Sas200622 727dc20a302Sas200622 uint32_t 7282c2961f8Sjose borrego smb_node_rename_check(smb_node_t *node) 729dc20a302Sas200622 { 7302c2961f8Sjose borrego smb_ofile_t *of; 7313ad684d6Sjb150015 uint32_t status; 732dc20a302Sas200622 7332c2961f8Sjose borrego SMB_NODE_VALID(node); 734dc20a302Sas200622 735dc20a302Sas200622 /* 736dc20a302Sas200622 * Intra-CIFS check 737dc20a302Sas200622 */ 738dc20a302Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 7393ad684d6Sjb150015 of = smb_llist_head(&node->n_ofile_list); 7403ad684d6Sjb150015 while (of) { 7413ad684d6Sjb150015 status = smb_ofile_rename_check(of); 742dc20a302Sas200622 7433ad684d6Sjb150015 switch (status) { 7443ad684d6Sjb150015 case NT_STATUS_INVALID_HANDLE: 7453ad684d6Sjb150015 case NT_STATUS_SUCCESS: 7463ad684d6Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 7473ad684d6Sjb150015 break; 7483ad684d6Sjb150015 default: 7493ad684d6Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 750dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 7513ad684d6Sjb150015 return (status); 752dc20a302Sas200622 } 753dc20a302Sas200622 } 754dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 755dc20a302Sas200622 756dc20a302Sas200622 /* 757dc20a302Sas200622 * system-wide share check 758dc20a302Sas200622 */ 759dc20a302Sas200622 if (nbl_share_conflict(node->vp, NBL_RENAME, NULL)) 760dc20a302Sas200622 return (NT_STATUS_SHARING_VIOLATION); 761dc20a302Sas200622 else 762dc20a302Sas200622 return (NT_STATUS_SUCCESS); 763dc20a302Sas200622 } 764dc20a302Sas200622 7653ad684d6Sjb150015 uint32_t 766dc20a302Sas200622 smb_node_delete_check(smb_node_t *node) 767dc20a302Sas200622 { 7683ad684d6Sjb150015 smb_ofile_t *of; 7693ad684d6Sjb150015 uint32_t status; 770dc20a302Sas200622 7712c2961f8Sjose borrego SMB_NODE_VALID(node); 772dc20a302Sas200622 773037cac00Sjoyce mcintosh if (node->vp->v_type == VDIR) 774dc20a302Sas200622 return (NT_STATUS_SUCCESS); 775dc20a302Sas200622 776dc20a302Sas200622 /* 777dc20a302Sas200622 * intra-CIFS check 778dc20a302Sas200622 */ 779dc20a302Sas200622 smb_llist_enter(&node->n_ofile_list, RW_READER); 7803ad684d6Sjb150015 of = smb_llist_head(&node->n_ofile_list); 7813ad684d6Sjb150015 while (of) { 7823ad684d6Sjb150015 status = smb_ofile_delete_check(of); 7833ad684d6Sjb150015 7843ad684d6Sjb150015 switch (status) { 7853ad684d6Sjb150015 case NT_STATUS_INVALID_HANDLE: 7863ad684d6Sjb150015 case NT_STATUS_SUCCESS: 7873ad684d6Sjb150015 of = smb_llist_next(&node->n_ofile_list, of); 7883ad684d6Sjb150015 break; 7893ad684d6Sjb150015 default: 7903ad684d6Sjb150015 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 791dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 7923ad684d6Sjb150015 return (status); 793dc20a302Sas200622 } 794dc20a302Sas200622 } 795dc20a302Sas200622 smb_llist_exit(&node->n_ofile_list); 796dc20a302Sas200622 797dc20a302Sas200622 /* 798dc20a302Sas200622 * system-wide share check 799dc20a302Sas200622 */ 800dc20a302Sas200622 if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL)) 801dc20a302Sas200622 return (NT_STATUS_SHARING_VIOLATION); 802dc20a302Sas200622 else 803dc20a302Sas200622 return (NT_STATUS_SUCCESS); 804dc20a302Sas200622 } 805dc20a302Sas200622 806b1352070SAlan Wright void 807b1352070SAlan Wright smb_node_notify_change(smb_node_t *node) 808b1352070SAlan Wright { 809b1352070SAlan Wright SMB_NODE_VALID(node); 810b1352070SAlan Wright 811b1352070SAlan Wright if (node->flags & NODE_FLAGS_NOTIFY_CHANGE) { 812b1352070SAlan Wright node->flags |= NODE_FLAGS_CHANGED; 813b1352070SAlan Wright smb_process_node_notify_change_queue(node); 814b1352070SAlan Wright } 815b1352070SAlan Wright } 816b1352070SAlan Wright 817dc20a302Sas200622 /* 818dc20a302Sas200622 * smb_node_start_crit() 819dc20a302Sas200622 * 820dc20a302Sas200622 * Enter critical region for share reservations. 821dc20a302Sas200622 * See comments above smb_fsop_shrlock(). 822dc20a302Sas200622 */ 823dc20a302Sas200622 824dc20a302Sas200622 void 825dc20a302Sas200622 smb_node_start_crit(smb_node_t *node, krw_t mode) 826dc20a302Sas200622 { 8272c2961f8Sjose borrego rw_enter(&node->n_lock, mode); 828dc20a302Sas200622 nbl_start_crit(node->vp, mode); 829dc20a302Sas200622 } 830dc20a302Sas200622 831dc20a302Sas200622 /* 832dc20a302Sas200622 * smb_node_end_crit() 833dc20a302Sas200622 * 834dc20a302Sas200622 * Exit critical region for share reservations. 835dc20a302Sas200622 */ 836dc20a302Sas200622 837dc20a302Sas200622 void 838dc20a302Sas200622 smb_node_end_crit(smb_node_t *node) 839dc20a302Sas200622 { 840dc20a302Sas200622 nbl_end_crit(node->vp); 8412c2961f8Sjose borrego rw_exit(&node->n_lock); 842dc20a302Sas200622 } 843dc20a302Sas200622 844dc20a302Sas200622 int 845dc20a302Sas200622 smb_node_in_crit(smb_node_t *node) 846dc20a302Sas200622 { 8472c2961f8Sjose borrego return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_lock)); 8482c2961f8Sjose borrego } 8492c2961f8Sjose borrego 8502c2961f8Sjose borrego void 8512c2961f8Sjose borrego smb_node_rdlock(smb_node_t *node) 8522c2961f8Sjose borrego { 8532c2961f8Sjose borrego rw_enter(&node->n_lock, RW_READER); 8542c2961f8Sjose borrego } 8552c2961f8Sjose borrego 8562c2961f8Sjose borrego void 8572c2961f8Sjose borrego smb_node_wrlock(smb_node_t *node) 8582c2961f8Sjose borrego { 8592c2961f8Sjose borrego rw_enter(&node->n_lock, RW_WRITER); 8602c2961f8Sjose borrego } 8612c2961f8Sjose borrego 8622c2961f8Sjose borrego void 8632c2961f8Sjose borrego smb_node_unlock(smb_node_t *node) 8642c2961f8Sjose borrego { 8652c2961f8Sjose borrego rw_exit(&node->n_lock); 8662c2961f8Sjose borrego } 8672c2961f8Sjose borrego 8682c2961f8Sjose borrego uint32_t 8692c2961f8Sjose borrego smb_node_get_ofile_count(smb_node_t *node) 8702c2961f8Sjose borrego { 8712c2961f8Sjose borrego uint32_t cntr; 8722c2961f8Sjose borrego 8732c2961f8Sjose borrego SMB_NODE_VALID(node); 8742c2961f8Sjose borrego 8752c2961f8Sjose borrego smb_llist_enter(&node->n_ofile_list, RW_READER); 8762c2961f8Sjose borrego cntr = smb_llist_get_count(&node->n_ofile_list); 8772c2961f8Sjose borrego smb_llist_exit(&node->n_ofile_list); 8782c2961f8Sjose borrego return (cntr); 8792c2961f8Sjose borrego } 8802c2961f8Sjose borrego 8812c2961f8Sjose borrego void 8822c2961f8Sjose borrego smb_node_add_ofile(smb_node_t *node, smb_ofile_t *of) 8832c2961f8Sjose borrego { 8842c2961f8Sjose borrego SMB_NODE_VALID(node); 8852c2961f8Sjose borrego 8862c2961f8Sjose borrego smb_llist_enter(&node->n_ofile_list, RW_WRITER); 8872c2961f8Sjose borrego smb_llist_insert_tail(&node->n_ofile_list, of); 8882c2961f8Sjose borrego smb_llist_exit(&node->n_ofile_list); 8892c2961f8Sjose borrego } 8902c2961f8Sjose borrego 8912c2961f8Sjose borrego void 8922c2961f8Sjose borrego smb_node_rem_ofile(smb_node_t *node, smb_ofile_t *of) 8932c2961f8Sjose borrego { 8942c2961f8Sjose borrego SMB_NODE_VALID(node); 8952c2961f8Sjose borrego 8962c2961f8Sjose borrego smb_llist_enter(&node->n_ofile_list, RW_WRITER); 8972c2961f8Sjose borrego smb_llist_remove(&node->n_ofile_list, of); 8982c2961f8Sjose borrego smb_llist_exit(&node->n_ofile_list); 8992c2961f8Sjose borrego } 9002c2961f8Sjose borrego 901037cac00Sjoyce mcintosh /* 902037cac00Sjoyce mcintosh * smb_node_inc_open_ofiles 903037cac00Sjoyce mcintosh */ 9042c2961f8Sjose borrego void 9052c2961f8Sjose borrego smb_node_inc_open_ofiles(smb_node_t *node) 9062c2961f8Sjose borrego { 9072c2961f8Sjose borrego SMB_NODE_VALID(node); 9082c2961f8Sjose borrego 9092c2961f8Sjose borrego mutex_enter(&node->n_mutex); 9102c2961f8Sjose borrego node->n_open_count++; 9112c2961f8Sjose borrego mutex_exit(&node->n_mutex); 912037cac00Sjoyce mcintosh 913e3f2c991SKeyur Desai smb_node_init_cached_data(node); 9142c2961f8Sjose borrego } 9152c2961f8Sjose borrego 916037cac00Sjoyce mcintosh /* 917037cac00Sjoyce mcintosh * smb_node_dec_open_ofiles 918037cac00Sjoyce mcintosh */ 9192c2961f8Sjose borrego void 9202c2961f8Sjose borrego smb_node_dec_open_ofiles(smb_node_t *node) 9212c2961f8Sjose borrego { 9222c2961f8Sjose borrego SMB_NODE_VALID(node); 9232c2961f8Sjose borrego 9242c2961f8Sjose borrego mutex_enter(&node->n_mutex); 9252c2961f8Sjose borrego node->n_open_count--; 9262c2961f8Sjose borrego mutex_exit(&node->n_mutex); 927037cac00Sjoyce mcintosh 928e3f2c991SKeyur Desai smb_node_clear_cached_data(node); 9292c2961f8Sjose borrego } 9302c2961f8Sjose borrego 9312c2961f8Sjose borrego uint32_t 9322c2961f8Sjose borrego smb_node_get_open_ofiles(smb_node_t *node) 9332c2961f8Sjose borrego { 9342c2961f8Sjose borrego uint32_t cnt; 9352c2961f8Sjose borrego 9362c2961f8Sjose borrego SMB_NODE_VALID(node); 9372c2961f8Sjose borrego 9382c2961f8Sjose borrego mutex_enter(&node->n_mutex); 9392c2961f8Sjose borrego cnt = node->n_open_count; 9402c2961f8Sjose borrego mutex_exit(&node->n_mutex); 9412c2961f8Sjose borrego return (cnt); 9422c2961f8Sjose borrego } 9432c2961f8Sjose borrego 9442c2961f8Sjose borrego /* 9452c2961f8Sjose borrego * smb_node_alloc 9462c2961f8Sjose borrego */ 9472c2961f8Sjose borrego static smb_node_t * 9482c2961f8Sjose borrego smb_node_alloc( 9492c2961f8Sjose borrego char *od_name, 9502c2961f8Sjose borrego vnode_t *vp, 9512c2961f8Sjose borrego smb_llist_t *bucket, 9522c2961f8Sjose borrego uint32_t hashkey) 9532c2961f8Sjose borrego { 9542c2961f8Sjose borrego smb_node_t *node; 9552c2961f8Sjose borrego 9562c2961f8Sjose borrego node = kmem_cache_alloc(smb_node_cache, KM_SLEEP); 9572c2961f8Sjose borrego 9582c2961f8Sjose borrego if (node->n_audit_buf != NULL) 9592c2961f8Sjose borrego node->n_audit_buf->anb_index = 0; 9602c2961f8Sjose borrego 961037cac00Sjoyce mcintosh node->flags = 0; 9622c2961f8Sjose borrego VN_HOLD(vp); 9632c2961f8Sjose borrego node->vp = vp; 9642c2961f8Sjose borrego node->n_refcnt = 1; 9652c2961f8Sjose borrego node->n_hash_bucket = bucket; 9662c2961f8Sjose borrego node->n_hashkey = hashkey; 9672c2961f8Sjose borrego node->n_orig_uid = 0; 9682c2961f8Sjose borrego node->readonly_creator = NULL; 9692c2961f8Sjose borrego node->waiting_event = 0; 9702c2961f8Sjose borrego node->n_open_count = 0; 9711fcced4cSJordan Brown node->n_dnode = NULL; 9721fcced4cSJordan Brown node->n_unode = NULL; 9732c2961f8Sjose borrego node->delete_on_close_cred = NULL; 9748b2cc8acSafshin salek ardakani - Sun Microsystems - Irvine United States node->n_delete_on_close_flags = 0; 9752c2961f8Sjose borrego 9762c2961f8Sjose borrego (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); 9772c2961f8Sjose borrego if (strcmp(od_name, XATTR_DIR) == 0) 9782c2961f8Sjose borrego node->flags |= NODE_XATTR_DIR; 9792c2961f8Sjose borrego 9802c2961f8Sjose borrego node->n_state = SMB_NODE_STATE_AVAILABLE; 9812c2961f8Sjose borrego node->n_magic = SMB_NODE_MAGIC; 9822c2961f8Sjose borrego return (node); 9832c2961f8Sjose borrego } 9842c2961f8Sjose borrego 9852c2961f8Sjose borrego /* 9862c2961f8Sjose borrego * smb_node_free 9872c2961f8Sjose borrego */ 9882c2961f8Sjose borrego static void 9892c2961f8Sjose borrego smb_node_free(smb_node_t *node) 9902c2961f8Sjose borrego { 9912c2961f8Sjose borrego SMB_NODE_VALID(node); 9922c2961f8Sjose borrego 9932c2961f8Sjose borrego node->n_magic = 0; 9942c2961f8Sjose borrego VERIFY(!list_link_active(&node->n_lnd)); 9952c2961f8Sjose borrego VERIFY(node->n_lock_list.ll_count == 0); 9962c2961f8Sjose borrego VERIFY(node->n_ofile_list.ll_count == 0); 9972c2961f8Sjose borrego VERIFY(node->n_oplock.ol_xthread == NULL); 998fc724630SAlan Wright VERIFY(mutex_owner(&node->n_mutex) == NULL); 999fc724630SAlan Wright VERIFY(!RW_LOCK_HELD(&node->n_lock)); 10002c2961f8Sjose borrego VN_RELE(node->vp); 10012c2961f8Sjose borrego kmem_cache_free(smb_node_cache, node); 10022c2961f8Sjose borrego } 10032c2961f8Sjose borrego 10042c2961f8Sjose borrego /* 10052c2961f8Sjose borrego * smb_node_constructor 10062c2961f8Sjose borrego */ 10072c2961f8Sjose borrego static int 10082c2961f8Sjose borrego smb_node_constructor(void *buf, void *un, int kmflags) 10092c2961f8Sjose borrego { 10102c2961f8Sjose borrego _NOTE(ARGUNUSED(kmflags, un)) 10112c2961f8Sjose borrego 10122c2961f8Sjose borrego smb_node_t *node = (smb_node_t *)buf; 10132c2961f8Sjose borrego 10142c2961f8Sjose borrego bzero(node, sizeof (smb_node_t)); 10152c2961f8Sjose borrego 10162c2961f8Sjose borrego smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 10172c2961f8Sjose borrego offsetof(smb_ofile_t, f_nnd)); 10182c2961f8Sjose borrego smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 10192c2961f8Sjose borrego offsetof(smb_lock_t, l_lnd)); 10202c2961f8Sjose borrego cv_init(&node->n_oplock.ol_cv, NULL, CV_DEFAULT, NULL); 10212c2961f8Sjose borrego rw_init(&node->n_lock, NULL, RW_DEFAULT, NULL); 10222c2961f8Sjose borrego mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL); 10232c2961f8Sjose borrego smb_node_create_audit_buf(node, kmflags); 10242c2961f8Sjose borrego return (0); 10252c2961f8Sjose borrego } 10262c2961f8Sjose borrego 10272c2961f8Sjose borrego /* 10282c2961f8Sjose borrego * smb_node_destructor 10292c2961f8Sjose borrego */ 10302c2961f8Sjose borrego static void 10312c2961f8Sjose borrego smb_node_destructor(void *buf, void *un) 10322c2961f8Sjose borrego { 10332c2961f8Sjose borrego _NOTE(ARGUNUSED(un)) 10342c2961f8Sjose borrego 10352c2961f8Sjose borrego smb_node_t *node = (smb_node_t *)buf; 10362c2961f8Sjose borrego 10372c2961f8Sjose borrego smb_node_destroy_audit_buf(node); 10382c2961f8Sjose borrego mutex_destroy(&node->n_mutex); 10392c2961f8Sjose borrego rw_destroy(&node->n_lock); 10402c2961f8Sjose borrego cv_destroy(&node->n_oplock.ol_cv); 10412c2961f8Sjose borrego smb_llist_destructor(&node->n_lock_list); 10422c2961f8Sjose borrego smb_llist_destructor(&node->n_ofile_list); 10432c2961f8Sjose borrego } 10442c2961f8Sjose borrego 10452c2961f8Sjose borrego /* 10462c2961f8Sjose borrego * smb_node_create_audit_buf 10472c2961f8Sjose borrego */ 10482c2961f8Sjose borrego static void 10492c2961f8Sjose borrego smb_node_create_audit_buf(smb_node_t *node, int kmflags) 10502c2961f8Sjose borrego { 10512c2961f8Sjose borrego smb_audit_buf_node_t *abn; 10522c2961f8Sjose borrego 10532c2961f8Sjose borrego if (smb_audit_flags & SMB_AUDIT_NODE) { 10542c2961f8Sjose borrego abn = kmem_zalloc(sizeof (smb_audit_buf_node_t), kmflags); 10552c2961f8Sjose borrego abn->anb_max_index = SMB_AUDIT_BUF_MAX_REC - 1; 10562c2961f8Sjose borrego node->n_audit_buf = abn; 10572c2961f8Sjose borrego } 10582c2961f8Sjose borrego } 10592c2961f8Sjose borrego 10602c2961f8Sjose borrego /* 10612c2961f8Sjose borrego * smb_node_destroy_audit_buf 10622c2961f8Sjose borrego */ 10632c2961f8Sjose borrego static void 10642c2961f8Sjose borrego smb_node_destroy_audit_buf(smb_node_t *node) 10652c2961f8Sjose borrego { 10662c2961f8Sjose borrego if (node->n_audit_buf != NULL) { 10672c2961f8Sjose borrego kmem_free(node->n_audit_buf, sizeof (smb_audit_buf_node_t)); 10682c2961f8Sjose borrego node->n_audit_buf = NULL; 10692c2961f8Sjose borrego } 10702c2961f8Sjose borrego } 10712c2961f8Sjose borrego 10722c2961f8Sjose borrego /* 10732c2961f8Sjose borrego * smb_node_audit 10742c2961f8Sjose borrego * 10752c2961f8Sjose borrego * This function saves the calling stack in the audit buffer of the node passed 10762c2961f8Sjose borrego * in. 10772c2961f8Sjose borrego */ 10782c2961f8Sjose borrego static void 10792c2961f8Sjose borrego smb_node_audit(smb_node_t *node) 10802c2961f8Sjose borrego { 10812c2961f8Sjose borrego smb_audit_buf_node_t *abn; 10822c2961f8Sjose borrego smb_audit_record_node_t *anr; 10832c2961f8Sjose borrego 10842c2961f8Sjose borrego if (node->n_audit_buf) { 10852c2961f8Sjose borrego abn = node->n_audit_buf; 10862c2961f8Sjose borrego anr = abn->anb_records; 10872c2961f8Sjose borrego anr += abn->anb_index; 10882c2961f8Sjose borrego abn->anb_index++; 10892c2961f8Sjose borrego abn->anb_index &= abn->anb_max_index; 10902c2961f8Sjose borrego anr->anr_refcnt = node->n_refcnt; 10912c2961f8Sjose borrego anr->anr_depth = getpcstack(anr->anr_stack, 10922c2961f8Sjose borrego SMB_AUDIT_STACK_DEPTH); 10932c2961f8Sjose borrego } 10942c2961f8Sjose borrego } 10952c2961f8Sjose borrego 10962c2961f8Sjose borrego static smb_llist_t * 10972c2961f8Sjose borrego smb_node_get_hash(fsid_t *fsid, smb_attr_t *attr, uint32_t *phashkey) 10982c2961f8Sjose borrego { 10992c2961f8Sjose borrego uint32_t hashkey; 11002c2961f8Sjose borrego 11012c2961f8Sjose borrego hashkey = fsid->val[0] + attr->sa_vattr.va_nodeid; 11022c2961f8Sjose borrego hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 11032c2961f8Sjose borrego *phashkey = hashkey; 11042c2961f8Sjose borrego return (&smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]); 1105dc20a302Sas200622 } 1106037cac00Sjoyce mcintosh 1107037cac00Sjoyce mcintosh boolean_t 1108037cac00Sjoyce mcintosh smb_node_is_dir(smb_node_t *node) 1109037cac00Sjoyce mcintosh { 1110037cac00Sjoyce mcintosh SMB_NODE_VALID(node); 1111037cac00Sjoyce mcintosh return (node->vp->v_type == VDIR); 1112037cac00Sjoyce mcintosh } 1113037cac00Sjoyce mcintosh 1114037cac00Sjoyce mcintosh boolean_t 1115037cac00Sjoyce mcintosh smb_node_is_link(smb_node_t *node) 1116037cac00Sjoyce mcintosh { 1117037cac00Sjoyce mcintosh SMB_NODE_VALID(node); 1118037cac00Sjoyce mcintosh return (node->vp->v_type == VLNK); 1119037cac00Sjoyce mcintosh } 1120037cac00Sjoyce mcintosh 1121037cac00Sjoyce mcintosh /* 1122037cac00Sjoyce mcintosh * smb_node_file_is_readonly 1123037cac00Sjoyce mcintosh * 1124037cac00Sjoyce mcintosh * Checks if the file (which node represents) is marked readonly 1125037cac00Sjoyce mcintosh * in the filesystem. No account is taken of any pending readonly 1126037cac00Sjoyce mcintosh * in the node, which must be handled by the callers. 1127037cac00Sjoyce mcintosh * (See SMB_OFILE_IS_READONLY and SMB_PATHFILE_IS_READONLY) 1128037cac00Sjoyce mcintosh */ 1129037cac00Sjoyce mcintosh boolean_t 1130037cac00Sjoyce mcintosh smb_node_file_is_readonly(smb_node_t *node) 1131037cac00Sjoyce mcintosh { 1132037cac00Sjoyce mcintosh smb_attr_t attr; 1133037cac00Sjoyce mcintosh 1134037cac00Sjoyce mcintosh if (node == NULL) 1135037cac00Sjoyce mcintosh return (B_FALSE); 1136037cac00Sjoyce mcintosh 1137037cac00Sjoyce mcintosh bzero(&attr, sizeof (smb_attr_t)); 1138037cac00Sjoyce mcintosh attr.sa_mask = SMB_AT_DOSATTR; 1139037cac00Sjoyce mcintosh (void) smb_fsop_getattr(NULL, kcred, node, &attr); 1140037cac00Sjoyce mcintosh return ((attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) != 0); 1141037cac00Sjoyce mcintosh } 1142037cac00Sjoyce mcintosh 1143037cac00Sjoyce mcintosh /* 1144037cac00Sjoyce mcintosh * smb_node_setattr 1145037cac00Sjoyce mcintosh * 1146037cac00Sjoyce mcintosh * The sr may be NULL, for example when closing an ofile. 1147037cac00Sjoyce mcintosh * The ofile may be NULL, for example when a client request 1148037cac00Sjoyce mcintosh * specifies the file by pathname. 1149037cac00Sjoyce mcintosh * 1150e3f2c991SKeyur Desai * Timestamps 1151037cac00Sjoyce mcintosh * When attributes are set on an ofile, any pending timestamps 1152037cac00Sjoyce mcintosh * from a write request on the ofile are implicitly set to "now". 1153037cac00Sjoyce mcintosh * For compatibility with windows the following timestamps are 1154037cac00Sjoyce mcintosh * also implicitly set to now: 1155037cac00Sjoyce mcintosh * - if any attribute is being explicitly set, set ctime to now 1156037cac00Sjoyce mcintosh * - if file size is being explicitly set, set atime & ctime to now 1157037cac00Sjoyce mcintosh * 1158e3f2c991SKeyur Desai * Any timestamp that is being explicitly set, or has previously 1159037cac00Sjoyce mcintosh * been explicitly set on the ofile, is excluded from implicit 1160037cac00Sjoyce mcintosh * (now) setting. 1161037cac00Sjoyce mcintosh * 1162037cac00Sjoyce mcintosh * Updates the node's cached timestamp values. 1163037cac00Sjoyce mcintosh * Updates the ofile's explicit times flag. 1164037cac00Sjoyce mcintosh * 1165e3f2c991SKeyur Desai * File allocation size 1166e3f2c991SKeyur Desai * When the file allocation size is set it is first rounded up 1167e3f2c991SKeyur Desai * to block size. If the file size is smaller than the allocation 1168e3f2c991SKeyur Desai * size the file is truncated by setting the filesize to allocsz. 1169e3f2c991SKeyur Desai * If there are open ofiles, the allocsz is cached on the node. 1170e3f2c991SKeyur Desai * 1171e3f2c991SKeyur Desai * Updates the node's cached allocsz value. 1172e3f2c991SKeyur Desai * 1173037cac00Sjoyce mcintosh * Returns: errno 1174037cac00Sjoyce mcintosh */ 1175037cac00Sjoyce mcintosh int 1176037cac00Sjoyce mcintosh smb_node_setattr(smb_request_t *sr, smb_node_t *node, 1177037cac00Sjoyce mcintosh cred_t *cr, smb_ofile_t *of, smb_attr_t *attr) 1178037cac00Sjoyce mcintosh { 1179037cac00Sjoyce mcintosh int rc; 1180e3f2c991SKeyur Desai uint32_t pending_times = 0; 1181e3f2c991SKeyur Desai uint32_t explicit_times = 0; 1182037cac00Sjoyce mcintosh timestruc_t now; 1183e3f2c991SKeyur Desai smb_attr_t tmp_attr; 1184037cac00Sjoyce mcintosh 1185037cac00Sjoyce mcintosh ASSERT(attr); 1186037cac00Sjoyce mcintosh SMB_NODE_VALID(node); 1187037cac00Sjoyce mcintosh 1188e3f2c991SKeyur Desai /* set attributes specified in attr */ 1189e3f2c991SKeyur Desai if (attr->sa_mask != 0) { 1190e3f2c991SKeyur Desai /* if allocation size is < file size, truncate the file */ 1191e3f2c991SKeyur Desai if (attr->sa_mask & SMB_AT_ALLOCSZ) { 1192e3f2c991SKeyur Desai attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_allocsz); 1193037cac00Sjoyce mcintosh 1194e3f2c991SKeyur Desai bzero(&tmp_attr, sizeof (smb_attr_t)); 1195e3f2c991SKeyur Desai tmp_attr.sa_mask = SMB_AT_SIZE; 1196e3f2c991SKeyur Desai (void) smb_fsop_getattr(NULL, kcred, node, &tmp_attr); 1197037cac00Sjoyce mcintosh 1198e3f2c991SKeyur Desai if (tmp_attr.sa_vattr.va_size > attr->sa_allocsz) { 1199e3f2c991SKeyur Desai attr->sa_vattr.va_size = attr->sa_allocsz; 1200e3f2c991SKeyur Desai attr->sa_mask |= SMB_AT_SIZE; 1201037cac00Sjoyce mcintosh } 1202037cac00Sjoyce mcintosh } 1203037cac00Sjoyce mcintosh 1204037cac00Sjoyce mcintosh rc = smb_fsop_setattr(sr, cr, node, attr); 1205037cac00Sjoyce mcintosh if (rc != 0) 1206037cac00Sjoyce mcintosh return (rc); 1207037cac00Sjoyce mcintosh 1208e3f2c991SKeyur Desai smb_node_set_cached_allocsz(node, attr); 1209037cac00Sjoyce mcintosh smb_node_set_cached_timestamps(node, attr); 1210e3f2c991SKeyur Desai if (of) { 1211e3f2c991SKeyur Desai smb_ofile_set_explicit_times(of, 1212e3f2c991SKeyur Desai (attr->sa_mask & SMB_AT_TIMES)); 1213e3f2c991SKeyur Desai } 1214e3f2c991SKeyur Desai } 1215037cac00Sjoyce mcintosh 1216e3f2c991SKeyur Desai /* 1217e3f2c991SKeyur Desai * Determine which timestamps to implicitly set to "now". 1218e3f2c991SKeyur Desai * Don't overwrite timestamps already explicitly set. 1219e3f2c991SKeyur Desai */ 1220e3f2c991SKeyur Desai bzero(&tmp_attr, sizeof (smb_attr_t)); 1221e3f2c991SKeyur Desai gethrestime(&now); 1222e3f2c991SKeyur Desai tmp_attr.sa_vattr.va_atime = now; 1223e3f2c991SKeyur Desai tmp_attr.sa_vattr.va_mtime = now; 1224e3f2c991SKeyur Desai tmp_attr.sa_vattr.va_ctime = now; 1225e3f2c991SKeyur Desai 1226e3f2c991SKeyur Desai /* pending write timestamps */ 1227e3f2c991SKeyur Desai if (of) { 1228e3f2c991SKeyur Desai if (smb_ofile_write_time_pending(of)) { 1229e3f2c991SKeyur Desai pending_times |= 1230e3f2c991SKeyur Desai (SMB_AT_MTIME | SMB_AT_CTIME | SMB_AT_ATIME); 1231e3f2c991SKeyur Desai } 1232e3f2c991SKeyur Desai explicit_times |= (smb_ofile_explicit_times(of)); 1233e3f2c991SKeyur Desai } 1234e3f2c991SKeyur Desai explicit_times |= (attr->sa_mask & SMB_AT_TIMES); 1235e3f2c991SKeyur Desai pending_times &= ~explicit_times; 1236e3f2c991SKeyur Desai 1237e3f2c991SKeyur Desai if (pending_times) { 1238e3f2c991SKeyur Desai tmp_attr.sa_mask = pending_times; 1239e3f2c991SKeyur Desai (void) smb_fsop_setattr(NULL, kcred, node, &tmp_attr); 1240e3f2c991SKeyur Desai } 1241e3f2c991SKeyur Desai 1242e3f2c991SKeyur Desai /* additional timestamps to update in cache */ 1243e3f2c991SKeyur Desai if (attr->sa_mask) 1244e3f2c991SKeyur Desai tmp_attr.sa_mask |= SMB_AT_CTIME; 1245e3f2c991SKeyur Desai if (attr->sa_mask & (SMB_AT_SIZE | SMB_AT_ALLOCSZ)) 1246e3f2c991SKeyur Desai tmp_attr.sa_mask |= SMB_AT_MTIME; 1247e3f2c991SKeyur Desai tmp_attr.sa_mask &= ~explicit_times; 1248e3f2c991SKeyur Desai 1249e3f2c991SKeyur Desai if (tmp_attr.sa_mask) 1250e3f2c991SKeyur Desai smb_node_set_cached_timestamps(node, &tmp_attr); 1251037cac00Sjoyce mcintosh 1252037cac00Sjoyce mcintosh return (0); 1253037cac00Sjoyce mcintosh } 1254037cac00Sjoyce mcintosh 1255037cac00Sjoyce mcintosh /* 1256037cac00Sjoyce mcintosh * smb_node_getattr 1257037cac00Sjoyce mcintosh * 1258037cac00Sjoyce mcintosh * Get attributes from the file system and apply any smb-specific 1259037cac00Sjoyce mcintosh * overrides for size, dos attributes and timestamps 1260037cac00Sjoyce mcintosh * 1261037cac00Sjoyce mcintosh * node->readonly_creator reflects whether a readonly set is pending 1262037cac00Sjoyce mcintosh * from a readonly create. The readonly attribute should be visible to 1263037cac00Sjoyce mcintosh * all clients even though the readonly creator fid is immune to the 1264037cac00Sjoyce mcintosh * readonly bit until close. 1265037cac00Sjoyce mcintosh * 1266037cac00Sjoyce mcintosh * Returns: errno 1267037cac00Sjoyce mcintosh */ 1268037cac00Sjoyce mcintosh int 1269037cac00Sjoyce mcintosh smb_node_getattr(smb_request_t *sr, smb_node_t *node, smb_attr_t *attr) 1270037cac00Sjoyce mcintosh { 1271037cac00Sjoyce mcintosh int rc; 1272037cac00Sjoyce mcintosh 1273037cac00Sjoyce mcintosh SMB_NODE_VALID(node); 1274037cac00Sjoyce mcintosh 1275037cac00Sjoyce mcintosh bzero(attr, sizeof (smb_attr_t)); 1276037cac00Sjoyce mcintosh attr->sa_mask = SMB_AT_ALL; 1277037cac00Sjoyce mcintosh rc = smb_fsop_getattr(sr, kcred, node, attr); 1278037cac00Sjoyce mcintosh if (rc != 0) 1279037cac00Sjoyce mcintosh return (rc); 1280037cac00Sjoyce mcintosh 1281037cac00Sjoyce mcintosh mutex_enter(&node->n_mutex); 1282037cac00Sjoyce mcintosh 1283e3f2c991SKeyur Desai if (node->vp->v_type == VDIR) { 1284037cac00Sjoyce mcintosh attr->sa_vattr.va_size = 0; 1285e3f2c991SKeyur Desai attr->sa_allocsz = 0; 1286e3f2c991SKeyur Desai } 1287037cac00Sjoyce mcintosh 1288037cac00Sjoyce mcintosh if (node->readonly_creator) 1289037cac00Sjoyce mcintosh attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY; 1290037cac00Sjoyce mcintosh if (attr->sa_dosattr == 0) 1291037cac00Sjoyce mcintosh attr->sa_dosattr = FILE_ATTRIBUTE_NORMAL; 1292037cac00Sjoyce mcintosh 1293e3f2c991SKeyur Desai 1294037cac00Sjoyce mcintosh mutex_exit(&node->n_mutex); 1295037cac00Sjoyce mcintosh 1296e3f2c991SKeyur Desai smb_node_get_cached_allocsz(node, attr); 1297037cac00Sjoyce mcintosh smb_node_get_cached_timestamps(node, attr); 1298e3f2c991SKeyur Desai 1299037cac00Sjoyce mcintosh return (0); 1300037cac00Sjoyce mcintosh } 1301037cac00Sjoyce mcintosh 1302037cac00Sjoyce mcintosh /* 1303e3f2c991SKeyur Desai * smb_node_init_cached_data 1304e3f2c991SKeyur Desai */ 1305e3f2c991SKeyur Desai static void 1306e3f2c991SKeyur Desai smb_node_init_cached_data(smb_node_t *node) 1307e3f2c991SKeyur Desai { 1308e3f2c991SKeyur Desai smb_attr_t attr; 1309e3f2c991SKeyur Desai 1310e3f2c991SKeyur Desai bzero(&attr, sizeof (smb_attr_t)); 1311e3f2c991SKeyur Desai attr.sa_mask = SMB_AT_ALL; 1312e3f2c991SKeyur Desai (void) smb_fsop_getattr(NULL, kcred, node, &attr); 1313e3f2c991SKeyur Desai 1314e3f2c991SKeyur Desai smb_node_init_cached_allocsz(node, &attr); 1315e3f2c991SKeyur Desai smb_node_init_cached_timestamps(node, &attr); 1316e3f2c991SKeyur Desai } 1317e3f2c991SKeyur Desai 1318e3f2c991SKeyur Desai /* 1319e3f2c991SKeyur Desai * smb_node_clear_cached_data 1320e3f2c991SKeyur Desai */ 1321e3f2c991SKeyur Desai static void 1322e3f2c991SKeyur Desai smb_node_clear_cached_data(smb_node_t *node) 1323e3f2c991SKeyur Desai { 1324e3f2c991SKeyur Desai smb_node_clear_cached_allocsz(node); 1325e3f2c991SKeyur Desai smb_node_clear_cached_timestamps(node); 1326e3f2c991SKeyur Desai } 1327e3f2c991SKeyur Desai 1328e3f2c991SKeyur Desai /* 1329e3f2c991SKeyur Desai * File allocation size (allocsz) caching 1330e3f2c991SKeyur Desai * 1331e3f2c991SKeyur Desai * When there are open ofiles on the node, the file allocsz is cached. 1332e3f2c991SKeyur Desai * The cached value (n_allocsz) is initialized when the first ofile is 1333e3f2c991SKeyur Desai * opened and cleared when the last is closed. Allocsz calculated from 1334e3f2c991SKeyur Desai * the filesize (rounded up to block size). 1335e3f2c991SKeyur Desai * When the allocation size is queried, if the cached allocsz is less 1336e3f2c991SKeyur Desai * than the filesize, it is recalculated from the filesize. 1337e3f2c991SKeyur Desai */ 1338e3f2c991SKeyur Desai 1339e3f2c991SKeyur Desai /* 1340e3f2c991SKeyur Desai * smb_node_init_cached_allocsz 1341e3f2c991SKeyur Desai * 1342e3f2c991SKeyur Desai * If there are open ofiles, cache the allocsz in the node. 1343e3f2c991SKeyur Desai * Calculate the allocsz from the filesizes. 1344e3f2c991SKeyur Desai * block size). 1345e3f2c991SKeyur Desai */ 1346e3f2c991SKeyur Desai static void 1347e3f2c991SKeyur Desai smb_node_init_cached_allocsz(smb_node_t *node, smb_attr_t *attr) 1348e3f2c991SKeyur Desai { 1349e3f2c991SKeyur Desai mutex_enter(&node->n_mutex); 1350e3f2c991SKeyur Desai if (node->n_open_count == 1) 1351e3f2c991SKeyur Desai node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size); 1352e3f2c991SKeyur Desai mutex_exit(&node->n_mutex); 1353e3f2c991SKeyur Desai } 1354e3f2c991SKeyur Desai 1355e3f2c991SKeyur Desai /* 1356e3f2c991SKeyur Desai * smb_node_clear_cached_allocsz 1357e3f2c991SKeyur Desai */ 1358e3f2c991SKeyur Desai static void 1359e3f2c991SKeyur Desai smb_node_clear_cached_allocsz(smb_node_t *node) 1360e3f2c991SKeyur Desai { 1361e3f2c991SKeyur Desai mutex_enter(&node->n_mutex); 1362e3f2c991SKeyur Desai if (node->n_open_count == 0) 1363e3f2c991SKeyur Desai node->n_allocsz = 0; 1364e3f2c991SKeyur Desai mutex_exit(&node->n_mutex); 1365e3f2c991SKeyur Desai } 1366e3f2c991SKeyur Desai 1367e3f2c991SKeyur Desai /* 1368e3f2c991SKeyur Desai * smb_node_get_cached_allocsz 1369e3f2c991SKeyur Desai * 1370e3f2c991SKeyur Desai * If there is no cached allocsz (no open files), calculate 1371e3f2c991SKeyur Desai * allocsz from the filesize. 1372e3f2c991SKeyur Desai * If the allocsz is cached but is smaller than the filesize 1373e3f2c991SKeyur Desai * recalculate the cached allocsz from the filesize. 1374e3f2c991SKeyur Desai * 1375e3f2c991SKeyur Desai * Return allocs in attr->sa_allocsz. 1376e3f2c991SKeyur Desai */ 1377e3f2c991SKeyur Desai static void 1378e3f2c991SKeyur Desai smb_node_get_cached_allocsz(smb_node_t *node, smb_attr_t *attr) 1379e3f2c991SKeyur Desai { 1380e3f2c991SKeyur Desai if (node->vp->v_type == VDIR) 1381e3f2c991SKeyur Desai return; 1382e3f2c991SKeyur Desai 1383e3f2c991SKeyur Desai mutex_enter(&node->n_mutex); 1384e3f2c991SKeyur Desai if (node->n_open_count == 0) { 1385e3f2c991SKeyur Desai attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size); 1386e3f2c991SKeyur Desai } else { 1387e3f2c991SKeyur Desai if (node->n_allocsz < attr->sa_vattr.va_size) 1388e3f2c991SKeyur Desai node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size); 1389e3f2c991SKeyur Desai attr->sa_allocsz = node->n_allocsz; 1390e3f2c991SKeyur Desai } 1391e3f2c991SKeyur Desai mutex_exit(&node->n_mutex); 1392e3f2c991SKeyur Desai } 1393e3f2c991SKeyur Desai 1394e3f2c991SKeyur Desai /* 1395e3f2c991SKeyur Desai * smb_node_set_cached_allocsz 1396e3f2c991SKeyur Desai * 1397e3f2c991SKeyur Desai * attr->sa_allocsz has already been rounded to block size by 1398e3f2c991SKeyur Desai * the caller. 1399e3f2c991SKeyur Desai */ 1400e3f2c991SKeyur Desai static void 1401e3f2c991SKeyur Desai smb_node_set_cached_allocsz(smb_node_t *node, smb_attr_t *attr) 1402e3f2c991SKeyur Desai { 1403e3f2c991SKeyur Desai mutex_enter(&node->n_mutex); 1404e3f2c991SKeyur Desai if (attr->sa_mask & SMB_AT_ALLOCSZ) { 1405e3f2c991SKeyur Desai if (node->n_open_count > 0) 1406e3f2c991SKeyur Desai node->n_allocsz = attr->sa_allocsz; 1407e3f2c991SKeyur Desai } 1408e3f2c991SKeyur Desai mutex_exit(&node->n_mutex); 1409e3f2c991SKeyur Desai } 1410e3f2c991SKeyur Desai 1411e3f2c991SKeyur Desai 1412e3f2c991SKeyur Desai /* 1413037cac00Sjoyce mcintosh * Timestamp caching 1414037cac00Sjoyce mcintosh * 1415037cac00Sjoyce mcintosh * Solaris file systems handle timestamps different from NTFS. For 1416037cac00Sjoyce mcintosh * example when file data is written NTFS doesn't update the timestamps 1417037cac00Sjoyce mcintosh * until the file is closed, and then only if they haven't been explicity 1418037cac00Sjoyce mcintosh * set via a set attribute request. In order to provide a more similar 1419037cac00Sjoyce mcintosh * view of an open file's timestamps, we cache the timestamps in the 1420037cac00Sjoyce mcintosh * node and manipulate them in a manner more consistent with windows. 1421037cac00Sjoyce mcintosh * (See handling of explicit times and pending timestamps from a write 1422037cac00Sjoyce mcintosh * request in smb_node_getattr and smb_node_setattr above.) 1423037cac00Sjoyce mcintosh * Timestamps remain cached while there are open ofiles for the node. 1424037cac00Sjoyce mcintosh * This includes open ofiles for named streams. 1425037cac00Sjoyce mcintosh * n_open_ofiles cannot be used as this doesn't include ofiles opened 1426037cac00Sjoyce mcintosh * for the node's named streams. Thus n_timestamps contains a count 1427037cac00Sjoyce mcintosh * of open ofiles (t_open_ofiles), including named streams' ofiles, 1428037cac00Sjoyce mcintosh * to be used to control timestamp caching. 1429037cac00Sjoyce mcintosh * 1430037cac00Sjoyce mcintosh * If a node represents a named stream the associated unnamed streams 1431037cac00Sjoyce mcintosh * cached timestamps are used instead. 1432037cac00Sjoyce mcintosh */ 1433037cac00Sjoyce mcintosh 1434037cac00Sjoyce mcintosh /* 1435037cac00Sjoyce mcintosh * smb_node_init_cached_timestamps 1436037cac00Sjoyce mcintosh * 1437037cac00Sjoyce mcintosh * Increment count of open ofiles which are using the cached timestamps. 1438037cac00Sjoyce mcintosh * If this is the first open ofile, init the cached timestamps from the 1439037cac00Sjoyce mcintosh * file system values. 1440037cac00Sjoyce mcintosh */ 1441037cac00Sjoyce mcintosh static void 1442e3f2c991SKeyur Desai smb_node_init_cached_timestamps(smb_node_t *node, smb_attr_t *attr) 1443037cac00Sjoyce mcintosh { 1444037cac00Sjoyce mcintosh smb_node_t *unode; 1445037cac00Sjoyce mcintosh 1446037cac00Sjoyce mcintosh if ((unode = SMB_IS_STREAM(node)) != NULL) 1447037cac00Sjoyce mcintosh node = unode; 1448037cac00Sjoyce mcintosh 1449037cac00Sjoyce mcintosh mutex_enter(&node->n_mutex); 1450037cac00Sjoyce mcintosh ++(node->n_timestamps.t_open_ofiles); 1451037cac00Sjoyce mcintosh if (node->n_timestamps.t_open_ofiles == 1) { 1452e3f2c991SKeyur Desai node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime; 1453e3f2c991SKeyur Desai node->n_timestamps.t_atime = attr->sa_vattr.va_atime; 1454e3f2c991SKeyur Desai node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime; 1455e3f2c991SKeyur Desai node->n_timestamps.t_crtime = attr->sa_crtime; 1456037cac00Sjoyce mcintosh node->n_timestamps.t_cached = B_TRUE; 1457037cac00Sjoyce mcintosh } 1458037cac00Sjoyce mcintosh mutex_exit(&node->n_mutex); 1459037cac00Sjoyce mcintosh } 1460037cac00Sjoyce mcintosh 1461037cac00Sjoyce mcintosh /* 1462037cac00Sjoyce mcintosh * smb_node_clear_cached_timestamps 1463037cac00Sjoyce mcintosh * 1464037cac00Sjoyce mcintosh * Decrement count of open ofiles using the cached timestamps. 1465037cac00Sjoyce mcintosh * If the decremented count is zero, clear the cached timestamps. 1466037cac00Sjoyce mcintosh */ 1467037cac00Sjoyce mcintosh static void 1468037cac00Sjoyce mcintosh smb_node_clear_cached_timestamps(smb_node_t *node) 1469037cac00Sjoyce mcintosh { 1470037cac00Sjoyce mcintosh smb_node_t *unode; 1471037cac00Sjoyce mcintosh 1472037cac00Sjoyce mcintosh if ((unode = SMB_IS_STREAM(node)) != NULL) 1473037cac00Sjoyce mcintosh node = unode; 1474037cac00Sjoyce mcintosh 1475037cac00Sjoyce mcintosh mutex_enter(&node->n_mutex); 1476037cac00Sjoyce mcintosh ASSERT(node->n_timestamps.t_open_ofiles > 0); 1477037cac00Sjoyce mcintosh --(node->n_timestamps.t_open_ofiles); 1478037cac00Sjoyce mcintosh if (node->n_timestamps.t_open_ofiles == 0) 1479037cac00Sjoyce mcintosh bzero(&node->n_timestamps, sizeof (smb_times_t)); 1480037cac00Sjoyce mcintosh mutex_exit(&node->n_mutex); 1481037cac00Sjoyce mcintosh } 1482037cac00Sjoyce mcintosh 1483037cac00Sjoyce mcintosh /* 1484037cac00Sjoyce mcintosh * smb_node_get_cached_timestamps 1485037cac00Sjoyce mcintosh * 1486037cac00Sjoyce mcintosh * Overwrite timestamps in attr with those cached in node. 1487037cac00Sjoyce mcintosh */ 1488037cac00Sjoyce mcintosh static void 1489037cac00Sjoyce mcintosh smb_node_get_cached_timestamps(smb_node_t *node, smb_attr_t *attr) 1490037cac00Sjoyce mcintosh { 1491037cac00Sjoyce mcintosh smb_node_t *unode; 1492037cac00Sjoyce mcintosh 1493037cac00Sjoyce mcintosh if ((unode = SMB_IS_STREAM(node)) != NULL) 1494037cac00Sjoyce mcintosh node = unode; 1495037cac00Sjoyce mcintosh 1496037cac00Sjoyce mcintosh mutex_enter(&node->n_mutex); 1497037cac00Sjoyce mcintosh if (node->n_timestamps.t_cached) { 1498037cac00Sjoyce mcintosh attr->sa_vattr.va_mtime = node->n_timestamps.t_mtime; 1499037cac00Sjoyce mcintosh attr->sa_vattr.va_atime = node->n_timestamps.t_atime; 1500037cac00Sjoyce mcintosh attr->sa_vattr.va_ctime = node->n_timestamps.t_ctime; 1501037cac00Sjoyce mcintosh attr->sa_crtime = node->n_timestamps.t_crtime; 1502037cac00Sjoyce mcintosh } 1503037cac00Sjoyce mcintosh mutex_exit(&node->n_mutex); 1504037cac00Sjoyce mcintosh } 1505037cac00Sjoyce mcintosh 1506037cac00Sjoyce mcintosh /* 1507037cac00Sjoyce mcintosh * smb_node_set_cached_timestamps 1508037cac00Sjoyce mcintosh * 1509037cac00Sjoyce mcintosh * Update the node's cached timestamps with values from attr. 1510037cac00Sjoyce mcintosh */ 1511037cac00Sjoyce mcintosh static void 1512037cac00Sjoyce mcintosh smb_node_set_cached_timestamps(smb_node_t *node, smb_attr_t *attr) 1513037cac00Sjoyce mcintosh { 1514037cac00Sjoyce mcintosh smb_node_t *unode; 1515037cac00Sjoyce mcintosh 1516037cac00Sjoyce mcintosh if ((unode = SMB_IS_STREAM(node)) != NULL) 1517037cac00Sjoyce mcintosh node = unode; 1518037cac00Sjoyce mcintosh 1519037cac00Sjoyce mcintosh mutex_enter(&node->n_mutex); 1520037cac00Sjoyce mcintosh if (node->n_timestamps.t_cached) { 1521037cac00Sjoyce mcintosh if (attr->sa_mask & SMB_AT_MTIME) 1522037cac00Sjoyce mcintosh node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime; 1523037cac00Sjoyce mcintosh if (attr->sa_mask & SMB_AT_ATIME) 1524037cac00Sjoyce mcintosh node->n_timestamps.t_atime = attr->sa_vattr.va_atime; 1525037cac00Sjoyce mcintosh if (attr->sa_mask & SMB_AT_CTIME) 1526037cac00Sjoyce mcintosh node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime; 1527037cac00Sjoyce mcintosh if (attr->sa_mask & SMB_AT_CRTIME) 1528037cac00Sjoyce mcintosh node->n_timestamps.t_crtime = attr->sa_crtime; 1529037cac00Sjoyce mcintosh } 1530037cac00Sjoyce mcintosh mutex_exit(&node->n_mutex); 1531037cac00Sjoyce mcintosh } 1532