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