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(tree->t_server->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_server = tree->t_server; 216 of->f_session = tree->t_user->u_session; 217 of->f_user = tree->t_user; 218 of->f_tree = tree; 219 of->f_node = node; 220 mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); 221 of->f_state = SMB_OFILE_STATE_OPEN; 222 223 if (ftype == SMB_FTYPE_MESG_PIPE) { 224 of->f_pipe_info = kmem_alloc(sizeof (mlsvc_pipe_t), KM_SLEEP); 225 bzero(of->f_pipe_info, sizeof (mlsvc_pipe_t)); 226 of->f_pipe_info->pipe_name = pipe_name; 227 of->f_pipe_info->fid = rpc_fid; 228 mutex_init(&of->f_pipe_info->mutex, NULL, MUTEX_DEFAULT, NULL); 229 cv_init(&of->f_pipe_info->cv, NULL, CV_DEFAULT, NULL); 230 } else { 231 ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ 232 ASSERT(node); 233 if (crgetuid(of->f_cr) == node->attr.sa_vattr.va_uid) { 234 /* 235 * Add this bit for the file's owner even if it's not 236 * specified in the request (Windows behavior). 237 */ 238 of->f_granted_access |= FILE_READ_ATTRIBUTES; 239 } 240 241 if ((node->vp->v_type == VREG) && (smb_fsop_open(of) != 0)) { 242 of->f_magic = 0; 243 mutex_destroy(&of->f_mutex); 244 crfree(of->f_cr); 245 smb_idpool_free(&tree->t_fid_pool, of->f_fid); 246 kmem_cache_free(tree->t_server->si_cache_ofile, of); 247 err->status = NT_STATUS_ACCESS_DENIED; 248 err->errcls = ERRDOS; 249 err->errcode = ERROR_ACCESS_DENIED; 250 return (NULL); 251 } 252 smb_llist_enter(&node->n_ofile_list, RW_WRITER); 253 smb_llist_insert_tail(&node->n_ofile_list, of); 254 smb_llist_exit(&node->n_ofile_list); 255 } 256 smb_llist_enter(&tree->t_ofile_list, RW_WRITER); 257 smb_llist_insert_tail(&tree->t_ofile_list, of); 258 smb_llist_exit(&tree->t_ofile_list); 259 atomic_inc_32(&tree->t_server->sv_open_files); 260 atomic_inc_32(&of->f_session->s_file_cnt); 261 262 return (of); 263 } 264 265 /* 266 * smb_ofile_close 267 * 268 * 269 */ 270 int 271 smb_ofile_close( 272 smb_ofile_t *of, 273 uint32_t last_wtime) 274 { 275 int rc = 0; 276 277 ASSERT(of); 278 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 279 280 mutex_enter(&of->f_mutex); 281 ASSERT(of->f_refcnt); 282 switch (of->f_state) { 283 case SMB_OFILE_STATE_OPEN: { 284 285 of->f_state = SMB_OFILE_STATE_CLOSING; 286 mutex_exit(&of->f_mutex); 287 288 if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { 289 smb_rpc_close(of); 290 } else { 291 if (of->f_node->flags & NODE_CREATED_READONLY) { 292 smb_node_set_dosattr(of->f_node, 293 of->f_node->attr.sa_dosattr | 294 SMB_FA_READONLY); 295 of->f_node->flags &= ~NODE_CREATED_READONLY; 296 } 297 298 smb_ofile_close_timestamp_update(of, last_wtime); 299 rc = smb_sync_fsattr(NULL, of->f_cr, of->f_node); 300 smb_commit_delete_on_close(of); 301 smb_release_oplock(of, OPLOCK_RELEASE_FILE_CLOSED); 302 303 /* 304 * Share reservations cannot be removed until the 305 * readonly bit has been set (if needed), above. 306 * See comments in smb_open_subr(). 307 */ 308 smb_fsop_unshrlock(of->f_cr, of->f_node, of->f_uniqid); 309 310 if (of->f_node->vp->v_type == VREG) 311 (void) smb_fsop_close(of); 312 313 /* 314 * Cancel any notify change requests related 315 * to this open instance. 316 */ 317 if (of->f_node->flags & NODE_FLAGS_NOTIFY_CHANGE) 318 smb_process_file_notify_change_queue(of); 319 smb_node_destroy_lock_by_ofile(of->f_node, of); 320 } 321 atomic_dec_32(&of->f_tree->t_server->sv_open_files); 322 323 mutex_enter(&of->f_mutex); 324 ASSERT(of->f_refcnt); 325 ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING); 326 of->f_state = SMB_OFILE_STATE_CLOSED; 327 mutex_exit(&of->f_mutex); 328 return (rc); 329 } 330 case SMB_OFILE_STATE_CLOSED: 331 case SMB_OFILE_STATE_CLOSING: 332 break; 333 334 default: 335 ASSERT(0); 336 break; 337 } 338 mutex_exit(&of->f_mutex); 339 return (rc); 340 } 341 342 /* 343 * smb_ofile_close_all 344 * 345 * 346 */ 347 void 348 smb_ofile_close_all( 349 smb_tree_t *tree) 350 { 351 smb_ofile_t *of; 352 353 ASSERT(tree); 354 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 355 356 smb_llist_enter(&tree->t_ofile_list, RW_READER); 357 of = smb_llist_head(&tree->t_ofile_list); 358 while (of) { 359 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 360 ASSERT(of->f_tree == tree); 361 of = smb_ofile_close_and_next(of); 362 } 363 smb_llist_exit(&tree->t_ofile_list); 364 } 365 366 /* 367 * smb_ofiles_close_by_pid 368 * 369 * 370 */ 371 void 372 smb_ofile_close_all_by_pid( 373 smb_tree_t *tree, 374 uint16_t pid) 375 { 376 smb_ofile_t *of; 377 378 ASSERT(tree); 379 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 380 381 smb_llist_enter(&tree->t_ofile_list, RW_READER); 382 of = smb_llist_head(&tree->t_ofile_list); 383 while (of) { 384 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 385 ASSERT(of->f_tree == tree); 386 if (of->f_opened_by_pid == pid) { 387 of = smb_ofile_close_and_next(of); 388 } else { 389 of = smb_llist_next(&tree->t_ofile_list, of); 390 } 391 } 392 smb_llist_exit(&tree->t_ofile_list); 393 } 394 395 /* 396 * smb_ofile_release 397 * 398 */ 399 void 400 smb_ofile_release( 401 smb_ofile_t *of) 402 { 403 ASSERT(of); 404 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 405 406 mutex_enter(&of->f_mutex); 407 ASSERT(of->f_refcnt); 408 of->f_refcnt--; 409 switch (of->f_state) { 410 case SMB_OFILE_STATE_OPEN: 411 case SMB_OFILE_STATE_CLOSING: 412 break; 413 414 case SMB_OFILE_STATE_CLOSED: 415 if (of->f_refcnt == 0) { 416 mutex_exit(&of->f_mutex); 417 smb_ofile_delete(of); 418 return; 419 } 420 break; 421 422 default: 423 ASSERT(0); 424 break; 425 } 426 mutex_exit(&of->f_mutex); 427 } 428 429 /* 430 * smb_ofile_lookup_by_fid 431 * 432 * Find the open file whose fid matches the one specified in the request. 433 * If we can't find the fid or the shares (trees) don't match, we have a 434 * bad fid. 435 */ 436 smb_ofile_t * 437 smb_ofile_lookup_by_fid( 438 smb_tree_t *tree, 439 uint16_t fid) 440 { 441 smb_llist_t *of_list; 442 smb_ofile_t *of; 443 444 ASSERT(tree->t_magic == SMB_TREE_MAGIC); 445 446 of_list = &tree->t_ofile_list; 447 448 smb_llist_enter(of_list, RW_READER); 449 of = smb_llist_head(of_list); 450 while (of) { 451 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 452 ASSERT(of->f_tree == tree); 453 if (of->f_fid == fid) { 454 mutex_enter(&of->f_mutex); 455 if (of->f_state != SMB_OFILE_STATE_OPEN) { 456 mutex_exit(&of->f_mutex); 457 smb_llist_exit(of_list); 458 return (NULL); 459 } 460 of->f_refcnt++; 461 mutex_exit(&of->f_mutex); 462 break; 463 } 464 of = smb_llist_next(of_list, of); 465 } 466 smb_llist_exit(of_list); 467 return (of); 468 } 469 470 /* 471 * smb_ofile_set_flags 472 * 473 * Return value: 474 * 475 * Current flags value 476 * 477 */ 478 void 479 smb_ofile_set_flags( 480 smb_ofile_t *of, 481 uint32_t flags) 482 { 483 ASSERT(of); 484 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 485 ASSERT(of->f_refcnt); 486 487 mutex_enter(&of->f_mutex); 488 of->f_flags |= flags; 489 mutex_exit(&of->f_mutex); 490 } 491 /* 492 * smb_ofile_seek 493 * 494 * Return value: 495 * 496 * 0 Success 497 * EINVAL Unknown mode 498 * EOVERFLOW offset too big 499 * 500 */ 501 int 502 smb_ofile_seek( 503 smb_ofile_t *of, 504 ushort_t mode, 505 int32_t off, 506 uint32_t *retoff) 507 { 508 u_offset_t newoff = 0; 509 int rc = 0; 510 511 ASSERT(of); 512 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 513 ASSERT(of->f_refcnt); 514 515 mutex_enter(&of->f_mutex); 516 switch (mode) { 517 case SMB_SEEK_SET: 518 if (off < 0) 519 newoff = 0; 520 else 521 newoff = (u_offset_t)off; 522 break; 523 524 case SMB_SEEK_CUR: 525 if (off < 0 && (-off) > of->f_seek_pos) 526 newoff = 0; 527 else 528 newoff = of->f_seek_pos + (u_offset_t)off; 529 break; 530 531 case SMB_SEEK_END: 532 if (off < 0 && (-off) > of->f_node->attr.sa_vattr.va_size) 533 newoff = 0; 534 else 535 newoff = of->f_node->attr.sa_vattr.va_size + 536 (u_offset_t)off; 537 break; 538 539 default: 540 mutex_exit(&of->f_mutex); 541 return (EINVAL); 542 } 543 544 /* 545 * See comments at the beginning of smb_seek.c. 546 * If the offset is greater than UINT_MAX, we will return an error. 547 */ 548 549 if (newoff > UINT_MAX) { 550 rc = EOVERFLOW; 551 } else { 552 of->f_seek_pos = newoff; 553 *retoff = (uint32_t)newoff; 554 } 555 mutex_exit(&of->f_mutex); 556 return (rc); 557 } 558 559 /* 560 * smb_ofile_close_timestamp_update 561 * 562 * 563 */ 564 void 565 smb_ofile_close_timestamp_update( 566 smb_ofile_t *of, 567 uint32_t last_wtime) 568 { 569 smb_node_t *node; 570 timestruc_t mtime, atime; 571 unsigned int what = 0; 572 573 mtime.tv_sec = last_wtime; 574 mtime.tv_nsec = 0; 575 576 if (mtime.tv_sec != 0 && mtime.tv_sec != 0xFFFFFFFF) { 577 mtime.tv_sec -= of->f_server->si_gmtoff; 578 what |= SMB_AT_MTIME; 579 } 580 581 /* 582 * NODE_FLAGS_SYNCATIME is set whenever something is 583 * written to a file. Compliant volumes don't update 584 * atime upon write, so don't update the atime if the 585 * volume is compliant. 586 */ 587 node = of->f_node; 588 if (node->flags & NODE_FLAGS_SYNCATIME) { 589 node->flags &= ~NODE_FLAGS_SYNCATIME; 590 what |= SMB_AT_ATIME; 591 (void) microtime(&atime); 592 } 593 594 smb_node_set_time(node, 0, &mtime, &atime, 0, what); 595 } 596 597 /* 598 * smb_ofile_is_open 599 * 600 */ 601 boolean_t 602 smb_ofile_is_open( 603 smb_ofile_t *of) 604 { 605 boolean_t rc = B_FALSE; 606 607 ASSERT(of); 608 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 609 610 mutex_enter(&of->f_mutex); 611 if (of->f_state == SMB_OFILE_STATE_OPEN) { 612 rc = B_TRUE; 613 } 614 mutex_exit(&of->f_mutex); 615 return (rc); 616 } 617 618 /* *************************** Static Functions ***************************** */ 619 620 /* 621 * smb_ofile_close_and_next 622 * 623 * This function closes the file passed in (if appropriate) and returns the 624 * next open file in the list of open files of the tree of the open file passed 625 * in. It requires that the list of open files of the tree be entered in 626 * RW_READER mode before being called. 627 */ 628 static smb_ofile_t * 629 smb_ofile_close_and_next( 630 smb_ofile_t *of) 631 { 632 smb_ofile_t *next_of; 633 smb_tree_t *tree; 634 635 ASSERT(of); 636 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 637 638 mutex_enter(&of->f_mutex); 639 switch (of->f_state) { 640 case SMB_OFILE_STATE_OPEN: 641 /* The file is still open. */ 642 of->f_refcnt++; 643 ASSERT(of->f_refcnt); 644 tree = of->f_tree; 645 mutex_exit(&of->f_mutex); 646 smb_llist_exit(&of->f_tree->t_ofile_list); 647 (void) smb_ofile_close(of, 0); 648 smb_ofile_release(of); 649 smb_llist_enter(&tree->t_ofile_list, RW_READER); 650 next_of = smb_llist_head(&tree->t_ofile_list); 651 break; 652 case SMB_OFILE_STATE_CLOSING: 653 case SMB_OFILE_STATE_CLOSED: 654 /* 655 * The ofile exists but is closed or 656 * in the process being closed. 657 */ 658 mutex_exit(&of->f_mutex); 659 next_of = smb_llist_next(&of->f_tree->t_ofile_list, of); 660 break; 661 default: 662 ASSERT(0); 663 mutex_exit(&of->f_mutex); 664 next_of = smb_llist_next(&of->f_tree->t_ofile_list, of); 665 break; 666 } 667 return (next_of); 668 } 669 670 /* 671 * smb_ofile_delete 672 * 673 * 674 */ 675 static void 676 smb_ofile_delete( 677 smb_ofile_t *of) 678 { 679 ASSERT(of); 680 ASSERT(of->f_magic == SMB_OFILE_MAGIC); 681 ASSERT(of->f_refcnt == 0); 682 ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED); 683 684 /* 685 * Let's remove the ofile from the list of ofiles of the tree. This has 686 * to be done before any resources associated with the ofile are 687 * released. 688 */ 689 smb_llist_enter(&of->f_tree->t_ofile_list, RW_WRITER); 690 smb_llist_remove(&of->f_tree->t_ofile_list, of); 691 smb_llist_exit(&of->f_tree->t_ofile_list); 692 atomic_dec_32(&of->f_session->s_file_cnt); 693 694 if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { 695 kmem_free(of->f_pipe_info, sizeof (mlsvc_pipe_t)); 696 of->f_pipe_info = NULL; 697 } else { 698 ASSERT(of->f_ftype == SMB_FTYPE_DISK); 699 ASSERT(of->f_node != NULL); 700 smb_llist_enter(&of->f_node->n_ofile_list, RW_WRITER); 701 smb_llist_remove(&of->f_node->n_ofile_list, of); 702 smb_llist_exit(&of->f_node->n_ofile_list); 703 smb_node_release(of->f_node); 704 } 705 706 of->f_magic = (uint32_t)~SMB_OFILE_MAGIC; 707 mutex_destroy(&of->f_mutex); 708 crfree(of->f_cr); 709 smb_idpool_free(&of->f_tree->t_fid_pool, of->f_fid); 710 kmem_cache_free(of->f_tree->t_server->si_cache_ofile, of); 711 } 712 713 /* 714 * smb_ofile_access 715 * 716 * This function will check to see if the access requested is granted. 717 * Returns NT status codes. 718 */ 719 uint32_t 720 smb_ofile_access(smb_ofile_t *of, cred_t *cr, uint32_t access) 721 { 722 723 if ((of == NULL) || (cr == kcred)) 724 return (NT_STATUS_SUCCESS); 725 726 /* 727 * If the request is for something 728 * I don't grant it is an error 729 */ 730 if (~(of->f_granted_access) & access) { 731 if (!(of->f_granted_access & ACCESS_SYSTEM_SECURITY) && 732 (access & ACCESS_SYSTEM_SECURITY)) { 733 return (NT_STATUS_PRIVILEGE_NOT_HELD); 734 } 735 return (NT_STATUS_ACCESS_DENIED); 736 } 737 738 return (NT_STATUS_SUCCESS); 739 } 740