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 2007 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 88 uint32_t smb_is_executable(char *path); 89 static void smb_node_delete_on_close(smb_node_t *node); 90 91 uint32_t smb_node_hit = 0; 92 uint32_t smb_node_miss = 0; 93 uint32_t smb_node_alloc = 0; 94 uint32_t smb_node_free = 0; 95 96 #define VALIDATE_DIR_NODE(_dir_, _node_) \ 97 ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \ 98 ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \ 99 ASSERT((_dir_)->dir_snode != (_node_)); 100 101 /* 102 * smb_node_lookup() 103 * 104 * NOTE: This routine should only be called by the file system interface layer, 105 * and not by SMB. 106 * 107 * smb_node_lookup() is called upon successful lookup, mkdir, and create 108 * (for both non-streams and streams). In each of these cases, a held vnode is 109 * passed into this routine. If an smb_node already exists for this vnode, 110 * the vp is released. Otherwise, a new smb_node will be created and the 111 * reference will be held until the refcnt on the node goes to 0 (see 112 * smb_node_release()). 113 * 114 * A reference is taken on the smb_node whether found in the hash table 115 * or newly created. 116 * 117 * If an smb_node needs to be created, a reference is also taken on the 118 * dir_snode (if passed in). 119 * 120 * See smb_node_release() for details on the release of these references. 121 */ 122 123 /*ARGSUSED*/ 124 smb_node_t * 125 smb_node_lookup( 126 struct smb_request *sr, 127 struct open_param *op, 128 cred_t *cred, 129 vnode_t *vp, 130 char *od_name, 131 smb_node_t *dir_snode, 132 smb_node_t *unnamed_node, 133 smb_attr_t *attr) 134 { 135 smb_llist_t *node_hdr; 136 smb_node_t *node; 137 uint32_t hashkey = 0; 138 fs_desc_t fsd; 139 int error; 140 krw_t lock_mode; 141 caller_context_t ct; 142 vnode_t *unnamed_vp = NULL; 143 144 /* 145 * smb_vop_getattr() is called here instead of smb_fsop_getattr(), 146 * because the node may not yet exist. We also do not want to call 147 * it with the list lock held. 148 */ 149 150 if (unnamed_node) 151 unnamed_vp = unnamed_node->vp; 152 153 /* 154 * This getattr is performed on behalf of the server 155 * that's why kcred is used not the user's cred 156 */ 157 smb_get_caller_context(sr, &ct); 158 attr->sa_mask = SMB_AT_ALL; 159 error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred, &ct); 160 if (error) 161 return (NULL); 162 163 if (sr) { 164 if (sr->tid_tree) { 165 /* 166 * The fsd for a file is that of the tree, even 167 * if the file resides in a different mountpoint 168 * under the share. 169 */ 170 fsd = sr->tid_tree->t_fsd; 171 } else { 172 /* 173 * This should be getting executed only for the 174 * tree's root smb_node. 175 */ 176 fsd = vp->v_vfsp->vfs_fsid; 177 } 178 } else { 179 fsd = vp->v_vfsp->vfs_fsid; 180 } 181 182 hashkey = fsd.val[0] + attr->sa_vattr.va_nodeid; 183 hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); 184 node_hdr = &smb_info.node_hash_table[(hashkey & SMBND_HASH_MASK)]; 185 lock_mode = RW_READER; 186 187 smb_llist_enter(node_hdr, lock_mode); 188 for (;;) { 189 node = list_head(&node_hdr->ll_list); 190 while (node) { 191 ASSERT(node->n_magic == SMB_NODE_MAGIC); 192 ASSERT(node->n_hash_bucket == node_hdr); 193 if ((node->n_hashkey == hashkey) && (node->vp == vp)) { 194 smb_rwx_xenter(&node->n_lock); 195 DTRACE_PROBE1(smb_node_lookup_hit, 196 smb_node_t *, node); 197 switch (node->n_state) { 198 case SMB_NODE_STATE_AVAILABLE: 199 /* The node was found. */ 200 node->n_refcnt++; 201 if ((node->dir_snode == NULL) && 202 (dir_snode != NULL) && 203 (strcmp(od_name, "..") != 0) && 204 (strcmp(od_name, ".") != 0)) { 205 VALIDATE_DIR_NODE(dir_snode, 206 node); 207 node->dir_snode = dir_snode; 208 smb_node_ref(dir_snode); 209 } 210 node->attr = *attr; 211 212 smb_audit_node(node); 213 smb_rwx_xexit(&node->n_lock); 214 smb_llist_exit(node_hdr); 215 VN_RELE(vp); 216 return (node); 217 218 case SMB_NODE_STATE_DESTROYING: 219 /* 220 * Although the node exists it is about 221 * to be destroyed. We act as it hasn't 222 * been found. 223 */ 224 smb_rwx_xexit(&node->n_lock); 225 break; 226 default: 227 /* 228 * Although the node exists it is in an 229 * unknown state. We act as it hasn't 230 * been found. 231 */ 232 ASSERT(0); 233 smb_rwx_xexit(&node->n_lock); 234 break; 235 } 236 } 237 node = smb_llist_next(node_hdr, node); 238 } 239 if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) { 240 lock_mode = RW_WRITER; 241 continue; 242 } 243 break; 244 } 245 node = kmem_cache_alloc(smb_info.si_cache_node, KM_SLEEP); 246 smb_node_alloc++; 247 248 bzero(node, sizeof (smb_node_t)); 249 250 node->n_state = SMB_NODE_STATE_AVAILABLE; 251 node->n_hash_bucket = node_hdr; 252 253 if (fsd_chkcap(&fsd, FSOLF_READONLY) > 0) { 254 node->flags |= NODE_READ_ONLY; 255 } 256 257 smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), 258 offsetof(smb_ofile_t, f_nnd)); 259 smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), 260 offsetof(smb_lock_t, l_lnd)); 261 node->n_hashkey = hashkey; 262 node->n_refcnt = 1; 263 264 if (sr) { 265 node->n_orig_session_id = sr->session->s_kid; 266 node->n_orig_uid = crgetuid(sr->user_cr); 267 } 268 269 node->vp = vp; 270 271 ASSERT(od_name); 272 (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); 273 node->tree_fsd = fsd; 274 275 if (op) 276 node->flags |= smb_is_executable(op->fqi.last_comp); 277 278 if (dir_snode) { 279 smb_node_ref(dir_snode); 280 node->dir_snode = dir_snode; 281 ASSERT(dir_snode->dir_snode != node); 282 ASSERT((dir_snode->vp->v_xattrdir) || 283 (dir_snode->vp->v_type == VDIR)); 284 } 285 286 if (unnamed_node) { 287 smb_node_ref(unnamed_node); 288 node->unnamed_stream_node = unnamed_node; 289 } 290 291 node->attr = *attr; 292 node->flags |= NODE_FLAGS_ATTR_VALID; 293 node->n_size = node->attr.sa_vattr.va_size; 294 295 smb_rwx_init(&node->n_lock); 296 node->n_magic = SMB_NODE_MAGIC; 297 smb_audit_buf_node_create(node); 298 299 DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); 300 smb_audit_node(node); 301 smb_llist_insert_head(node_hdr, node); 302 smb_llist_exit(node_hdr); 303 return (node); 304 } 305 306 /* 307 * smb_stream_node_lookup() 308 * 309 * Note: stream_name (the name that will be stored in the "od_name" field 310 * of a stream's smb_node) is the same as the on-disk name for the stream 311 * except that it does not have SMB_STREAM_PREFIX prepended. 312 */ 313 314 smb_node_t * 315 smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, 316 vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr) 317 { 318 smb_node_t *xattrdir_node; 319 smb_node_t *snode; 320 smb_attr_t tmp_attr; 321 322 xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, 323 fnode, NULL, &tmp_attr); 324 325 if (xattrdir_node == NULL) 326 return (NULL); 327 328 snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, 329 fnode, ret_attr); 330 331 /* 332 * The following VN_HOLD is necessary because the caller will VN_RELE 333 * xattrdirvp in the case of an error. (xattrdir_node has the original 334 * hold on the vnode, which the smb_node_release() call below will 335 * release.) 336 */ 337 if (snode == NULL) { 338 VN_HOLD(xattrdirvp); 339 } 340 (void) smb_node_release(xattrdir_node); 341 return (snode); 342 } 343 344 345 /* 346 * This function should be called whenever a reference is needed on an 347 * smb_node pointer. The copy of an smb_node pointer from one non-local 348 * data structure to another requires a reference to be taken on the smb_node 349 * (unless the usage is localized). Each data structure deallocation routine 350 * will call smb_node_release() on its smb_node pointers. 351 * 352 * In general, an smb_node pointer residing in a structure should never be 353 * stale. A node pointer may be NULL, however, and care should be taken 354 * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. 355 * Care also needs to be taken with respect to racing deallocations of a 356 * structure. 357 */ 358 359 void 360 smb_node_ref(smb_node_t *node) 361 { 362 ASSERT(node); 363 ASSERT(node->n_magic == SMB_NODE_MAGIC); 364 ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 365 366 smb_rwx_xenter(&node->n_lock); 367 node->n_refcnt++; 368 ASSERT(node->n_refcnt); 369 DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); 370 smb_audit_node(node); 371 smb_rwx_xexit(&node->n_lock); 372 } 373 374 /* 375 * smb_node_lookup() takes a hold on an smb_node, whether found in the 376 * hash table or newly created. This hold is expected to be released 377 * in the following manner. 378 * 379 * smb_node_lookup() takes an address of an smb_node pointer. This should 380 * be getting passed down via a lookup (whether path name or component), mkdir, 381 * create. If the original smb_node pointer resides in a data structure, then 382 * the deallocation routine for the data structure is responsible for calling 383 * smb_node_release() on the smb_node pointer. Alternatively, 384 * smb_node_release() can be called as soon as the smb_node pointer is no longer 385 * needed. In this case, callers are responsible for setting an embedded 386 * pointer to NULL if it is known that the last reference is being released. 387 * 388 * If the passed-in address of the smb_node pointer belongs to a local variable, 389 * then the caller with the local variable should call smb_node_release() 390 * directly. 391 * 392 * smb_node_release() itself will call smb_node_release() on a node's dir_snode, 393 * as smb_node_lookup() takes a hold on dir_snode. 394 */ 395 396 void 397 smb_node_release(smb_node_t *node) 398 { 399 ASSERT(node); 400 ASSERT(node->n_magic == SMB_NODE_MAGIC); 401 402 smb_rwx_xenter(&node->n_lock); 403 ASSERT(node->n_refcnt); 404 DTRACE_PROBE1(smb_node_release, smb_node_t *, node); 405 if (--node->n_refcnt == 0) { 406 switch (node->n_state) { 407 408 case SMB_NODE_STATE_AVAILABLE: 409 node->n_state = SMB_NODE_STATE_DESTROYING; 410 smb_rwx_xexit(&node->n_lock); 411 412 smb_llist_enter(node->n_hash_bucket, RW_WRITER); 413 smb_llist_remove(node->n_hash_bucket, node); 414 smb_llist_exit(node->n_hash_bucket); 415 416 /* 417 * Check if the file was deleted 418 */ 419 smb_node_delete_on_close(node); 420 node->n_magic = (uint32_t)~SMB_NODE_MAGIC; 421 422 /* These lists should be empty. */ 423 smb_llist_destructor(&node->n_ofile_list); 424 smb_llist_destructor(&node->n_lock_list); 425 426 if (node->dir_snode) { 427 ASSERT(node->dir_snode->n_magic == 428 SMB_NODE_MAGIC); 429 smb_node_release(node->dir_snode); 430 } 431 432 if (node->unnamed_stream_node) { 433 ASSERT(node->unnamed_stream_node->n_magic == 434 SMB_NODE_MAGIC); 435 smb_node_release(node->unnamed_stream_node); 436 } 437 438 ASSERT(node->vp); 439 VN_RELE(node->vp); 440 441 smb_audit_buf_node_destroy(node); 442 smb_rwx_destroy(&node->n_lock); 443 kmem_cache_free(smb_info.si_cache_node, node); 444 ++smb_node_free; 445 return; 446 447 default: 448 ASSERT(0); 449 break; 450 } 451 } 452 smb_audit_node(node); 453 smb_rwx_xexit(&node->n_lock); 454 } 455 456 static void 457 smb_node_delete_on_close(smb_node_t *node) 458 { 459 smb_node_t *d_snode; 460 int rc = 0; 461 462 d_snode = node->dir_snode; 463 if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 464 465 node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 466 ASSERT(node->od_name != NULL); 467 if (node->attr.sa_vattr.va_type == VDIR) 468 rc = smb_fsop_rmdir(0, node->delete_on_close_cred, 469 d_snode, node->od_name, 1); 470 else 471 rc = smb_fsop_remove(0, node->delete_on_close_cred, 472 d_snode, node->od_name, 1); 473 smb_cred_rele(node->delete_on_close_cred); 474 } 475 if (rc != 0) 476 cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", 477 node->od_name, rc); 478 DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); 479 } 480 481 /* 482 * smb_node_rename() 483 * 484 */ 485 int 486 smb_node_rename( 487 smb_node_t *from_dir_snode, 488 smb_node_t *ret_snode, 489 smb_node_t *to_dir_snode, 490 char *to_name) 491 { 492 ASSERT(from_dir_snode); 493 ASSERT(to_dir_snode); 494 ASSERT(ret_snode); 495 ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); 496 ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); 497 ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC); 498 ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 499 ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); 500 ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE); 501 502 smb_node_ref(to_dir_snode); 503 smb_rwx_xenter(&ret_snode->n_lock); 504 ret_snode->dir_snode = to_dir_snode; 505 smb_rwx_xexit(&ret_snode->n_lock); 506 ASSERT(to_dir_snode->dir_snode != ret_snode); 507 ASSERT((to_dir_snode->vp->v_xattrdir) || 508 (to_dir_snode->vp->v_type == VDIR)); 509 smb_node_release(from_dir_snode); 510 511 (void) strcpy(ret_snode->od_name, to_name); 512 513 /* 514 * XXX Need to update attributes? 515 */ 516 517 return (0); 518 } 519 520 int 521 smb_node_root_init() 522 { 523 smb_attr_t va; 524 525 /* 526 * Take an explicit hold on rootdir. This goes with the 527 * corresponding release in smb_node_root_fini()/smb_node_release(). 528 */ 529 530 VN_HOLD(rootdir); 531 532 if ((smb_info.si_root_smb_node = smb_node_lookup(NULL, NULL, kcred, 533 rootdir, ROOTVOL, NULL, NULL, &va)) == NULL) 534 return (-1); 535 else 536 return (0); 537 } 538 539 void 540 smb_node_root_fini() 541 { 542 if (smb_info.si_root_smb_node) { 543 smb_node_release(smb_info.si_root_smb_node); 544 smb_info.si_root_smb_node = NULL; 545 } 546 } 547 548 /* 549 * smb_node_get_size 550 */ 551 uint64_t 552 smb_node_get_size( 553 smb_node_t *node, 554 smb_attr_t *attr) 555 { 556 uint64_t size; 557 558 if (attr->sa_vattr.va_type == VDIR) 559 return (0); 560 561 smb_rwx_xenter(&node->n_lock); 562 if (node && (node->flags & NODE_FLAGS_SET_SIZE)) 563 size = node->n_size; 564 else 565 size = attr->sa_vattr.va_size; 566 smb_rwx_xexit(&node->n_lock); 567 return (size); 568 } 569 570 static int 571 timeval_cmp(timestruc_t *a, timestruc_t *b) 572 { 573 if (a->tv_sec < b->tv_sec) 574 return (-1); 575 if (a->tv_sec > b->tv_sec) 576 return (1); 577 /* Seconds are equal compare tv_nsec */ 578 if (a->tv_nsec < b->tv_nsec) 579 return (-1); 580 return (a->tv_nsec > b->tv_nsec); 581 } 582 583 /* 584 * smb_node_set_time 585 * 586 * This function will update the time stored in the node and 587 * set the appropriate flags. If there is nothing to update 588 * or the node is readonly, the function would return without 589 * any updates. The update is only in the node level and the 590 * attribute in the file system will be updated when client 591 * close the file. 592 */ 593 void 594 smb_node_set_time(struct smb_node *node, struct timestruc *crtime, 595 struct timestruc *mtime, struct timestruc *atime, 596 struct timestruc *ctime, unsigned int what) 597 { 598 smb_rwx_xenter(&node->n_lock); 599 if (node->flags & NODE_READ_ONLY || what == 0) { 600 smb_rwx_xexit(&node->n_lock); 601 return; 602 } 603 604 if ((what & SMB_AT_CRTIME && crtime == 0) || 605 (what & SMB_AT_MTIME && mtime == 0) || 606 (what & SMB_AT_ATIME && atime == 0) || 607 (what & SMB_AT_CTIME && ctime == 0)) { 608 smb_rwx_xexit(&node->n_lock); 609 return; 610 } 611 612 if ((what & SMB_AT_CRTIME) && 613 timeval_cmp((timestruc_t *)&node->attr.sa_crtime, 614 crtime) != 0) { 615 node->what |= SMB_AT_CRTIME; 616 node->attr.sa_crtime = *((timestruc_t *)crtime); 617 } 618 619 if ((what & SMB_AT_MTIME) && 620 timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime, 621 mtime) != 0) { 622 node->what |= SMB_AT_MTIME; 623 node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime); 624 } 625 626 if ((what & SMB_AT_ATIME) && 627 timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime, 628 atime) != 0) { 629 node->what |= SMB_AT_ATIME; 630 node->attr.sa_vattr.va_atime = *((timestruc_t *)atime); 631 } 632 633 /* 634 * The ctime handling is trickier. It has three scenarios. 635 * 1. Only ctime need to be set and it is the same as the ctime 636 * stored in the node. (update not necessary) 637 * 2. The ctime is the same as the ctime stored in the node but 638 * is not the only time need to be set. (update required) 639 * 3. The ctime need to be set and is not the same as the ctime 640 * stored in the node. (update required) 641 * Unlike other time setting, the ctime needs to be set even when 642 * it is the same as the ctime in the node if there are other time 643 * needs to be set (#2). This will ensure the ctime not being 644 * updated when other times are being updated in the file system. 645 * 646 * Retained file rules: 647 * 648 * 1. Don't add SMB_AT_CTIME to node->what by default because the 649 * request will be rejected by filesystem 650 * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e. 651 * any request for changing ctime on these files should have 652 * been already rejected 653 */ 654 node->what |= SMB_AT_CTIME; 655 if (what & SMB_AT_CTIME) { 656 if ((what == SMB_AT_CTIME) && 657 timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime, 658 ctime) == 0) { 659 node->what &= ~SMB_AT_CTIME; 660 } else { 661 gethrestime(&node->attr.sa_vattr.va_ctime); 662 } 663 } else { 664 gethrestime(&node->attr.sa_vattr.va_ctime); 665 } 666 smb_rwx_xexit(&node->n_lock); 667 } 668 669 670 timestruc_t * 671 smb_node_get_crtime(smb_node_t *node) 672 { 673 return ((timestruc_t *)&node->attr.sa_crtime); 674 } 675 676 timestruc_t * 677 smb_node_get_atime(smb_node_t *node) 678 { 679 return ((timestruc_t *)&node->attr.sa_vattr.va_atime); 680 } 681 682 timestruc_t * 683 smb_node_get_ctime(smb_node_t *node) 684 { 685 return ((timestruc_t *)&node->attr.sa_vattr.va_ctime); 686 } 687 688 timestruc_t * 689 smb_node_get_mtime(smb_node_t *node) 690 { 691 return ((timestruc_t *)&node->attr.sa_vattr.va_mtime); 692 } 693 694 /* 695 * smb_node_set_dosattr 696 * 697 * Parse the specified DOS attributes and, if they have been modified, 698 * update the node cache. This call should be followed by a 699 * smb_sync_fsattr() call to write the attribute changes to filesystem. 700 */ 701 void 702 smb_node_set_dosattr(smb_node_t *node, uint32_t dos_attr) 703 { 704 unsigned int mode; /* New mode */ 705 706 mode = 0; 707 708 /* Handle the archive bit */ 709 if (dos_attr & SMB_FA_ARCHIVE) 710 mode |= FILE_ATTRIBUTE_ARCHIVE; 711 712 /* Handle the readonly bit */ 713 if (dos_attr & SMB_FA_READONLY) 714 mode |= FILE_ATTRIBUTE_READONLY; 715 716 /* Handle the hidden bit */ 717 if (dos_attr & SMB_FA_HIDDEN) 718 mode |= FILE_ATTRIBUTE_HIDDEN; 719 720 /* Handle the system bit */ 721 if (dos_attr & SMB_FA_SYSTEM) 722 mode |= FILE_ATTRIBUTE_SYSTEM; 723 724 smb_rwx_xenter(&node->n_lock); 725 if (node->attr.sa_dosattr != mode) { 726 node->attr.sa_dosattr = mode; 727 node->what |= SMB_AT_DOSATTR; 728 } 729 smb_rwx_xexit(&node->n_lock); 730 } 731 732 /* 733 * smb_node_get_dosattr 734 * 735 * This function will get dos attribute using the node. 736 */ 737 uint32_t 738 smb_node_get_dosattr(smb_node_t *node) 739 { 740 return (smb_mode_to_dos_attributes(&node->attr)); 741 } 742 743 int 744 smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr) 745 { 746 int rc = -1; 747 748 smb_rwx_xenter(&node->n_lock); 749 if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) && 750 !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) { 751 crhold(cr); 752 node->delete_on_close_cred = cr; 753 node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; 754 rc = 0; 755 } 756 smb_rwx_xexit(&node->n_lock); 757 return (rc); 758 } 759 760 void 761 smb_node_reset_delete_on_close(smb_node_t *node) 762 { 763 smb_rwx_xenter(&node->n_lock); 764 if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 765 node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; 766 crfree(node->delete_on_close_cred); 767 node->delete_on_close_cred = NULL; 768 } 769 smb_rwx_xexit(&node->n_lock); 770 } 771