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