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 "%Z%%M% %I% %E% 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 fs_desc_t fsd; 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) { 227 if (sr->tid_tree) { 228 /* 229 * The fsd for a file is that of the tree, even 230 * if the file resides in a different mountpoint 231 * under the share. 232 */ 233 fsd = sr->tid_tree->t_fsd; 234 } else { 235 /* 236 * This should be getting executed only for the 237 * tree's root smb_node. 238 */ 239 fsd = vp->v_vfsp->vfs_fsid; 240 } 241 } else { 242 fsd = vp->v_vfsp->vfs_fsid; 243 } 244 245 hashkey = fsd.val[0] + attr->sa_vattr.va_nodeid; 246 hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 247 node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]; 248 lock_mode = RW_READER; 249 250 smb_llist_enter(node_hdr, lock_mode); 251 for (;;) { 252 node = list_head(&node_hdr->ll_list); 253 while (node) { 254 ASSERT(node->n_magic == SMB_NODE_MAGIC); 255 ASSERT(node->n_hash_bucket == node_hdr); 256 if ((node->n_hashkey == hashkey) && (node->vp == vp)) { 257 smb_rwx_xenter(&node->n_lock); 258 DTRACE_PROBE1(smb_node_lookup_hit, 259 smb_node_t *, node); 260 switch (node->n_state) { 261 case SMB_NODE_STATE_AVAILABLE: 262 /* The node was found. */ 263 node->n_refcnt++; 264 if ((node->dir_snode == NULL) && 265 (dir_snode != NULL) && 266 (strcmp(od_name, "..") != 0) && 267 (strcmp(od_name, ".") != 0)) { 268 VALIDATE_DIR_NODE(dir_snode, 269 node); 270 node->dir_snode = dir_snode; 271 smb_node_ref(dir_snode); 272 } 273 node->attr = *attr; 274 node->n_size = attr->sa_vattr.va_size; 275 276 smb_audit_node(node); 277 smb_rwx_xexit(&node->n_lock); 278 smb_llist_exit(node_hdr); 279 VN_RELE(vp); 280 return (node); 281 282 case SMB_NODE_STATE_DESTROYING: 283 /* 284 * Although the node exists it is about 285 * to be destroyed. We act as it hasn't 286 * been found. 287 */ 288 smb_rwx_xexit(&node->n_lock); 289 break; 290 default: 291 /* 292 * Although the node exists it is in an 293 * unknown state. We act as it hasn't 294 * been found. 295 */ 296 ASSERT(0); 297 smb_rwx_xexit(&node->n_lock); 298 break; 299 } 300 } 301 node = smb_llist_next(node_hdr, node); 302 } 303 if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) { 304 lock_mode = RW_WRITER; 305 continue; 306 } 307 break; 308 } 309 node = kmem_cache_alloc(sr->sr_server->si_cache_node, KM_SLEEP); 310 bzero(node, sizeof (smb_node_t)); 311 312 node->n_state = SMB_NODE_STATE_AVAILABLE; 313 node->n_hash_bucket = node_hdr; 314 node->n_sr = sr; 315 node->vp = vp; 316 node->n_hashkey = hashkey; 317 node->n_refcnt = 1; 318 node->tree_fsd = vp->v_vfsp->vfs_fsid; 319 node->attr = *attr; 320 node->flags |= NODE_FLAGS_ATTR_VALID; 321 node->n_size = node->attr.sa_vattr.va_size; 322 node->n_orig_session_id = sr->session->s_kid; 323 node->n_orig_uid = crgetuid(sr->user_cr); 324 node->n_cache = sr->sr_server->si_cache_node; 325 326 ASSERT(od_name); 327 (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); 328 329 if (fsd_chkcap(&vp->v_vfsp->vfs_fsid, FSOLF_READONLY) > 0) 330 node->flags |= NODE_READ_ONLY; 331 332 smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 333 offsetof(smb_ofile_t, f_nnd)); 334 smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 335 offsetof(smb_lock_t, l_lnd)); 336 337 338 if (strcmp(od_name, XATTR_DIR) == 0) 339 node->flags |= NODE_XATTR_DIR; 340 if (op) 341 node->flags |= smb_is_executable(op->fqi.last_comp); 342 343 if (dir_snode) { 344 smb_node_ref(dir_snode); 345 node->dir_snode = dir_snode; 346 ASSERT(dir_snode->dir_snode != node); 347 ASSERT((dir_snode->vp->v_xattrdir) || 348 (dir_snode->vp->v_type == VDIR)); 349 } 350 351 if (unnamed_node) { 352 smb_node_ref(unnamed_node); 353 node->unnamed_stream_node = unnamed_node; 354 } 355 356 smb_rwx_init(&node->n_lock); 357 node->n_magic = SMB_NODE_MAGIC; 358 smb_audit_buf_node_create(node); 359 DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); 360 smb_audit_node(node); 361 smb_llist_insert_head(node_hdr, node); 362 363 smb_llist_exit(node_hdr); 364 return (node); 365 } 366 367 /* 368 * smb_stream_node_lookup() 369 * 370 * Note: stream_name (the name that will be stored in the "od_name" field 371 * of a stream's smb_node) is the same as the on-disk name for the stream 372 * except that it does not have SMB_STREAM_PREFIX prepended. 373 */ 374 375 smb_node_t * 376 smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, 377 vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr) 378 { 379 smb_node_t *xattrdir_node; 380 smb_node_t *snode; 381 smb_attr_t tmp_attr; 382 383 xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, 384 fnode, NULL, &tmp_attr); 385 386 if (xattrdir_node == NULL) 387 return (NULL); 388 389 snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, 390 fnode, ret_attr); 391 392 /* 393 * The following VN_HOLD is necessary because the caller will VN_RELE 394 * xattrdirvp in the case of an error. (xattrdir_node has the original 395 * hold on the vnode, which the smb_node_release() call below will 396 * release.) 397 */ 398 if (snode == NULL) { 399 VN_HOLD(xattrdirvp); 400 } 401 (void) smb_node_release(xattrdir_node); 402 return (snode); 403 } 404 405 406 /* 407 * This function should be called whenever a reference is needed on an 408 * smb_node pointer. The copy of an smb_node pointer from one non-local 409 * data structure to another requires a reference to be taken on the smb_node 410 * (unless the usage is localized). Each data structure deallocation routine 411 * will call smb_node_release() on its smb_node pointers. 412 * 413 * In general, an smb_node pointer residing in a structure should never be 414 * stale. A node pointer may be NULL, however, and care should be taken 415 * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. 416 * Care also needs to be taken with respect to racing deallocations of a 417 * structure. 418 */ 419 420 void 421 smb_node_ref(smb_node_t *node) 422 { 423 ASSERT(node); 424 ASSERT(node->n_magic == SMB_NODE_MAGIC); 425 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 426 427 smb_rwx_xenter(&node->n_lock); 428 node->n_refcnt++; 429 ASSERT(node->n_refcnt); 430 DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); 431 smb_audit_node(node); 432 smb_rwx_xexit(&node->n_lock); 433 } 434 435 /* 436 * smb_node_lookup() takes a hold on an smb_node, whether found in the 437 * hash table or newly created. This hold is expected to be released 438 * in the following manner. 439 * 440 * smb_node_lookup() takes an address of an smb_node pointer. This should 441 * be getting passed down via a lookup (whether path name or component), mkdir, 442 * create. If the original smb_node pointer resides in a data structure, then 443 * the deallocation routine for the data structure is responsible for calling 444 * smb_node_release() on the smb_node pointer. Alternatively, 445 * smb_node_release() can be called as soon as the smb_node pointer is no longer 446 * needed. In this case, callers are responsible for setting an embedded 447 * pointer to NULL if it is known that the last reference is being released. 448 * 449 * If the passed-in address of the smb_node pointer belongs to a local variable, 450 * then the caller with the local variable should call smb_node_release() 451 * directly. 452 * 453 * smb_node_release() itself will call smb_node_release() on a node's dir_snode, 454 * as smb_node_lookup() takes a hold on dir_snode. 455 */ 456 void 457 smb_node_release(smb_node_t *node) 458 { 459 ASSERT(node); 460 ASSERT(node->n_magic == SMB_NODE_MAGIC); 461 462 smb_rwx_xenter(&node->n_lock); 463 ASSERT(node->n_refcnt); 464 DTRACE_PROBE1(smb_node_release, smb_node_t *, node); 465 if (--node->n_refcnt == 0) { 466 switch (node->n_state) { 467 468 case SMB_NODE_STATE_AVAILABLE: 469 node->n_state = SMB_NODE_STATE_DESTROYING; 470 smb_rwx_xexit(&node->n_lock); 471 472 smb_llist_enter(node->n_hash_bucket, RW_WRITER); 473 smb_llist_remove(node->n_hash_bucket, node); 474 smb_llist_exit(node->n_hash_bucket); 475 476 /* 477 * Check if the file was deleted 478 */ 479 smb_node_delete_on_close(node); 480 node->n_magic = (uint32_t)~SMB_NODE_MAGIC; 481 482 /* These lists should be empty. */ 483 smb_llist_destructor(&node->n_ofile_list); 484 smb_llist_destructor(&node->n_lock_list); 485 486 if (node->dir_snode) { 487 ASSERT(node->dir_snode->n_magic == 488 SMB_NODE_MAGIC); 489 smb_node_release(node->dir_snode); 490 } 491 492 if (node->unnamed_stream_node) { 493 ASSERT(node->unnamed_stream_node->n_magic == 494 SMB_NODE_MAGIC); 495 smb_node_release(node->unnamed_stream_node); 496 } 497 498 ASSERT(node->vp); 499 VN_RELE(node->vp); 500 501 smb_audit_buf_node_destroy(node); 502 smb_rwx_destroy(&node->n_lock); 503 kmem_cache_free(node->n_cache, node); 504 return; 505 506 default: 507 ASSERT(0); 508 break; 509 } 510 } 511 smb_audit_node(node); 512 smb_rwx_xexit(&node->n_lock); 513 } 514 515 static void 516 smb_node_delete_on_close(smb_node_t *node) 517 { 518 smb_node_t *d_snode; 519 int rc = 0; 520 521 d_snode = node->dir_snode; 522 if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 523 524 node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 525 ASSERT(node->od_name != NULL); 526 if (node->attr.sa_vattr.va_type == VDIR) 527 rc = smb_fsop_rmdir(0, node->delete_on_close_cred, 528 d_snode, node->od_name, 1); 529 else 530 rc = smb_fsop_remove(0, node->delete_on_close_cred, 531 d_snode, node->od_name, 1); 532 smb_cred_rele(node->delete_on_close_cred); 533 } 534 if (rc != 0) 535 cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", 536 node->od_name, rc); 537 DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); 538 } 539 540 /* 541 * smb_node_rename() 542 * 543 */ 544 int 545 smb_node_rename( 546 smb_node_t *from_dir_snode, 547 smb_node_t *ret_snode, 548 smb_node_t *to_dir_snode, 549 char *to_name) 550 { 551 ASSERT(from_dir_snode); 552 ASSERT(to_dir_snode); 553 ASSERT(ret_snode); 554 ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); 555 ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); 556 ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC); 557 ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 558 ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 559 ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE); 560 561 smb_node_ref(to_dir_snode); 562 smb_rwx_xenter(&ret_snode->n_lock); 563 ret_snode->dir_snode = to_dir_snode; 564 smb_rwx_xexit(&ret_snode->n_lock); 565 ASSERT(to_dir_snode->dir_snode != ret_snode); 566 ASSERT((to_dir_snode->vp->v_xattrdir) || 567 (to_dir_snode->vp->v_type == VDIR)); 568 smb_node_release(from_dir_snode); 569 570 (void) strcpy(ret_snode->od_name, to_name); 571 572 /* 573 * XXX Need to update attributes? 574 */ 575 576 return (0); 577 } 578 579 int 580 smb_node_root_init(vnode_t *vp, smb_server_t *sv, smb_node_t **root) 581 { 582 smb_attr_t va; 583 int error; 584 uint32_t hashkey; 585 smb_llist_t *node_hdr; 586 smb_node_t *node; 587 588 /* 589 * Take an explicit hold on rootdir. This goes with the 590 * corresponding release in smb_node_root_fini()/smb_node_release(). 591 */ 592 VN_HOLD(vp); 593 594 va.sa_mask = SMB_AT_ALL; 595 error = smb_vop_getattr(vp, NULL, &va, 0, kcred); 596 if (error) { 597 VN_RELE(vp); 598 return (error); 599 } 600 601 hashkey = vp->v_vfsp->vfs_fsid.val[0] + va.sa_vattr.va_nodeid; 602 hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 603 node_hdr = &smb_node_hash_table[(hashkey & SMBND_HASH_MASK)]; 604 605 node = kmem_cache_alloc(sv->si_cache_node, KM_SLEEP); 606 bzero(node, sizeof (smb_node_t)); 607 608 node->n_state = SMB_NODE_STATE_AVAILABLE; 609 node->n_hash_bucket = node_hdr; 610 node->vp = vp; 611 node->n_hashkey = hashkey; 612 node->n_refcnt = 1; 613 node->tree_fsd = vp->v_vfsp->vfs_fsid; 614 node->attr = va; 615 node->flags |= NODE_FLAGS_ATTR_VALID; 616 node->n_size = node->attr.sa_vattr.va_size; 617 node->n_cache = sv->si_cache_node; 618 (void) strlcpy(node->od_name, ROOTVOL, sizeof (node->od_name)); 619 620 if (fsd_chkcap(&vp->v_vfsp->vfs_fsid, FSOLF_READONLY) > 0) 621 node->flags |= NODE_READ_ONLY; 622 623 smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 624 offsetof(smb_ofile_t, f_nnd)); 625 smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 626 offsetof(smb_lock_t, l_lnd)); 627 628 smb_rwx_init(&node->n_lock); 629 node->n_magic = SMB_NODE_MAGIC; 630 smb_audit_buf_node_create(node); 631 632 sv->si_root_smb_node = node; 633 634 smb_audit_node(node); 635 smb_llist_enter(node_hdr, RW_WRITER); 636 smb_llist_insert_head(node_hdr, node); 637 smb_llist_exit(node_hdr); 638 639 *root = node; 640 641 return (0); 642 } 643 644 /* 645 * smb_node_get_size 646 */ 647 u_offset_t 648 smb_node_get_size(smb_node_t *node, smb_attr_t *attr) 649 { 650 u_offset_t size; 651 652 if (attr->sa_vattr.va_type == VDIR) 653 return (0); 654 655 smb_rwx_xenter(&node->n_lock); 656 if (node && (node->flags & NODE_FLAGS_SET_SIZE)) 657 size = node->n_size; 658 else 659 size = attr->sa_vattr.va_size; 660 smb_rwx_xexit(&node->n_lock); 661 return (size); 662 } 663 664 static int 665 timeval_cmp(timestruc_t *a, timestruc_t *b) 666 { 667 if (a->tv_sec < b->tv_sec) 668 return (-1); 669 if (a->tv_sec > b->tv_sec) 670 return (1); 671 /* Seconds are equal compare tv_nsec */ 672 if (a->tv_nsec < b->tv_nsec) 673 return (-1); 674 return (a->tv_nsec > b->tv_nsec); 675 } 676 677 /* 678 * smb_node_set_time 679 * 680 * This function will update the time stored in the node and 681 * set the appropriate flags. If there is nothing to update 682 * or the node is readonly, the function would return without 683 * any updates. The update is only in the node level and the 684 * attribute in the file system will be updated when client 685 * close the file. 686 */ 687 void 688 smb_node_set_time(struct smb_node *node, struct timestruc *crtime, 689 struct timestruc *mtime, struct timestruc *atime, 690 struct timestruc *ctime, unsigned int what) 691 { 692 smb_rwx_xenter(&node->n_lock); 693 if (node->flags & NODE_READ_ONLY || what == 0) { 694 smb_rwx_xexit(&node->n_lock); 695 return; 696 } 697 698 if ((what & SMB_AT_CRTIME && crtime == 0) || 699 (what & SMB_AT_MTIME && mtime == 0) || 700 (what & SMB_AT_ATIME && atime == 0) || 701 (what & SMB_AT_CTIME && ctime == 0)) { 702 smb_rwx_xexit(&node->n_lock); 703 return; 704 } 705 706 if ((what & SMB_AT_CRTIME) && 707 timeval_cmp((timestruc_t *)&node->attr.sa_crtime, 708 crtime) != 0) { 709 node->what |= SMB_AT_CRTIME; 710 node->attr.sa_crtime = *((timestruc_t *)crtime); 711 } 712 713 if ((what & SMB_AT_MTIME) && 714 timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime, 715 mtime) != 0) { 716 node->what |= SMB_AT_MTIME; 717 node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime); 718 } 719 720 if ((what & SMB_AT_ATIME) && 721 timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime, 722 atime) != 0) { 723 node->what |= SMB_AT_ATIME; 724 node->attr.sa_vattr.va_atime = *((timestruc_t *)atime); 725 } 726 727 /* 728 * The ctime handling is trickier. It has three scenarios. 729 * 1. Only ctime need to be set and it is the same as the ctime 730 * stored in the node. (update not necessary) 731 * 2. The ctime is the same as the ctime stored in the node but 732 * is not the only time need to be set. (update required) 733 * 3. The ctime need to be set and is not the same as the ctime 734 * stored in the node. (update required) 735 * Unlike other time setting, the ctime needs to be set even when 736 * it is the same as the ctime in the node if there are other time 737 * needs to be set (#2). This will ensure the ctime not being 738 * updated when other times are being updated in the file system. 739 * 740 * Retained file rules: 741 * 742 * 1. Don't add SMB_AT_CTIME to node->what by default because the 743 * request will be rejected by filesystem 744 * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e. 745 * any request for changing ctime on these files should have 746 * been already rejected 747 */ 748 node->what |= SMB_AT_CTIME; 749 if (what & SMB_AT_CTIME) { 750 if ((what == SMB_AT_CTIME) && 751 timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime, 752 ctime) == 0) { 753 node->what &= ~SMB_AT_CTIME; 754 } else { 755 gethrestime(&node->attr.sa_vattr.va_ctime); 756 } 757 } else { 758 gethrestime(&node->attr.sa_vattr.va_ctime); 759 } 760 smb_rwx_xexit(&node->n_lock); 761 } 762 763 764 timestruc_t * 765 smb_node_get_crtime(smb_node_t *node) 766 { 767 return ((timestruc_t *)&node->attr.sa_crtime); 768 } 769 770 timestruc_t * 771 smb_node_get_atime(smb_node_t *node) 772 { 773 return ((timestruc_t *)&node->attr.sa_vattr.va_atime); 774 } 775 776 timestruc_t * 777 smb_node_get_ctime(smb_node_t *node) 778 { 779 return ((timestruc_t *)&node->attr.sa_vattr.va_ctime); 780 } 781 782 timestruc_t * 783 smb_node_get_mtime(smb_node_t *node) 784 { 785 return ((timestruc_t *)&node->attr.sa_vattr.va_mtime); 786 } 787 788 /* 789 * smb_node_set_dosattr 790 * 791 * Parse the specified DOS attributes and, if they have been modified, 792 * update the node cache. This call should be followed by a 793 * smb_sync_fsattr() call to write the attribute changes to filesystem. 794 */ 795 void 796 smb_node_set_dosattr(smb_node_t *node, uint32_t dosattr) 797 { 798 uint32_t mode = dosattr & (FILE_ATTRIBUTE_ARCHIVE | 799 FILE_ATTRIBUTE_READONLY | 800 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); 801 802 smb_rwx_xenter(&node->n_lock); 803 if (node->attr.sa_dosattr != mode) { 804 node->attr.sa_dosattr = mode; 805 node->what |= SMB_AT_DOSATTR; 806 } 807 smb_rwx_xexit(&node->n_lock); 808 } 809 810 /* 811 * smb_node_get_dosattr 812 * 813 * This function will get dos attribute using the node. 814 */ 815 uint32_t 816 smb_node_get_dosattr(smb_node_t *node) 817 { 818 return (smb_mode_to_dos_attributes(&node->attr)); 819 } 820 821 int 822 smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr) 823 { 824 int rc = -1; 825 826 smb_rwx_xenter(&node->n_lock); 827 if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) && 828 !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) { 829 crhold(cr); 830 node->delete_on_close_cred = cr; 831 node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; 832 rc = 0; 833 } 834 smb_rwx_xexit(&node->n_lock); 835 return (rc); 836 } 837 838 void 839 smb_node_reset_delete_on_close(smb_node_t *node) 840 { 841 smb_rwx_xenter(&node->n_lock); 842 if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 843 node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 844 crfree(node->delete_on_close_cred); 845 node->delete_on_close_cred = NULL; 846 } 847 smb_rwx_xexit(&node->n_lock); 848 } 849 850 /* 851 * smb_node_open_check 852 * 853 * check file sharing rules for current open request 854 * against all existing opens for a file. 855 * 856 * Returns NT_STATUS_SHARING_VIOLATION if there is any 857 * sharing conflict, otherwise returns NT_STATUS_SUCCESS. 858 */ 859 uint32_t 860 smb_node_open_check(struct smb_node *node, cred_t *cr, 861 uint32_t desired_access, uint32_t share_access) 862 { 863 smb_ofile_t *of; 864 uint32_t status; 865 866 ASSERT(node); 867 ASSERT(node->n_magic == SMB_NODE_MAGIC); 868 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 869 870 smb_llist_enter(&node->n_ofile_list, RW_READER); 871 of = smb_llist_head(&node->n_ofile_list); 872 while (of) { 873 status = smb_ofile_open_check(of, cr, desired_access, 874 share_access); 875 876 switch (status) { 877 case NT_STATUS_INVALID_HANDLE: 878 case NT_STATUS_SUCCESS: 879 of = smb_llist_next(&node->n_ofile_list, of); 880 break; 881 default: 882 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 883 smb_llist_exit(&node->n_ofile_list); 884 return (status); 885 } 886 } 887 888 smb_llist_exit(&node->n_ofile_list); 889 return (NT_STATUS_SUCCESS); 890 } 891 892 uint32_t 893 smb_node_rename_check(struct smb_node *node) 894 { 895 struct smb_ofile *of; 896 uint32_t status; 897 898 ASSERT(node); 899 ASSERT(node->n_magic == SMB_NODE_MAGIC); 900 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 901 902 /* 903 * Intra-CIFS check 904 */ 905 906 smb_llist_enter(&node->n_ofile_list, RW_READER); 907 of = smb_llist_head(&node->n_ofile_list); 908 while (of) { 909 status = smb_ofile_rename_check(of); 910 911 switch (status) { 912 case NT_STATUS_INVALID_HANDLE: 913 case NT_STATUS_SUCCESS: 914 of = smb_llist_next(&node->n_ofile_list, of); 915 break; 916 default: 917 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 918 smb_llist_exit(&node->n_ofile_list); 919 return (status); 920 } 921 } 922 smb_llist_exit(&node->n_ofile_list); 923 924 /* 925 * system-wide share check 926 */ 927 928 if (nbl_share_conflict(node->vp, NBL_RENAME, NULL)) 929 return (NT_STATUS_SHARING_VIOLATION); 930 else 931 return (NT_STATUS_SUCCESS); 932 } 933 934 uint32_t 935 smb_node_delete_check(smb_node_t *node) 936 { 937 smb_ofile_t *of; 938 uint32_t status; 939 940 ASSERT(node); 941 ASSERT(node->n_magic == SMB_NODE_MAGIC); 942 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 943 944 if (node->attr.sa_vattr.va_type == VDIR) 945 return (NT_STATUS_SUCCESS); 946 947 /* 948 * intra-CIFS check 949 */ 950 951 smb_llist_enter(&node->n_ofile_list, RW_READER); 952 of = smb_llist_head(&node->n_ofile_list); 953 while (of) { 954 status = smb_ofile_delete_check(of); 955 956 switch (status) { 957 case NT_STATUS_INVALID_HANDLE: 958 case NT_STATUS_SUCCESS: 959 of = smb_llist_next(&node->n_ofile_list, of); 960 break; 961 default: 962 ASSERT(status == NT_STATUS_SHARING_VIOLATION); 963 smb_llist_exit(&node->n_ofile_list); 964 return (status); 965 } 966 } 967 smb_llist_exit(&node->n_ofile_list); 968 969 /* 970 * system-wide share check 971 */ 972 973 if (nbl_share_conflict(node->vp, NBL_REMOVE, NULL)) 974 return (NT_STATUS_SHARING_VIOLATION); 975 else 976 return (NT_STATUS_SUCCESS); 977 } 978 979 /* 980 * smb_node_start_crit() 981 * 982 * Enter critical region for share reservations. 983 * See comments above smb_fsop_shrlock(). 984 */ 985 986 void 987 smb_node_start_crit(smb_node_t *node, krw_t mode) 988 { 989 rw_enter(&node->n_share_lock, mode); 990 nbl_start_crit(node->vp, mode); 991 } 992 993 /* 994 * smb_node_end_crit() 995 * 996 * Exit critical region for share reservations. 997 */ 998 999 void 1000 smb_node_end_crit(smb_node_t *node) 1001 { 1002 nbl_end_crit(node->vp); 1003 rw_exit(&node->n_share_lock); 1004 } 1005 1006 int 1007 smb_node_in_crit(smb_node_t *node) 1008 { 1009 return (nbl_in_crit(node->vp) && RW_LOCK_HELD(&node->n_share_lock)); 1010 } 1011