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 * General Structures Layout 30 * ------------------------- 31 * 32 * This is a simplified diagram showing the relationship between most of the 33 * main structures. 34 * 35 * +-------------------+ 36 * | SMB_INFO | 37 * +-------------------+ 38 * | 39 * | 40 * v 41 * +-------------------+ +-------------------+ +-------------------+ 42 * | SESSION |<----->| SESSION |......| SESSION | 43 * +-------------------+ +-------------------+ +-------------------+ 44 * | 45 * | 46 * v 47 * +-------------------+ +-------------------+ +-------------------+ 48 * | USER |<----->| USER |......| USER | 49 * +-------------------+ +-------------------+ +-------------------+ 50 * | 51 * | 52 * v 53 * +-------------------+ +-------------------+ +-------------------+ 54 * | TREE |<----->| TREE |......| TREE | 55 * +-------------------+ +-------------------+ +-------------------+ 56 * | | 57 * | | 58 * | v 59 * | +-------+ +-------+ +-------+ 60 * | | OFILE |<----->| OFILE |......| OFILE | 61 * | +-------+ +-------+ +-------+ 62 * | 63 * | 64 * v 65 * +-------+ +------+ +------+ 66 * | ODIR |<----->| ODIR |......| ODIR | 67 * +-------+ +------+ +------+ 68 * 69 * 70 * Ofile State Machine 71 * ------------------ 72 * 73 * +-------------------------+ T0 74 * | SMB_OFILE_STATE_OPEN |<----------- Creation/Allocation 75 * +-------------------------+ 76 * | 77 * | T1 78 * | 79 * v 80 * +-------------------------+ 81 * | SMB_OFILE_STATE_CLOSING | 82 * +-------------------------+ 83 * | 84 * | T2 85 * | 86 * v 87 * +-------------------------+ T3 88 * | SMB_OFILE_STATE_CLOSED |----------> Deletion/Free 89 * +-------------------------+ 90 * 91 * SMB_OFILE_STATE_OPEN 92 * 93 * While in this state: 94 * - The ofile is queued in the list of ofiles of its tree. 95 * - References will be given out if the ofile is looked up. 96 * 97 * SMB_OFILE_STATE_CLOSING 98 * 99 * While in this state: 100 * - The ofile is queued in the list of ofiles of its tree. 101 * - References will not be given out if the ofile is looked up. 102 * - The file is closed and the locks held are being released. 103 * - The resources associated with the ofile remain. 104 * 105 * SMB_OFILE_STATE_CLOSED 106 * 107 * While in this state: 108 * - The ofile is queued in the list of ofiles of its tree. 109 * - References will not be given out if the ofile is looked up. 110 * - The resources associated with the ofile remain. 111 * 112 * Transition T0 113 * 114 * This transition occurs in smb_ofile_open(). A new ofile is created and 115 * added to the list of ofiles of a tree. 116 * 117 * Transition T1 118 * 119 * This transition occurs in smb_ofile_close(). 120 * 121 * Transition T2 122 * 123 * This transition occurs in smb_ofile_release(). The resources associated 124 * with the ofile are freed as well as the ofile structure. For the 125 * transition to occur, the ofile must be in the SMB_OFILE_STATE_CLOSED 126 * state and the reference count be zero. 127 * 128 * Comments 129 * -------- 130 * 131 * The state machine of the ofile structures is controlled by 3 elements: 132 * - The list of ofiles of the tree it belongs to. 133 * - The mutex embedded in the structure itself. 134 * - The reference count. 135 * 136 * There's a mutex embedded in the ofile structure used to protect its fields 137 * and there's a lock embedded in the list of ofiles of a tree. To 138 * increment or to decrement the reference count the mutex must be entered. 139 * To insert the ofile into the list of ofiles of the tree and to remove 140 * the ofile from it, the lock must be entered in RW_WRITER mode. 141 * 142 * Rules of access to a ofile structure: 143 * 144 * 1) In order to avoid deadlocks, when both (mutex and lock of the ofile 145 * list) have to be entered, the lock must be entered first. 146 * 147 * 2) All actions applied to an ofile require a reference count. 148 * 149 * 3) There are 2 ways of getting a reference count. One is when the ofile 150 * is opened. The other one when the ofile is looked up. This translates 151 * into 2 functions: smb_ofile_open() and smb_ofile_lookup_by_fid(). 152 * 153 * It should be noted that the reference count of an ofile registers the 154 * number of references to the ofile in other structures (such as an smb 155 * request). The reference count is not incremented in these 2 instances: 156 * 157 * 1) The ofile is open. An ofile is anchored by his state. If there's 158 * no activity involving an ofile currently open, the reference count 159 * of that ofile is zero. 160 * 161 * 2) The ofile is queued in the list of ofiles of its tree. The fact of 162 * being queued in that list is NOT registered by incrementing the 163 * reference count. 164 */ 165 #include <smbsrv/smb_incl.h> 166 #include <smbsrv/smb_kproto.h> 167 #include <smbsrv/smb_fsops.h> 168 169 /* Static functions defined further down this file. */ 170 static void smb_ofile_delete(smb_ofile_t *of); 171 static smb_ofile_t *smb_ofile_close_and_next(smb_ofile_t *of); 172 173 /* 174 * smb_ofile_open 175 * 176 * 177 */ 178 smb_ofile_t * 179 smb_ofile_open( 180 smb_tree_t *tree, 181 smb_node_t *node, 182 uint16_t pid, 183 uint32_t access_granted, 184 uint32_t create_options, 185 uint32_t share_access, 186 uint16_t ftype, 187 char *pipe_name, 188 uint32_t rpc_fid, 189 uint32_t uniqid, 190 smb_error_t *err) 191 { 192 smb_ofile_t *of; 193 uint16_t fid; 194 195 if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) { 196 err->status = NT_STATUS_TOO_MANY_OPENED_FILES; 197 err->errcls = ERRDOS; 198 err->errcode = ERROR_TOO_MANY_OPEN_FILES; 199 return (NULL); 200 } 201 202 of = kmem_cache_alloc(smb_info.si_cache_ofile, KM_SLEEP); 203 bzero(of, sizeof (smb_ofile_t)); 204 of->f_magic = SMB_OFILE_MAGIC; 205 of->f_refcnt = 1; 206 of->f_fid = fid; 207 of->f_uniqid = uniqid; 208 of->f_opened_by_pid = pid; 209 of->f_granted_access = access_granted; 210 of->f_share_access = share_access; 211 of->f_create_options = create_options; 212 of->f_cr = tree->t_user->u_cred; 213 crhold(of->f_cr); 214 of->f_ftype = ftype; 215 of->f_session = tree->t_user->u_session; 216 of->f_user = tree->t_user; 217 of->f_tree = tree; 218 of->f_node = node; 219 mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); 220 of->f_state = SMB_OFILE_STATE_OPEN; 221 222 if (ftype == SMB_FTYPE_MESG_PIPE) { 223 of->f_pipe_info = kmem_alloc(sizeof (mlsvc_pipe_t), KM_SLEEP); 224 bzero(of->f_pipe_info, sizeof (mlsvc_pipe_t)); 225 of->f_pipe_info->pipe_name = pipe_name; 226 of->f_pipe_info->fid = rpc_fid; 227 mutex_init(&of->f_pipe_info->mutex, NULL, MUTEX_DEFAULT, NULL); 228 cv_init(&of->f_pipe_info->cv, NULL, CV_DEFAULT, NULL); 229 } else { 230 ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ 231 ASSERT(node); 232 if (crgetuid(of->f_cr) == node->attr.sa_vattr.va_uid) { 233 /* 234 * Add this bit for the file's owner even if it's not 235 * specified in the request (Windows behavior). 236 */ 237 of->f_granted_access |= FILE_READ_ATTRIBUTES; 238 } 239 240 if ((node->vp->v_type == VREG) && (smb_fsop_open(of) != 0)) { 241 of->f_magic = 0; 242 mutex_destroy(&of->f_mutex); 243 crfree(of->f_cr); 244 smb_idpool_free(&tree->t_fid_pool, of->f_fid); 245 kmem_cache_free(smb_info.si_cache_ofile, of); 246 err->status = NT_STATUS_ACCESS_DENIED; 247 err->errcls = ERRDOS; 248 err->errcode = ERROR_ACCESS_DENIED; 249 return (NULL); 250 } 251 smb_llist_enter(&node->n_ofile_list, RW_WRITER); 252 smb_llist_insert_tail(&node->n_ofile_list, of); 253 smb_llist_exit(&node->n_ofile_list); 254 } 255 smb_llist_enter(&tree->t_ofile_list, RW_WRITER); 256 smb_llist_insert_tail(&tree->t_ofile_list, of); 257 smb_llist_exit(&tree->t_ofile_list); 258 atomic_inc_32(&smb_info.open_files); 259 atomic_inc_32(&of->f_session->s_file_cnt); 260 261 return (of); 262 } 263 264 /* 265 * smb_ofile_close 266 * 267 * 268 */ 269 int 270 smb_ofile_close( 271 smb_ofile_t *of, 272 uint32_t last_wtime) 273 { 274 int rc = 0; 275 276 ASSERT(of); 277 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 278 279 mutex_enter(&of->f_mutex); 280 ASSERT(of->f_refcnt); 281 switch (of->f_state) { 282 case SMB_OFILE_STATE_OPEN: { 283 284 of->f_state = SMB_OFILE_STATE_CLOSING; 285 mutex_exit(&of->f_mutex); 286 287 if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { 288 smb_rpc_close(of); 289 } else { 290 if (of->f_node->flags & NODE_CREATED_READONLY) { 291 smb_node_set_dosattr(of->f_node, 292 of->f_node->attr.sa_dosattr | 293 SMB_FA_READONLY); 294 of->f_node->flags &= ~NODE_CREATED_READONLY; 295 } 296 297 smb_ofile_close_timestamp_update(of, last_wtime); 298 rc = smb_sync_fsattr(NULL, of->f_cr, of->f_node); 299 smb_commit_delete_on_close(of); 300 smb_release_oplock(of, OPLOCK_RELEASE_FILE_CLOSED); 301 302 /* 303 * Share reservations cannot be removed until the 304 * readonly bit has been set (if needed), above. 305 * See comments in smb_open_subr(). 306 */ 307 smb_fsop_unshrlock(of->f_cr, of->f_node, of->f_uniqid); 308 309 if (of->f_node->vp->v_type == VREG) 310 (void) smb_fsop_close(of); 311 312 /* 313 * Cancel any notify change requests related 314 * to this open instance. 315 */ 316 if (of->f_node->flags & NODE_FLAGS_NOTIFY_CHANGE) 317 smb_process_file_notify_change_queue(of); 318 smb_node_destroy_lock_by_ofile(of->f_node, of); 319 } 320 atomic_dec_32(&smb_info.open_files); 321 322 mutex_enter(&of->f_mutex); 323 ASSERT(of->f_refcnt); 324 ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING); 325 of->f_state = SMB_OFILE_STATE_CLOSED; 326 mutex_exit(&of->f_mutex); 327 return (rc); 328 } 329 case SMB_OFILE_STATE_CLOSED: 330 case SMB_OFILE_STATE_CLOSING: 331 break; 332 333 default: 334 ASSERT(0); 335 break; 336 } 337 mutex_exit(&of->f_mutex); 338 return (rc); 339 } 340 341 /* 342 * smb_ofile_close_all 343 * 344 * 345 */ 346 void 347 smb_ofile_close_all( 348 smb_tree_t *tree) 349 { 350 smb_ofile_t *of; 351 352 ASSERT(tree); 353 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 354 355 smb_llist_enter(&tree->t_ofile_list, RW_READER); 356 of = smb_llist_head(&tree->t_ofile_list); 357 while (of) { 358 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 359 ASSERT(of->f_tree == tree); 360 of = smb_ofile_close_and_next(of); 361 } 362 smb_llist_exit(&tree->t_ofile_list); 363 } 364 365 /* 366 * smb_ofiles_close_by_pid 367 * 368 * 369 */ 370 void 371 smb_ofile_close_all_by_pid( 372 smb_tree_t *tree, 373 uint16_t pid) 374 { 375 smb_ofile_t *of; 376 377 ASSERT(tree); 378 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 379 380 smb_llist_enter(&tree->t_ofile_list, RW_READER); 381 of = smb_llist_head(&tree->t_ofile_list); 382 while (of) { 383 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 384 ASSERT(of->f_tree == tree); 385 if (of->f_opened_by_pid == pid) { 386 of = smb_ofile_close_and_next(of); 387 } else { 388 of = smb_llist_next(&tree->t_ofile_list, of); 389 } 390 } 391 smb_llist_exit(&tree->t_ofile_list); 392 } 393 394 /* 395 * smb_ofile_release 396 * 397 */ 398 void 399 smb_ofile_release( 400 smb_ofile_t *of) 401 { 402 ASSERT(of); 403 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 404 405 mutex_enter(&of->f_mutex); 406 ASSERT(of->f_refcnt); 407 of->f_refcnt--; 408 switch (of->f_state) { 409 case SMB_OFILE_STATE_OPEN: 410 case SMB_OFILE_STATE_CLOSING: 411 break; 412 413 case SMB_OFILE_STATE_CLOSED: 414 if (of->f_refcnt == 0) { 415 mutex_exit(&of->f_mutex); 416 smb_ofile_delete(of); 417 return; 418 } 419 break; 420 421 default: 422 ASSERT(0); 423 break; 424 } 425 mutex_exit(&of->f_mutex); 426 } 427 428 /* 429 * smb_ofile_lookup_by_fid 430 * 431 * Find the open file whose fid matches the one specified in the request. 432 * If we can't find the fid or the shares (trees) don't match, we have a 433 * bad fid. 434 */ 435 smb_ofile_t * 436 smb_ofile_lookup_by_fid( 437 smb_tree_t *tree, 438 uint16_t fid) 439 { 440 smb_llist_t *of_list; 441 smb_ofile_t *of; 442 443 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 444 445 of_list = &tree->t_ofile_list; 446 447 smb_llist_enter(of_list, RW_READER); 448 of = smb_llist_head(of_list); 449 while (of) { 450 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 451 ASSERT(of->f_tree == tree); 452 if (of->f_fid == fid) { 453 mutex_enter(&of->f_mutex); 454 if (of->f_state != SMB_OFILE_STATE_OPEN) { 455 mutex_exit(&of->f_mutex); 456 smb_llist_exit(of_list); 457 return (NULL); 458 } 459 of->f_refcnt++; 460 mutex_exit(&of->f_mutex); 461 break; 462 } 463 of = smb_llist_next(of_list, of); 464 } 465 smb_llist_exit(of_list); 466 return (of); 467 } 468 469 /* 470 * smb_ofile_set_flags 471 * 472 * Return value: 473 * 474 * Current flags value 475 * 476 */ 477 void 478 smb_ofile_set_flags( 479 smb_ofile_t *of, 480 uint32_t flags) 481 { 482 ASSERT(of); 483 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 484 ASSERT(of->f_refcnt); 485 486 mutex_enter(&of->f_mutex); 487 of->f_flags |= flags; 488 mutex_exit(&of->f_mutex); 489 } 490 /* 491 * smb_ofile_seek 492 * 493 * Return value: 494 * 495 * 0 Success 496 * EINVAL Unknown mode 497 * EOVERFLOW offset too big 498 * 499 */ 500 int 501 smb_ofile_seek( 502 smb_ofile_t *of, 503 ushort_t mode, 504 int32_t off, 505 uint32_t *retoff) 506 { 507 u_offset_t newoff = 0; 508 int rc = 0; 509 510 ASSERT(of); 511 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 512 ASSERT(of->f_refcnt); 513 514 mutex_enter(&of->f_mutex); 515 switch (mode) { 516 case SMB_SEEK_SET: 517 if (off < 0) 518 newoff = 0; 519 else 520 newoff = (u_offset_t)off; 521 break; 522 523 case SMB_SEEK_CUR: 524 if (off < 0 && (-off) > of->f_seek_pos) 525 newoff = 0; 526 else 527 newoff = of->f_seek_pos + (u_offset_t)off; 528 break; 529 530 case SMB_SEEK_END: 531 if (off < 0 && (-off) > of->f_node->attr.sa_vattr.va_size) 532 newoff = 0; 533 else 534 newoff = of->f_node->attr.sa_vattr.va_size + 535 (u_offset_t)off; 536 break; 537 538 default: 539 mutex_exit(&of->f_mutex); 540 return (EINVAL); 541 } 542 543 /* 544 * See comments at the beginning of smb_seek.c. 545 * If the offset is greater than UINT_MAX, we will return an error. 546 */ 547 548 if (newoff > UINT_MAX) { 549 rc = EOVERFLOW; 550 } else { 551 of->f_seek_pos = newoff; 552 *retoff = (uint32_t)newoff; 553 } 554 mutex_exit(&of->f_mutex); 555 return (rc); 556 } 557 558 /* 559 * smb_ofile_close_timestamp_update 560 * 561 * 562 */ 563 void 564 smb_ofile_close_timestamp_update( 565 smb_ofile_t *of, 566 uint32_t last_wtime) 567 { 568 smb_node_t *node; 569 timestruc_t mtime, atime; 570 unsigned int what = 0; 571 572 mtime.tv_sec = last_wtime; 573 mtime.tv_nsec = 0; 574 575 if (mtime.tv_sec != 0 && mtime.tv_sec != 0xFFFFFFFF) { 576 mtime.tv_sec = smb_local_time_to_gmt(mtime.tv_sec); 577 what |= SMB_AT_MTIME; 578 } 579 580 /* 581 * NODE_FLAGS_SYNCATIME is set whenever something is 582 * written to a file. Compliant volumes don't update 583 * atime upon write, so don't update the atime if the 584 * volume is compliant. 585 */ 586 node = of->f_node; 587 if (node->flags & NODE_FLAGS_SYNCATIME) { 588 node->flags &= ~NODE_FLAGS_SYNCATIME; 589 what |= SMB_AT_ATIME; 590 (void) microtime(&atime); 591 } 592 593 smb_node_set_time(node, 0, &mtime, &atime, 0, what); 594 } 595 596 /* 597 * smb_ofile_is_open 598 * 599 */ 600 boolean_t 601 smb_ofile_is_open( 602 smb_ofile_t *of) 603 { 604 boolean_t rc = B_FALSE; 605 606 ASSERT(of); 607 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 608 609 mutex_enter(&of->f_mutex); 610 if (of->f_state == SMB_OFILE_STATE_OPEN) { 611 rc = B_TRUE; 612 } 613 mutex_exit(&of->f_mutex); 614 return (rc); 615 } 616 617 /* *************************** Static Functions ***************************** */ 618 619 /* 620 * smb_ofile_close_and_next 621 * 622 * This function closes the file passed in (if appropriate) and returns the 623 * next open file in the list of open files of the tree of the open file passed 624 * in. It requires that the list of open files of the tree be entered in 625 * RW_READER mode before being called. 626 */ 627 static smb_ofile_t * 628 smb_ofile_close_and_next( 629 smb_ofile_t *of) 630 { 631 smb_ofile_t *next_of; 632 smb_tree_t *tree; 633 634 ASSERT(of); 635 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 636 637 mutex_enter(&of->f_mutex); 638 switch (of->f_state) { 639 case SMB_OFILE_STATE_OPEN: 640 /* The file is still open. */ 641 of->f_refcnt++; 642 ASSERT(of->f_refcnt); 643 tree = of->f_tree; 644 mutex_exit(&of->f_mutex); 645 smb_llist_exit(&of->f_tree->t_ofile_list); 646 (void) smb_ofile_close(of, 0); 647 smb_ofile_release(of); 648 smb_llist_enter(&tree->t_ofile_list, RW_READER); 649 next_of = smb_llist_head(&tree->t_ofile_list); 650 break; 651 case SMB_OFILE_STATE_CLOSING: 652 case SMB_OFILE_STATE_CLOSED: 653 /* 654 * The ofile exists but is closed or 655 * in the process being closed. 656 */ 657 mutex_exit(&of->f_mutex); 658 next_of = smb_llist_next(&of->f_tree->t_ofile_list, of); 659 break; 660 default: 661 ASSERT(0); 662 mutex_exit(&of->f_mutex); 663 next_of = smb_llist_next(&of->f_tree->t_ofile_list, of); 664 break; 665 } 666 return (next_of); 667 } 668 669 /* 670 * smb_ofile_delete 671 * 672 * 673 */ 674 static void 675 smb_ofile_delete( 676 smb_ofile_t *of) 677 { 678 ASSERT(of); 679 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 680 ASSERT(of->f_refcnt == 0); 681 ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED); 682 683 /* 684 * Let's remove the ofile from the list of ofiles of the tree. This has 685 * to be done before any resources associated with the ofile are 686 * released. 687 */ 688 smb_llist_enter(&of->f_tree->t_ofile_list, RW_WRITER); 689 smb_llist_remove(&of->f_tree->t_ofile_list, of); 690 smb_llist_exit(&of->f_tree->t_ofile_list); 691 atomic_dec_32(&of->f_session->s_file_cnt); 692 693 if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { 694 kmem_free(of->f_pipe_info, sizeof (mlsvc_pipe_t)); 695 of->f_pipe_info = NULL; 696 } else { 697 ASSERT(of->f_ftype == SMB_FTYPE_DISK); 698 ASSERT(of->f_node != NULL); 699 smb_llist_enter(&of->f_node->n_ofile_list, RW_WRITER); 700 smb_llist_remove(&of->f_node->n_ofile_list, of); 701 smb_llist_exit(&of->f_node->n_ofile_list); 702 smb_node_release(of->f_node); 703 } 704 705 of->f_magic = (uint32_t)~SMB_OFILE_MAGIC; 706 mutex_destroy(&of->f_mutex); 707 crfree(of->f_cr); 708 smb_idpool_free(&of->f_tree->t_fid_pool, of->f_fid); 709 kmem_cache_free(smb_info.si_cache_ofile, of); 710 } 711 712 /* 713 * smb_ofile_access 714 * 715 * This function will check to see if the access requested is granted. 716 * Returns NT status codes. 717 */ 718 uint32_t 719 smb_ofile_access(smb_ofile_t *of, cred_t *cr, uint32_t access) 720 { 721 722 if ((of == NULL) || (cr == kcred)) 723 return (NT_STATUS_SUCCESS); 724 725 /* 726 * If the request is for something 727 * I don't grant it is an error 728 */ 729 if (~(of->f_granted_access) & access) { 730 if (!(of->f_granted_access & ACCESS_SYSTEM_SECURITY) && 731 (access & ACCESS_SYSTEM_SECURITY)) { 732 return (NT_STATUS_PRIVILEGE_NOT_HELD); 733 } 734 return (NT_STATUS_ACCESS_DENIED); 735 } 736 737 return (NT_STATUS_SUCCESS); 738 } 739