1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 Create.c 8 9 Abstract: 10 11 This module implements the File Create routine for Cdfs called by the 12 Fsd/Fsp dispatch routines. 13 14 15 --*/ 16 17 #include "cdprocs.h" 18 19 // 20 // The Bug check file id for this module 21 // 22 23 #define BugCheckFileId (CDFS_BUG_CHECK_CREATE) 24 25 // 26 // Local support routines 27 // 28 29 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedCcb, _In_)) 30 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedCcb, _In_opt_)) 31 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedFileName, _In_)) 32 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedFileName, _In_opt_)) 33 NTSTATUS 34 CdNormalizeFileNames ( 35 _Inout_ PIRP_CONTEXT IrpContext, 36 _In_ PVCB Vcb, 37 _In_ BOOLEAN OpenByFileId, 38 _In_ BOOLEAN IgnoreCase, 39 _In_ TYPE_OF_OPEN RelatedTypeOfOpen, 40 PCCB RelatedCcb, 41 PUNICODE_STRING RelatedFileName, 42 _Inout_ PUNICODE_STRING FileName, 43 _Inout_ PCD_NAME RemainingName 44 ); 45 46 _Requires_lock_held_(_Global_critical_region_) 47 _Acquires_exclusive_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) 48 NTSTATUS 49 CdOpenByFileId ( 50 _In_ PIRP_CONTEXT IrpContext, 51 _In_ PIO_STACK_LOCATION IrpSp, 52 _In_ PVCB Vcb, 53 _Inout_ PFCB *CurrentFcb 54 ); 55 56 _Requires_lock_held_(_Global_critical_region_) 57 NTSTATUS 58 CdOpenExistingFcb ( 59 _In_ PIRP_CONTEXT IrpContext, 60 _In_ PIO_STACK_LOCATION IrpSp, 61 _Inout_ PFCB *CurrentFcb, 62 _In_ TYPE_OF_OPEN TypeOfOpen, 63 _In_ BOOLEAN IgnoreCase, 64 _In_opt_ PCCB RelatedCcb 65 ); 66 67 _Requires_lock_held_(_Global_critical_region_) 68 _Acquires_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) 69 NTSTATUS 70 CdOpenDirectoryFromPathEntry ( 71 _In_ PIRP_CONTEXT IrpContext, 72 _In_ PIO_STACK_LOCATION IrpSp, 73 _In_ PVCB Vcb, 74 _Inout_ PFCB *CurrentFcb, 75 _In_ PCD_NAME DirName, 76 _In_ BOOLEAN IgnoreCase, 77 _In_ BOOLEAN ShortNameMatch, 78 _In_ PPATH_ENTRY PathEntry, 79 _In_ BOOLEAN PerformUserOpen, 80 _In_opt_ PCCB RelatedCcb 81 ); 82 83 _Requires_lock_held_(_Global_critical_region_) 84 NTSTATUS 85 CdOpenFileFromFileContext ( 86 _In_ PIRP_CONTEXT IrpContext, 87 _In_ PIO_STACK_LOCATION IrpSp, 88 _In_ PVCB Vcb, 89 _Inout_ PFCB *CurrentFcb, 90 _In_ PCD_NAME FileName, 91 _In_ BOOLEAN IgnoreCase, 92 _In_ BOOLEAN ShortNameMatch, 93 _In_ PFILE_ENUM_CONTEXT FileContext, 94 _In_opt_ PCCB RelatedCcb 95 ); 96 97 _Requires_lock_held_(_Global_critical_region_) 98 NTSTATUS 99 CdCompleteFcbOpen ( 100 _In_ PIRP_CONTEXT IrpContext, 101 _In_ PIO_STACK_LOCATION IrpSp, 102 _In_ PVCB Vcb, 103 _Inout_ PFCB *CurrentFcb, 104 _In_ TYPE_OF_OPEN TypeOfOpen, 105 _In_ ULONG UserCcbFlags, 106 _In_ ACCESS_MASK DesiredAccess 107 ); 108 109 #ifdef ALLOC_PRAGMA 110 #pragma alloc_text(PAGE, CdCommonCreate) 111 #pragma alloc_text(PAGE, CdCompleteFcbOpen) 112 #pragma alloc_text(PAGE, CdNormalizeFileNames) 113 #pragma alloc_text(PAGE, CdOpenByFileId) 114 #pragma alloc_text(PAGE, CdOpenDirectoryFromPathEntry) 115 #pragma alloc_text(PAGE, CdOpenExistingFcb) 116 #pragma alloc_text(PAGE, CdOpenFileFromFileContext) 117 #endif 118 119 120 _Requires_lock_held_(_Global_critical_region_) 121 NTSTATUS 122 #ifdef _MSC_VER 123 #pragma prefast(suppress:26165, "Esp:1153") 124 #endif 125 CdCommonCreate ( 126 _Inout_ PIRP_CONTEXT IrpContext, 127 _Inout_ PIRP Irp 128 ) 129 130 /*++ 131 132 Routine Description: 133 134 This is the common routine for opening a file called by both the 135 Fsp and Fsd threads. 136 137 The file can be opened either by name or by file Id either with or without 138 a relative name. The file name field in the file object passed to this routine 139 contains either a unicode string or a 64 bit value which is the file Id. 140 If this is not a Joliet disk then we will convert the unicode name to 141 an Oem string in this routine. If there is a related file object with 142 a name then we will already have converted that name to Oem. 143 144 We will store the full name for the file in the file object on a successful 145 open. We will allocate a larger buffer if necessary and combine the 146 related and file object names. The only exception is the relative open 147 when the related file object is for an OpenByFileId file. If we need to 148 allocate a buffer for a case insensitive name then we allocate it at 149 the tail of the buffer we will store into the file object. The upcased 150 portion will begin immediately after the name defined by the FileName 151 in the file object. 152 153 Once we have the full name in the file object we don't want to split the 154 name in the event of a retry. We use a flag in the IrpContext to indicate 155 that the name has been split. 156 157 Arguments: 158 159 Irp - Supplies the Irp to process 160 161 Return Value: 162 163 NTSTATUS - This is the status from this open operation. 164 165 --*/ 166 167 { 168 NTSTATUS Status = STATUS_SUCCESS; 169 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); 170 171 PFILE_OBJECT FileObject; 172 173 COMPOUND_PATH_ENTRY CompoundPathEntry = {{0}};/* ReactOS Change: GCC "missing braces around initializer" */ 174 BOOLEAN CleanupCompoundPathEntry = FALSE; 175 176 FILE_ENUM_CONTEXT FileContext = {0}; 177 BOOLEAN CleanupFileContext = FALSE; 178 BOOLEAN FoundEntry; 179 180 PVCB Vcb; 181 182 BOOLEAN OpenByFileId; 183 BOOLEAN IgnoreCase; 184 ULONG CreateDisposition; 185 186 BOOLEAN ShortNameMatch; 187 ULONG ShortNameDirentOffset; 188 189 BOOLEAN VolumeOpen = FALSE; 190 191 // 192 // We will be acquiring and releasing file Fcb's as we move down the 193 // directory tree during opens. At any time we need to know the deepest 194 // point we have traversed down in the tree in case we need to cleanup 195 // any structures created here. 196 // 197 // CurrentFcb - represents this point. If non-null it means we have 198 // acquired it and need to release it in finally clause. 199 // 200 // NextFcb - represents the NextFcb to walk to but haven't acquired yet. 201 // 202 203 TYPE_OF_OPEN RelatedTypeOfOpen = UnopenedFileObject; 204 PFILE_OBJECT RelatedFileObject; 205 PCCB RelatedCcb = NULL; 206 207 PFCB NextFcb; 208 PFCB CurrentFcb = NULL; 209 210 // 211 // During the open we need to combine the related file object name 212 // with the remaining name. We also may need to upcase the file name 213 // in order to do a case-insensitive name comparison. We also need 214 // to restore the name in the file object in the event that we retry 215 // the request. We use the following string variables to manage the 216 // name. We will can put these strings into either Unicode or Ansi 217 // form. 218 // 219 // FileName - Pointer to name as currently stored in the file 220 // object. We store the full name into the file object early in 221 // the open operation. 222 // 223 // RelatedFileName - Pointer to the name in the related file object. 224 // 225 // RemainingName - String containing remaining name to parse. 226 // 227 // MatchingName - Address of name structure in FileContext which matched. 228 // We need this to know whether we matched the long or short name. 229 // 230 231 PUNICODE_STRING FileName; 232 PUNICODE_STRING RelatedFileName = NULL; 233 234 CD_NAME RemainingName = {{0}};/* ReactOS Change: GCC "missing braces around initializer" */ 235 CD_NAME FinalName; 236 PCD_NAME MatchingName = NULL; 237 238 PAGED_CODE(); 239 240 // 241 // If we were called with our file system device object instead of a 242 // volume device object, just complete this request with STATUS_SUCCESS. 243 // 244 245 if (IrpContext->Vcb == NULL) { 246 247 CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); 248 return STATUS_SUCCESS; 249 } 250 251 // 252 // Get create parameters from the Irp. 253 // 254 255 OpenByFileId = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID ); 256 IgnoreCase = !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE ); 257 CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff; 258 259 // 260 // Do some preliminary checks to make sure the operation is supported. 261 // We fail in the following cases immediately. 262 // 263 // - Open a paging file. 264 // - Open a target directory. 265 // - Open a file with Eas. 266 // - Create a file. 267 // 268 269 if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE | SL_OPEN_TARGET_DIRECTORY) || 270 (IrpSp->Parameters.Create.EaLength != 0) || 271 (CreateDisposition == FILE_CREATE)) { 272 273 CdCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); 274 return STATUS_ACCESS_DENIED; 275 } 276 277 #if (NTDDI_VERSION >= NTDDI_WIN7) 278 // 279 // CDFS does not support FILE_OPEN_REQUIRING_OPLOCK 280 // 281 282 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_REQUIRING_OPLOCK )) { 283 284 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); 285 return STATUS_INVALID_PARAMETER; 286 } 287 #endif 288 289 // 290 // Copy the Vcb to a local. Assume the starting directory is the root. 291 // 292 293 Vcb = IrpContext->Vcb; 294 NextFcb = Vcb->RootIndexFcb; 295 296 // 297 // Reference our input parameters to make things easier 298 // 299 300 FileObject = IrpSp->FileObject; 301 RelatedFileObject = NULL; 302 303 FileName = &FileObject->FileName; 304 305 // 306 // Set up the file object's Vpb pointer in case anything happens. 307 // This will allow us to get a reasonable pop-up. 308 // 309 310 if ((FileObject->RelatedFileObject != NULL) && !OpenByFileId) { 311 312 RelatedFileObject = FileObject->RelatedFileObject; 313 FileObject->Vpb = RelatedFileObject->Vpb; 314 315 RelatedTypeOfOpen = CdDecodeFileObject( IrpContext, RelatedFileObject, &NextFcb, &RelatedCcb ); 316 317 // 318 // Fail the request if this is not a user file object. 319 // 320 321 if (RelatedTypeOfOpen < UserVolumeOpen) { 322 323 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); 324 return STATUS_INVALID_PARAMETER; 325 } 326 327 // 328 // Remember the name in the related file object. 329 // 330 331 RelatedFileName = &RelatedFileObject->FileName; 332 } 333 334 // 335 // If we haven't initialized the names then make sure the strings are valid. 336 // If this an OpenByFileId then verify the file id buffer. 337 // 338 // After this routine returns we know that the full name is in the 339 // FileName buffer and the buffer will hold the upcased portion 340 // of the name yet to parse immediately after the full name in the 341 // buffer. Any trailing backslash has been removed and the flag 342 // in the IrpContext will indicate whether we removed the 343 // backslash. 344 // 345 346 Status = CdNormalizeFileNames( IrpContext, 347 Vcb, 348 OpenByFileId, 349 IgnoreCase, 350 RelatedTypeOfOpen, 351 RelatedCcb, 352 RelatedFileName, 353 FileName, 354 &RemainingName ); 355 356 // 357 // Return the error code if not successful. 358 // 359 360 if (!NT_SUCCESS( Status )) { 361 362 CdCompleteRequest( IrpContext, Irp, Status ); 363 return Status; 364 } 365 366 // 367 // We want to acquire the Vcb. Exclusively for a volume open, shared otherwise. 368 // The file name is empty for a volume open. 369 // 370 371 if ((FileName->Length == 0) && 372 (RelatedTypeOfOpen <= UserVolumeOpen) && 373 !OpenByFileId) { 374 375 VolumeOpen = TRUE; 376 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); 377 378 } else { 379 380 CdAcquireVcbShared( IrpContext, Vcb, FALSE ); 381 } 382 383 // 384 // Use a try-finally to facilitate cleanup. 385 // 386 387 _SEH2_TRY { 388 389 // 390 // Verify that the Vcb is not in an unusable condition. This routine 391 // will raise if not usable. 392 // 393 394 CdVerifyVcb( IrpContext, Vcb ); 395 396 // 397 // If the Vcb is locked then we cannot open another file 398 // 399 400 if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) { 401 402 try_return( Status = STATUS_ACCESS_DENIED ); 403 } 404 405 // 406 // If we are opening this file by FileId then process this immediately 407 // and exit. 408 // 409 410 if (OpenByFileId) { 411 412 // 413 // We only allow Dasd opens of audio disks. Fail this request at 414 // this point. 415 // 416 417 if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { 418 419 try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); 420 } 421 422 // 423 // The only create disposition we allow is OPEN. 424 // 425 426 if ((CreateDisposition != FILE_OPEN) && 427 (CreateDisposition != FILE_OPEN_IF)) { 428 429 try_return( Status = STATUS_ACCESS_DENIED ); 430 } 431 432 // 433 // Make sure we can wait for this request. 434 // 435 436 if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { 437 438 CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 439 } 440 441 try_return( Status = CdOpenByFileId( IrpContext, 442 IrpSp, 443 Vcb, 444 &CurrentFcb )); 445 } 446 447 // 448 // If we are opening this volume Dasd then process this immediately 449 // and exit. 450 // 451 452 if (VolumeOpen) { 453 454 // 455 // The only create disposition we allow is OPEN. 456 // 457 458 if ((CreateDisposition != FILE_OPEN) && 459 (CreateDisposition != FILE_OPEN_IF)) { 460 461 try_return( Status = STATUS_ACCESS_DENIED ); 462 } 463 464 // 465 // If they wanted to open a directory, surprise. 466 // 467 468 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { 469 470 try_return( Status = STATUS_NOT_A_DIRECTORY ); 471 } 472 473 // 474 // Acquire the Fcb first. 475 // 476 477 CurrentFcb = Vcb->VolumeDasdFcb; 478 CdAcquireFcbExclusive( IrpContext, CurrentFcb, FALSE ); 479 480 try_return( Status = CdOpenExistingFcb( IrpContext, 481 IrpSp, 482 &CurrentFcb, 483 UserVolumeOpen, 484 FALSE, 485 NULL )); 486 } 487 488 // 489 // At this point CurrentFcb points to the deepest Fcb for this open 490 // in the tree. Let's acquire this Fcb to keep it from being deleted 491 // beneath us. 492 // 493 494 CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); 495 CurrentFcb = NextFcb; 496 497 // 498 // Do a prefix search if there is more of the name to parse. 499 // 500 501 if (RemainingName.FileName.Length != 0) { 502 503 // 504 // Do the prefix search to find the longest matching name. 505 // 506 507 CdFindPrefix( IrpContext, 508 &CurrentFcb, 509 &RemainingName.FileName, 510 IgnoreCase ); 511 } 512 513 // 514 // If the remaining name length is zero then we have found our 515 // target. 516 // 517 518 if (RemainingName.FileName.Length == 0) { 519 520 // 521 // If this is a file so verify the user didn't want to open 522 // a directory. 523 // 524 525 if (SafeNodeType( CurrentFcb ) == CDFS_NTC_FCB_DATA) { 526 527 if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) || 528 FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { 529 530 try_return( Status = STATUS_NOT_A_DIRECTORY ); 531 } 532 533 // 534 // The only create disposition we allow is OPEN. 535 // 536 537 if ((CreateDisposition != FILE_OPEN) && 538 (CreateDisposition != FILE_OPEN_IF)) { 539 540 try_return( Status = STATUS_ACCESS_DENIED ); 541 } 542 543 try_return( Status = CdOpenExistingFcb( IrpContext, 544 IrpSp, 545 &CurrentFcb, 546 UserFileOpen, 547 IgnoreCase, 548 RelatedCcb )); 549 550 // 551 // This is a directory. Verify the user didn't want to open 552 // as a file. 553 // 554 555 } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { 556 557 try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); 558 559 // 560 // Open the file as a directory. 561 // 562 563 } else { 564 565 // 566 // The only create disposition we allow is OPEN. 567 // 568 569 if ((CreateDisposition != FILE_OPEN) && 570 (CreateDisposition != FILE_OPEN_IF)) { 571 572 try_return( Status = STATUS_ACCESS_DENIED ); 573 } 574 575 try_return( Status = CdOpenExistingFcb( IrpContext, 576 IrpSp, 577 &CurrentFcb, 578 UserDirectoryOpen, 579 IgnoreCase, 580 RelatedCcb )); 581 } 582 } 583 584 // 585 // We have more work to do. We have a starting Fcb which we own shared. 586 // We also have the remaining name to parse. Walk through the name 587 // component by component looking for the full name. 588 // 589 590 // 591 // Our starting Fcb better be a directory. 592 // 593 594 if (!FlagOn( CurrentFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { 595 596 try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); 597 } 598 599 // 600 // If we can't wait then post this request. 601 // 602 603 if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { 604 605 CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 606 } 607 608 // 609 // Make sure the final name has no version string. 610 // 611 612 FinalName.VersionString.Length = 0; 613 614 while (TRUE) { 615 616 ShortNameMatch = FALSE; 617 618 // 619 // Split off the next component from the name. 620 // 621 622 CdDissectName( IrpContext, 623 &RemainingName.FileName, 624 &FinalName.FileName ); 625 626 // 627 // Go ahead and look this entry up in the path table. 628 // 629 630 CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); 631 CleanupCompoundPathEntry = TRUE; 632 633 FoundEntry = CdFindPathEntry( IrpContext, 634 CurrentFcb, 635 &FinalName, 636 IgnoreCase, 637 &CompoundPathEntry ); 638 639 // 640 // If we didn't find the entry then check if the current name 641 // is a possible short name. 642 // 643 644 if (!FoundEntry) { 645 646 ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &FinalName.FileName ); 647 648 // 649 // If there is an embedded short name offset then look for the 650 // matching long name in the directory. 651 // 652 653 if (ShortNameDirentOffset != MAXULONG) { 654 655 if (CleanupFileContext) { 656 657 CdCleanupFileContext( IrpContext, &FileContext ); 658 } 659 660 CdInitializeFileContext( IrpContext, &FileContext ); 661 CleanupFileContext = TRUE; 662 663 FoundEntry = CdFindFileByShortName( IrpContext, 664 CurrentFcb, 665 &FinalName, 666 IgnoreCase, 667 ShortNameDirentOffset, 668 &FileContext ); 669 670 // 671 // If we found an entry and it is a directory then look 672 // this up in the path table. 673 // 674 675 if (FoundEntry) { 676 677 ShortNameMatch = TRUE; 678 679 if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags, 680 CD_ATTRIBUTE_DIRECTORY )) { 681 682 CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); 683 CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); 684 685 FoundEntry = CdFindPathEntry( IrpContext, 686 CurrentFcb, 687 &FileContext.InitialDirent->Dirent.CdCaseFileName, 688 IgnoreCase, 689 &CompoundPathEntry ); 690 691 // 692 // We better find this entry. 693 // 694 695 if (!FoundEntry) { 696 697 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 698 } 699 700 // 701 // Upcase the name with the short name if case 702 // insensitive. 703 // 704 705 if (IgnoreCase) { 706 707 CdUpcaseName( IrpContext, &FinalName, &FinalName ); 708 } 709 710 // 711 // We found a matching file. If we are at the last 712 // entry then break out of the loop and open the 713 // file below. Otherwise we return an error. 714 // 715 716 } else if (RemainingName.FileName.Length == 0) { 717 718 // 719 // Break out of the loop. We will process the dirent 720 // below. 721 // 722 723 MatchingName = &FileContext.ShortName; 724 break; 725 726 } else { 727 728 try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); 729 } 730 } 731 } 732 733 // 734 // We didn't find the name in either the path table or as 735 // a short name in a directory. If the remaining name 736 // length is zero then break out of the loop to search 737 // the directory. 738 // 739 740 if (!FoundEntry) { 741 742 if (RemainingName.FileName.Length == 0) { 743 744 break; 745 746 // 747 // Otherwise this path could not be cracked. 748 // 749 750 } else { 751 752 try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); 753 } 754 } 755 } 756 757 // 758 // If this is an ignore case open then copy the exact case 759 // in the file object name. If it was a short name match then 760 // the name must be upcase already. 761 // 762 763 if (IgnoreCase && !ShortNameMatch) { 764 765 RtlCopyMemory( FinalName.FileName.Buffer, 766 CompoundPathEntry.PathEntry.CdDirName.FileName.Buffer, 767 CompoundPathEntry.PathEntry.CdDirName.FileName.Length ); 768 } 769 770 // 771 // If we have found the last component then open this as a directory 772 // and return to our caller. 773 // 774 775 if (RemainingName.FileName.Length == 0) { 776 777 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { 778 779 try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); 780 } 781 782 // 783 // The only create disposition we allow is OPEN. 784 // 785 786 if ((CreateDisposition != FILE_OPEN) && 787 (CreateDisposition != FILE_OPEN_IF)) { 788 789 try_return( Status = STATUS_ACCESS_DENIED ); 790 } 791 792 try_return( Status = CdOpenDirectoryFromPathEntry( IrpContext, 793 IrpSp, 794 Vcb, 795 &CurrentFcb, 796 &FinalName, 797 IgnoreCase, 798 ShortNameMatch, 799 &CompoundPathEntry.PathEntry, 800 TRUE, 801 RelatedCcb )); 802 } 803 804 // 805 // Otherwise open an Fcb for this intermediate index Fcb. 806 // 807 808 CdOpenDirectoryFromPathEntry( IrpContext, 809 IrpSp, 810 Vcb, 811 &CurrentFcb, 812 &FinalName, 813 IgnoreCase, 814 ShortNameMatch, 815 &CompoundPathEntry.PathEntry, 816 FALSE, 817 NULL ); 818 819 CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); 820 CleanupCompoundPathEntry = FALSE; 821 } 822 823 // 824 // We need to scan the current directory for a matching file name 825 // if we don't already have one. 826 // 827 828 if (!FoundEntry) { 829 830 if (CleanupFileContext) { 831 832 CdCleanupFileContext( IrpContext, &FileContext ); 833 } 834 835 CdInitializeFileContext( IrpContext, &FileContext ); 836 CleanupFileContext = TRUE; 837 838 // 839 // Split our search name into separate components. 840 // 841 842 CdConvertNameToCdName( IrpContext, &FinalName ); 843 844 FoundEntry = CdFindFile( IrpContext, 845 CurrentFcb, 846 &FinalName, 847 IgnoreCase, 848 &FileContext, 849 &MatchingName ); 850 } 851 852 // 853 // If we didn't find a match then check if the name is invalid to 854 // determine which error code to return. 855 // 856 857 if (!FoundEntry) { 858 859 if ((CreateDisposition == FILE_OPEN) || 860 (CreateDisposition == FILE_OVERWRITE)) { 861 862 try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND ); 863 } 864 865 // 866 // Any other operation return STATUS_ACCESS_DENIED. 867 // 868 869 try_return( Status = STATUS_ACCESS_DENIED ); 870 } 871 872 // 873 // If this is a directory then the disk is corrupt because it wasn't 874 // in the Path Table. 875 // 876 877 if (FlagOn( FileContext.InitialDirent->Dirent.Flags, CD_ATTRIBUTE_DIRECTORY )) { 878 879 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); 880 } 881 882 // 883 // Make sure our opener didn't want a directory. 884 // 885 886 if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) || 887 FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { 888 889 try_return( Status = STATUS_NOT_A_DIRECTORY ); 890 } 891 892 // 893 // The only create disposition we allow is OPEN. 894 // 895 896 if ((CreateDisposition != FILE_OPEN) && 897 (CreateDisposition != FILE_OPEN_IF)) { 898 899 try_return( Status = STATUS_ACCESS_DENIED ); 900 } 901 902 // 903 // If this is an ignore case open then copy the exact case 904 // in the file object name. Any version portion should 905 // already be upcased. 906 // 907 908 if (IgnoreCase) { 909 910 RtlCopyMemory( FinalName.FileName.Buffer, 911 MatchingName->FileName.Buffer, 912 MatchingName->FileName.Length ); 913 } 914 915 // 916 // Open the file using the file context. We already have the 917 // first and last dirents. 918 // 919 920 try_return( Status = CdOpenFileFromFileContext( IrpContext, 921 IrpSp, 922 Vcb, 923 &CurrentFcb, 924 &FinalName, 925 IgnoreCase, 926 (BOOLEAN) (MatchingName == &FileContext.ShortName), 927 &FileContext, 928 RelatedCcb )); 929 930 try_exit: NOTHING; 931 } _SEH2_FINALLY { 932 933 // 934 // Cleanup the PathEntry if initialized. 935 // 936 937 if (CleanupCompoundPathEntry) { 938 939 CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); 940 } 941 942 // 943 // Cleanup the FileContext if initialized. 944 // 945 946 if (CleanupFileContext) { 947 948 CdCleanupFileContext( IrpContext, &FileContext ); 949 } 950 951 // 952 // The result of this open could be success, pending or some error 953 // condition. 954 // 955 956 if (_SEH2_AbnormalTermination()) { 957 958 959 // 960 // In the error path we start by calling our teardown routine if we 961 // have a CurrentFcb and its not the volume Dasd Fcb. 962 // 963 964 if ((CurrentFcb != NULL) && 965 (CurrentFcb != Vcb->VolumeDasdFcb)) { 966 967 BOOLEAN RemovedFcb; 968 969 CdTeardownStructures( IrpContext, CurrentFcb, &RemovedFcb ); 970 971 if (RemovedFcb) { 972 973 CurrentFcb = NULL; 974 } 975 } 976 977 // 978 // No need to complete the request. 979 // 980 981 IrpContext = NULL; 982 Irp = NULL; 983 984 // 985 // If we posted this request through the oplock package we need 986 // to show that there is no reason to complete the request. 987 // 988 989 } else if (Status == STATUS_PENDING) { 990 991 IrpContext = NULL; 992 Irp = NULL; 993 } 994 995 // 996 // Release the Current Fcb if still acquired. 997 // 998 999 if (CurrentFcb != NULL) { 1000 _Analysis_assume_lock_held_(CurrentFcb->FcbNonpaged->FcbResource); 1001 CdReleaseFcb( IrpContext, CurrentFcb ); 1002 } 1003 1004 // 1005 // Release the Vcb. 1006 // 1007 1008 CdReleaseVcb( IrpContext, Vcb ); 1009 1010 // 1011 // Call our completion routine. It will handle the case where either 1012 // the Irp and/or IrpContext are gone. 1013 // 1014 1015 CdCompleteRequest( IrpContext, Irp, Status ); 1016 } _SEH2_END; 1017 1018 return Status; 1019 } 1020 1021 1022 // 1023 // Local support routine 1024 // 1025 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedCcb, _In_)) 1026 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedCcb, _In_opt_)) 1027 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedFileName, _In_)) 1028 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedFileName, _In_opt_)) 1029 NTSTATUS 1030 CdNormalizeFileNames ( 1031 _Inout_ PIRP_CONTEXT IrpContext, 1032 _In_ PVCB Vcb, 1033 _In_ BOOLEAN OpenByFileId, 1034 _In_ BOOLEAN IgnoreCase, 1035 _In_ TYPE_OF_OPEN RelatedTypeOfOpen, 1036 PCCB RelatedCcb, 1037 PUNICODE_STRING RelatedFileName, 1038 _Inout_ PUNICODE_STRING FileName, 1039 _Inout_ PCD_NAME RemainingName 1040 ) 1041 1042 /*++ 1043 1044 Routine Description: 1045 1046 This routine is called to store the full name and upcased name into the 1047 filename buffer. We only upcase the portion yet to parse. We also 1048 check for a trailing backslash and lead-in double backslashes. This 1049 routine also verifies the mode of the related open against the name 1050 currently in the filename. 1051 1052 Arguments: 1053 1054 Vcb - Vcb for this volume. 1055 1056 OpenByFileId - Indicates if the filename should be a 64 bit FileId. 1057 1058 IgnoreCase - Indicates if this open is a case-insensitive operation. 1059 1060 RelatedTypeOfOpen - Indicates the type of the related file object. 1061 1062 RelatedCcb - Ccb for the related open. Ignored if no relative open. 1063 1064 RelatedFileName - FileName buffer for related open. Ignored if no 1065 relative open. 1066 1067 FileName - FileName to update in this routine. The name should 1068 either be a 64-bit FileId or a Unicode string. 1069 1070 RemainingName - Name with the remaining portion of the name. This 1071 will begin after the related name and any separator. For a 1072 non-relative open we also step over the initial separator. 1073 1074 Return Value: 1075 1076 NTSTATUS - STATUS_SUCCESS if the names are OK, appropriate error code 1077 otherwise. 1078 1079 --*/ 1080 1081 { 1082 ULONG RemainingNameLength = 0; 1083 ULONG RelatedNameLength = 0; 1084 ULONG SeparatorLength = 0; 1085 1086 ULONG BufferLength; 1087 1088 UNICODE_STRING NewFileName; 1089 1090 PAGED_CODE(); 1091 1092 // 1093 // If this is the first pass then we need to build the full name and 1094 // check for name compatibility. 1095 // 1096 1097 if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME )) { 1098 1099 // 1100 // Deal with the regular file name case first. 1101 // 1102 1103 if (!OpenByFileId) { 1104 1105 // 1106 // This is here because the Win32 layer can't avoid sending me double 1107 // beginning backslashes. 1108 // 1109 1110 if ((FileName->Length > sizeof( WCHAR )) && 1111 (FileName->Buffer[1] == L'\\') && 1112 (FileName->Buffer[0] == L'\\')) { 1113 1114 // 1115 // If there are still two beginning backslashes, the name is bogus. 1116 // 1117 1118 if ((FileName->Length > 2 * sizeof( WCHAR )) && 1119 (FileName->Buffer[2] == L'\\')) { 1120 1121 return STATUS_OBJECT_NAME_INVALID; 1122 } 1123 1124 // 1125 // Slide the name down in the buffer. 1126 // 1127 1128 FileName->Length -= sizeof( WCHAR ); 1129 1130 RtlMoveMemory( FileName->Buffer, 1131 FileName->Buffer + 1, 1132 FileName->Length ); 1133 } 1134 1135 // 1136 // Check for a trailing backslash. Don't strip off if only character 1137 // in the full name or for relative opens where this is illegal. 1138 // 1139 1140 if (((FileName->Length > sizeof( WCHAR)) || 1141 ((FileName->Length == sizeof( WCHAR )) && (RelatedTypeOfOpen == UserDirectoryOpen))) && 1142 (FileName->Buffer[ (FileName->Length/2) - 1 ] == L'\\')) { 1143 1144 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ); 1145 FileName->Length -= sizeof( WCHAR ); 1146 } 1147 1148 // 1149 // Remember the length we need for this portion of the name. 1150 // 1151 1152 RemainingNameLength = FileName->Length; 1153 1154 // 1155 // If this is a related file object then we verify the compatibility 1156 // of the name in the file object with the relative file object. 1157 // 1158 1159 if (RelatedTypeOfOpen != UnopenedFileObject) { 1160 1161 // 1162 // If the filename length was zero then it must be legal. 1163 // If there are characters then check with the related 1164 // type of open. 1165 // 1166 1167 if (FileName->Length != 0) { 1168 1169 // 1170 // The name length must always be zero for a volume open. 1171 // 1172 1173 if (RelatedTypeOfOpen <= UserVolumeOpen) { 1174 1175 return STATUS_INVALID_PARAMETER; 1176 1177 // 1178 // The remaining name cannot begin with a backslash. 1179 // 1180 1181 } else if (FileName->Buffer[0] == L'\\' ) { 1182 1183 return STATUS_INVALID_PARAMETER; 1184 1185 // 1186 // If the related file is a user file then there 1187 // is no file with this path. 1188 // 1189 1190 } else if (RelatedTypeOfOpen == UserFileOpen) { 1191 1192 return STATUS_OBJECT_PATH_NOT_FOUND; 1193 } 1194 } 1195 1196 // 1197 // Remember the length of the related name when building 1198 // the full name. We leave the RelatedNameLength and 1199 // SeparatorLength at zero if the relative file is opened 1200 // by Id. 1201 // 1202 1203 if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) { 1204 1205 // 1206 // Add a separator if the name length is non-zero 1207 // unless the relative Fcb is at the root. 1208 // 1209 1210 if ((FileName->Length != 0) && 1211 (RelatedCcb->Fcb != Vcb->RootIndexFcb)) { 1212 1213 SeparatorLength = sizeof( WCHAR ); 1214 } 1215 1216 RelatedNameLength = RelatedFileName->Length; 1217 } 1218 1219 // 1220 // The full name is already in the filename. It must either 1221 // be length 0 or begin with a backslash. 1222 // 1223 1224 } else if (FileName->Length != 0) { 1225 1226 if (FileName->Buffer[0] != L'\\') { 1227 1228 return STATUS_INVALID_PARAMETER; 1229 } 1230 1231 // 1232 // We will want to trim the leading backslash from the 1233 // remaining name we return. 1234 // 1235 1236 RemainingNameLength -= sizeof( WCHAR ); 1237 SeparatorLength = sizeof( WCHAR ); 1238 } 1239 1240 // 1241 // Now see if the buffer is large enough to hold the full name. 1242 // 1243 1244 BufferLength = RelatedNameLength + SeparatorLength + RemainingNameLength; 1245 1246 // 1247 // Check for an overflow of the maximum filename size. 1248 // 1249 1250 if (BufferLength > MAXUSHORT) { 1251 1252 return STATUS_INVALID_PARAMETER; 1253 } 1254 1255 // 1256 // Now see if we need to allocate a new buffer. 1257 // 1258 1259 if (FileName->MaximumLength < BufferLength) { 1260 1261 NewFileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, 1262 BufferLength, 1263 TAG_FILE_NAME ); 1264 1265 NewFileName.MaximumLength = (USHORT) BufferLength; 1266 1267 } else { 1268 1269 NewFileName.Buffer = FileName->Buffer; 1270 NewFileName.MaximumLength = FileName->MaximumLength; 1271 } 1272 1273 // 1274 // If there is a related name then we need to slide the remaining bytes up and 1275 // insert the related name. Otherwise the name is in the correct position 1276 // already. 1277 // 1278 1279 if (RelatedNameLength != 0) { 1280 1281 // 1282 // Store the remaining name in its correct position. 1283 // 1284 1285 if (RemainingNameLength != 0) { 1286 1287 RtlMoveMemory( Add2Ptr( NewFileName.Buffer, RelatedNameLength + SeparatorLength, PVOID ), 1288 FileName->Buffer, 1289 RemainingNameLength ); 1290 } 1291 1292 RtlCopyMemory( NewFileName.Buffer, 1293 RelatedFileName->Buffer, 1294 RelatedNameLength ); 1295 1296 // 1297 // Add the separator if needed. 1298 // 1299 1300 if (SeparatorLength != 0) { 1301 1302 *(Add2Ptr( NewFileName.Buffer, RelatedNameLength, PWCHAR )) = L'\\'; 1303 } 1304 1305 // 1306 // Update the filename value we got from the user. 1307 // 1308 1309 if (NewFileName.Buffer != FileName->Buffer) { 1310 1311 if (FileName->Buffer != NULL) { 1312 1313 CdFreePool( &FileName->Buffer ); 1314 } 1315 1316 FileName->Buffer = NewFileName.Buffer; 1317 FileName->MaximumLength = NewFileName.MaximumLength; 1318 } 1319 1320 // 1321 // Copy the name length to the user's filename. 1322 // 1323 1324 FileName->Length = (USHORT) (RelatedNameLength + SeparatorLength + RemainingNameLength); 1325 } 1326 1327 // 1328 // Now update the remaining name to parse. 1329 // 1330 1331 RemainingName->FileName.MaximumLength = 1332 RemainingName->FileName.Length = (USHORT) RemainingNameLength; 1333 RemainingName->VersionString.Length = 0; 1334 1335 RemainingName->FileName.Buffer = Add2Ptr( FileName->Buffer, 1336 RelatedNameLength + SeparatorLength, 1337 PWCHAR ); 1338 1339 // 1340 // Upcase the name if necessary. 1341 // 1342 1343 if (IgnoreCase && (RemainingNameLength != 0)) { 1344 1345 CdUpcaseName( IrpContext, 1346 RemainingName, 1347 RemainingName ); 1348 } 1349 1350 // 1351 // Do a quick check to make sure there are no wildcards. 1352 // 1353 #ifdef _MSC_VER 1354 #pragma prefast(push) 1355 #pragma prefast(suppress:26000, "RemainingName->FileName.Buffer = FileName.Buffer + (RelatedNameLength + SeparatorLength); FileName.MaximumLength < (RelatedNameLength + SeparatorLength + RemainingNameLength).") 1356 #endif 1357 if (FsRtlDoesNameContainWildCards( &RemainingName->FileName )) { 1358 #ifdef _MSC_VER 1359 #pragma prefast(pop) 1360 #endif 1361 1362 return STATUS_OBJECT_NAME_INVALID; 1363 } 1364 1365 // 1366 // For the open by file Id case we verify the name really contains 1367 // a 64 bit value. 1368 // 1369 1370 } else { 1371 1372 // 1373 // Check for validity of the buffer. 1374 // 1375 1376 if (FileName->Length != sizeof( FILE_ID )) { 1377 1378 return STATUS_INVALID_PARAMETER; 1379 } 1380 } 1381 1382 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME ); 1383 1384 // 1385 // If we are in the retry path then the full name is already in the 1386 // file object name. If this is a case-sensitive operation then 1387 // we need to upcase the name from the end of any related file name already stored 1388 // there. 1389 // 1390 1391 } else { 1392 1393 // 1394 // Assume there is no relative name. 1395 // 1396 1397 RemainingName->FileName = *FileName; 1398 RemainingName->VersionString.Length = 0; 1399 1400 // 1401 // Nothing to do if the name length is zero. 1402 // 1403 1404 if (RemainingName->FileName.Length != 0) { 1405 1406 // 1407 // If there is a relative name then we need to walk past it. 1408 // 1409 1410 if (RelatedTypeOfOpen != UnopenedFileObject) { 1411 1412 // 1413 // Nothing to walk past if the RelatedCcb is opened by FileId. 1414 // 1415 1416 1417 if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) { 1418 1419 // 1420 // Related file name is a proper prefix of the full name. 1421 // We step over the related name and if we are then 1422 // pointing at a separator character we step over that. 1423 // 1424 1425 RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer, 1426 RelatedFileName->Length, 1427 PWCHAR ); 1428 1429 RemainingName->FileName.Length -= RelatedFileName->Length; 1430 } 1431 } 1432 1433 // 1434 // If we are pointing at a separator character then step past that. 1435 // 1436 1437 if (RemainingName->FileName.Length != 0) { 1438 1439 if (*(RemainingName->FileName.Buffer) == L'\\') { 1440 1441 RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer, 1442 sizeof( WCHAR ), 1443 PWCHAR ); 1444 1445 RemainingName->FileName.Length -= sizeof( WCHAR ); 1446 } 1447 } 1448 } 1449 1450 // 1451 // Upcase the name if necessary. 1452 // 1453 1454 if (IgnoreCase && (RemainingName->FileName.Length != 0)) { 1455 1456 CdUpcaseName( IrpContext, 1457 RemainingName, 1458 RemainingName ); 1459 } 1460 } 1461 1462 #ifdef _MSC_VER 1463 #pragma prefast(push) 1464 #pragma prefast(suppress:26030, "RemainingName->FileName.Buffer = FileName.Buffer + (RelatedNameLength + SeparatorLength); FileName.MaximumLength < (RelatedNameLength + SeparatorLength + RemainingNameLength).") 1465 #endif 1466 return STATUS_SUCCESS; 1467 #ifdef _MSC_VER 1468 #pragma prefast(pop) 1469 #endif 1470 } 1471 1472 1473 // 1474 // Local support routine 1475 // 1476 1477 _Requires_lock_held_(_Global_critical_region_) 1478 _Acquires_exclusive_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) 1479 NTSTATUS 1480 CdOpenByFileId ( 1481 _In_ PIRP_CONTEXT IrpContext, 1482 _In_ PIO_STACK_LOCATION IrpSp, 1483 _In_ PVCB Vcb, 1484 _Inout_ PFCB *CurrentFcb 1485 ) 1486 1487 /*++ 1488 1489 Routine Description: 1490 1491 This routine is called to open a file by the FileId. The file Id is in 1492 the FileObject name buffer and has been verified to be 64 bits. 1493 1494 We extract the Id number and then check to see whether we are opening a 1495 file or directory and compare that with the create options. If this 1496 generates no error then optimistically look up the Fcb in the Fcb Table. 1497 1498 If we don't find the Fcb then we need to carefully verify there is a file 1499 at this offset. First check whether the Parent Fcb is in the table. If 1500 not then lookup the parent at the path table offset given by file ID. 1501 1502 If found then build the Fcb from this entry and store the new Fcb in the 1503 tree. 1504 1505 We know have the parent Fcb. Do a directory scan to find the dirent at 1506 the given offset in this stream. This must point to the first entry 1507 of a valid file. 1508 1509 Finally we call our worker routine to complete the open on this Fcb. 1510 1511 Arguments: 1512 1513 IrpSp - Stack location within the create Irp. 1514 1515 Vcb - Vcb for this volume. 1516 1517 CurrentFcb - Address to store the Fcb for this open. We only store the 1518 CurrentFcb here when we have acquired it so our caller knows to 1519 free or deallocate it. 1520 1521 Return Value: 1522 1523 NTSTATUS - Status indicating the result of the operation. 1524 1525 --*/ 1526 1527 { 1528 NTSTATUS Status = STATUS_ACCESS_DENIED; 1529 1530 BOOLEAN UnlockVcb = FALSE; 1531 BOOLEAN Found; 1532 1533 ULONG StreamOffset; 1534 1535 NODE_TYPE_CODE NodeTypeCode; 1536 TYPE_OF_OPEN TypeOfOpen; 1537 1538 FILE_ENUM_CONTEXT FileContext; 1539 BOOLEAN CleanupFileContext = FALSE; 1540 1541 COMPOUND_PATH_ENTRY CompoundPathEntry = {{0}};/* ReactOS Change: GCC "missing braces around initializer" */ 1542 BOOLEAN CleanupCompoundPathEntry = FALSE; 1543 1544 FILE_ID FileId; 1545 FILE_ID ParentFileId; 1546 1547 PFCB NextFcb; 1548 1549 PAGED_CODE(); 1550 1551 // 1552 // Extract the FileId from the FileObject. 1553 // 1554 1555 RtlCopyMemory( &FileId, IrpSp->FileObject->FileName.Buffer, sizeof( FILE_ID )); 1556 1557 // 1558 // Use a try-finally to facilitate cleanup. 1559 // 1560 1561 _SEH2_TRY { 1562 1563 // 1564 // Go ahead and figure out the TypeOfOpen and NodeType. We can 1565 // get these from the input FileId. 1566 // 1567 1568 if (CdFidIsDirectory( FileId )) { 1569 1570 TypeOfOpen = UserDirectoryOpen; 1571 NodeTypeCode = CDFS_NTC_FCB_INDEX; 1572 1573 // 1574 // If the offset isn't zero then the file Id is bad. 1575 // 1576 1577 if (CdQueryFidDirentOffset( FileId ) != 0) { 1578 1579 try_return( Status = STATUS_INVALID_PARAMETER ); 1580 } 1581 1582 } else { 1583 1584 TypeOfOpen = UserFileOpen; 1585 NodeTypeCode = CDFS_NTC_FCB_DATA; 1586 } 1587 1588 // 1589 // Acquire the Vcb and check if there is already an Fcb. 1590 // If not we will need to carefully verify the Fcb. 1591 // We will post the request if we don't find the Fcb and this 1592 // request can't wait. 1593 // 1594 1595 CdLockVcb( IrpContext, Vcb ); 1596 UnlockVcb = TRUE; 1597 1598 NextFcb = CdLookupFcbTable( IrpContext, Vcb, FileId ); 1599 1600 if (NextFcb == NULL) { 1601 1602 // 1603 // Get the path table offset from the file id. 1604 // 1605 1606 StreamOffset = CdQueryFidPathTableOffset( FileId ); 1607 1608 // 1609 // Build the parent FileId for this and try looking it 1610 // up in the PathTable. 1611 // 1612 1613 CdSetFidDirentOffset( ParentFileId, 0 ); 1614 CdSetFidPathTableOffset( ParentFileId, StreamOffset ); 1615 CdFidSetDirectory( ParentFileId ); 1616 1617 NextFcb = CdLookupFcbTable( IrpContext, Vcb, ParentFileId ); 1618 1619 // 1620 // If not present then walk through the PathTable to this point. 1621 // 1622 1623 if (NextFcb == NULL) { 1624 1625 CdUnlockVcb( IrpContext, Vcb ); 1626 UnlockVcb = FALSE; 1627 1628 // 1629 // Check that the path table offset lies within the path 1630 // table. 1631 // 1632 1633 if (StreamOffset > Vcb->PathTableFcb->FileSize.LowPart) { 1634 1635 try_return( Status = STATUS_INVALID_PARAMETER ); 1636 } 1637 1638 CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); 1639 CleanupCompoundPathEntry = TRUE; 1640 1641 // 1642 // Start at the first entry in the PathTable. 1643 // 1644 1645 CdLookupPathEntry( IrpContext, 1646 Vcb->PathTableFcb->StreamOffset, 1647 1, 1648 TRUE, 1649 &CompoundPathEntry ); 1650 1651 // 1652 // Continue looking until we have passed our target offset. 1653 // 1654 1655 while (TRUE) { 1656 1657 // 1658 // Move to the next entry. 1659 // 1660 1661 Found = CdLookupNextPathEntry( IrpContext, 1662 &CompoundPathEntry.PathContext, 1663 &CompoundPathEntry.PathEntry ); 1664 1665 // 1666 // If we didn't find the entry or are beyond it then the 1667 // input Id is invalid. 1668 // 1669 1670 if (!Found || 1671 (CompoundPathEntry.PathEntry.PathTableOffset > StreamOffset)) { 1672 1673 try_return( Status = STATUS_INVALID_PARAMETER ); 1674 } 1675 } 1676 1677 // 1678 // If the FileId specified a directory then we have found 1679 // the entry. Make sure our caller wanted to open a directory. 1680 // 1681 1682 if ((TypeOfOpen == UserDirectoryOpen) && 1683 FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { 1684 1685 try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); 1686 } 1687 1688 // 1689 // Lock the Vcb and create the Fcb if necessary. 1690 // 1691 1692 CdLockVcb( IrpContext, Vcb ); 1693 UnlockVcb = TRUE; 1694 1695 NextFcb = CdCreateFcb( IrpContext, ParentFileId, NodeTypeCode, &Found ); 1696 1697 // 1698 // It's possible that someone got in here ahead of us. 1699 // 1700 1701 if (!Found) { 1702 1703 CdInitializeFcbFromPathEntry( IrpContext, 1704 NextFcb, 1705 NULL, 1706 &CompoundPathEntry.PathEntry ); 1707 } 1708 1709 // 1710 // If the user wanted to open a directory then we have found 1711 // it. Store this Fcb into the CurrentFcb and skip the 1712 // directory scan. 1713 // 1714 1715 if (TypeOfOpen == UserDirectoryOpen) { 1716 1717 *CurrentFcb = NextFcb; 1718 NextFcb = NULL; 1719 } 1720 } 1721 1722 // 1723 // Perform the directory scan if we don't already have our target. 1724 // 1725 1726 if (NextFcb != NULL) { 1727 1728 // 1729 // Acquire the parent. We currently own the Vcb lock so 1730 // do this without waiting first. 1731 // 1732 1733 if (!CdAcquireFcbExclusive( IrpContext, 1734 NextFcb, 1735 TRUE )) { 1736 1737 NextFcb->FcbReference += 1; 1738 CdUnlockVcb( IrpContext, Vcb ); 1739 1740 CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); 1741 1742 CdLockVcb( IrpContext, Vcb ); 1743 NextFcb->FcbReference -= 1; 1744 CdUnlockVcb( IrpContext, Vcb ); 1745 1746 } else { 1747 1748 CdUnlockVcb( IrpContext, Vcb ); 1749 } 1750 1751 UnlockVcb = FALSE; 1752 1753 // 1754 // Set up the CurrentFcb pointers. We know there was 1755 // no previous parent in this case. 1756 // 1757 1758 *CurrentFcb = NextFcb; 1759 1760 // 1761 // Calculate the offset in the stream. 1762 // 1763 1764 StreamOffset = CdQueryFidDirentOffset( FileId ); 1765 1766 // 1767 // Create the stream file if it doesn't exist. This will update 1768 // the Fcb with the size from the self entry. 1769 // 1770 1771 CdVerifyOrCreateDirStreamFile( IrpContext, NextFcb); 1772 1773 // 1774 // If our offset is beyond the end of the directory then the 1775 // FileId is invalid. 1776 // 1777 1778 if (StreamOffset > NextFcb->FileSize.LowPart) { 1779 1780 try_return( Status = STATUS_INVALID_PARAMETER ); 1781 } 1782 1783 // 1784 // Otherwise position ourselves at the self entry and walk 1785 // through dirent by dirent until this location is found. 1786 // 1787 1788 CdInitializeFileContext( IrpContext, &FileContext ); 1789 CdLookupInitialFileDirent( IrpContext, 1790 NextFcb, 1791 &FileContext, 1792 NextFcb->StreamOffset ); 1793 1794 CleanupFileContext = TRUE; 1795 1796 while (TRUE) { 1797 1798 // 1799 // Move to the first entry of the next file. 1800 // 1801 1802 Found = CdLookupNextInitialFileDirent( IrpContext, 1803 NextFcb, 1804 &FileContext ); 1805 1806 // 1807 // If we didn't find the entry or are beyond it then the 1808 // input Id is invalid. 1809 // 1810 1811 if (!Found || 1812 (FileContext.InitialDirent->Dirent.DirentOffset > StreamOffset)) { 1813 1814 try_return( Status = STATUS_INVALID_PARAMETER ); 1815 } 1816 } 1817 1818 // 1819 // This better not be a directory. Directory FileIds must 1820 // refer to the self entry for directories. 1821 // 1822 1823 if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags, 1824 CD_ATTRIBUTE_DIRECTORY )) { 1825 1826 try_return( Status = STATUS_INVALID_PARAMETER ); 1827 } 1828 1829 // 1830 // Check that our caller wanted to open a file. 1831 // 1832 1833 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { 1834 1835 try_return( Status = STATUS_NOT_A_DIRECTORY ); 1836 } 1837 1838 // 1839 // Otherwise we want to collect all of the dirents for this file 1840 // and create an Fcb with this. 1841 // 1842 1843 CdLookupLastFileDirent( IrpContext, NextFcb, &FileContext ); 1844 1845 CdLockVcb( IrpContext, Vcb ); 1846 UnlockVcb = TRUE; 1847 1848 NextFcb = CdCreateFcb( IrpContext, FileId, NodeTypeCode, &Found ); 1849 1850 // 1851 // It's possible that someone has since created this Fcb since we 1852 // first checked. If so then can simply use this. Otherwise 1853 // we need to initialize a new Fcb and attach it to our parent 1854 // and insert it into the Fcb Table. 1855 // 1856 1857 if (!Found) { 1858 1859 CdInitializeFcbFromFileContext( IrpContext, 1860 NextFcb, 1861 *CurrentFcb, 1862 &FileContext ); 1863 } 1864 } 1865 1866 // 1867 // We have the Fcb. Check that the type of the file is compatible with 1868 // the desired type of file to open. 1869 // 1870 1871 } else { 1872 1873 if (FlagOn( NextFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { 1874 1875 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { 1876 1877 try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); 1878 } 1879 1880 } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { 1881 1882 try_return( Status = STATUS_NOT_A_DIRECTORY ); 1883 } 1884 } 1885 1886 // 1887 // If we have a the previous Fcb and have inserted the next Fcb into 1888 // the Fcb Table. It is safe to release the current Fcb if present 1889 // since it is referenced through the child Fcb. 1890 // 1891 1892 if (*CurrentFcb != NULL) { 1893 1894 CdReleaseFcb( IrpContext, *CurrentFcb ); 1895 } 1896 1897 // 1898 // We now know the Fcb and currently hold the Vcb lock. 1899 // Try to acquire this Fcb without waiting. Otherwise we 1900 // need to reference it, drop the Vcb, acquire the Fcb and 1901 // then dereference the Fcb. 1902 // 1903 1904 if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { 1905 1906 NextFcb->FcbReference += 1; 1907 1908 CdUnlockVcb( IrpContext, Vcb ); 1909 1910 CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); 1911 1912 CdLockVcb( IrpContext, Vcb ); 1913 NextFcb->FcbReference -= 1; 1914 CdUnlockVcb( IrpContext, Vcb ); 1915 1916 } else { 1917 1918 CdUnlockVcb( IrpContext, Vcb ); 1919 } 1920 1921 UnlockVcb = FALSE; 1922 1923 // 1924 // Move to this Fcb. 1925 // 1926 1927 *CurrentFcb = NextFcb; 1928 1929 // Lock object is acquired using internal state 1930 _Analysis_suppress_lock_checking_(NextFcb->FcbNonpaged->FcbResource); 1931 1932 // 1933 // Check the requested access on this Fcb. 1934 // 1935 1936 if (!CdIllegalFcbAccess( IrpContext, 1937 TypeOfOpen, 1938 IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { 1939 1940 // 1941 // Call our worker routine to complete the open. 1942 // 1943 1944 Status = CdCompleteFcbOpen( IrpContext, 1945 IrpSp, 1946 Vcb, 1947 CurrentFcb, 1948 TypeOfOpen, 1949 CCB_FLAG_OPEN_BY_ID, 1950 IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); 1951 1952 } 1953 1954 try_exit: NOTHING; 1955 } _SEH2_FINALLY { 1956 1957 if (UnlockVcb) { 1958 1959 CdUnlockVcb( IrpContext, Vcb ); 1960 } 1961 1962 if (CleanupFileContext) { 1963 1964 CdCleanupFileContext( IrpContext, &FileContext ); 1965 } 1966 1967 if (CleanupCompoundPathEntry) { 1968 1969 CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); 1970 } 1971 } _SEH2_END; 1972 1973 return Status; 1974 } 1975 1976 1977 // 1978 // Local support routine 1979 // 1980 1981 _Requires_lock_held_(_Global_critical_region_) 1982 NTSTATUS 1983 CdOpenExistingFcb ( 1984 _In_ PIRP_CONTEXT IrpContext, 1985 _In_ PIO_STACK_LOCATION IrpSp, 1986 _Inout_ PFCB *CurrentFcb, 1987 _In_ TYPE_OF_OPEN TypeOfOpen, 1988 _In_ BOOLEAN IgnoreCase, 1989 _In_opt_ PCCB RelatedCcb 1990 ) 1991 1992 /*++ 1993 1994 Routine Description: 1995 1996 This routine is called to open an Fcb which is already in the Fcb table. 1997 We will verify the access to the file and then call our worker routine 1998 to perform the final operations. 1999 2000 Arguments: 2001 2002 IrpSp - Pointer to the stack location for this open. 2003 2004 CurrentFcb - Address of Fcb to open. We will clear this if the Fcb 2005 is released here. 2006 2007 TypeOfOpen - Indicates whether we are opening a file, directory or volume. 2008 2009 IgnoreCase - Indicates if this open is case-insensitive. 2010 2011 RelatedCcb - Ccb for related file object if relative open. We use 2012 this when setting the Ccb flags for this open. It will tell 2013 us whether the name currently in the file object is relative or 2014 absolute. 2015 2016 Return Value: 2017 2018 NTSTATUS - Status indicating the result of the operation. 2019 2020 --*/ 2021 2022 { 2023 ULONG CcbFlags = 0; 2024 2025 NTSTATUS Status = STATUS_ACCESS_DENIED; 2026 2027 PAGED_CODE(); 2028 2029 // 2030 // Check that the desired access is legal. 2031 // 2032 2033 if (!CdIllegalFcbAccess( IrpContext, 2034 TypeOfOpen, 2035 IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { 2036 2037 // 2038 // Set the Ignore case. 2039 // 2040 2041 if (IgnoreCase) { 2042 2043 SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); 2044 } 2045 2046 // 2047 // Check the related Ccb to see if this was an OpenByFileId and 2048 // whether there was a version. 2049 // 2050 2051 if (ARGUMENT_PRESENT( RelatedCcb )) { 2052 2053 SetFlag( CcbFlags, FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_WITH_VERSION )); 2054 2055 2056 if (FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { 2057 2058 SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID ); 2059 } 2060 } 2061 2062 // 2063 // Call our worker routine to complete the open. 2064 // 2065 2066 Status = CdCompleteFcbOpen( IrpContext, 2067 IrpSp, 2068 (*CurrentFcb)->Vcb, 2069 CurrentFcb, 2070 TypeOfOpen, 2071 CcbFlags, 2072 IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); 2073 } 2074 2075 return Status; 2076 } 2077 2078 2079 // 2080 // Local support routine 2081 // 2082 2083 _Requires_lock_held_(_Global_critical_region_) 2084 _Acquires_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) 2085 NTSTATUS 2086 CdOpenDirectoryFromPathEntry ( 2087 _In_ PIRP_CONTEXT IrpContext, 2088 _In_ PIO_STACK_LOCATION IrpSp, 2089 _In_ PVCB Vcb, 2090 _Inout_ PFCB *CurrentFcb, 2091 _In_ PCD_NAME DirName, 2092 _In_ BOOLEAN IgnoreCase, 2093 _In_ BOOLEAN ShortNameMatch, 2094 _In_ PPATH_ENTRY PathEntry, 2095 _In_ BOOLEAN PerformUserOpen, 2096 _In_opt_ PCCB RelatedCcb 2097 ) 2098 2099 /*++ 2100 2101 Routine Description: 2102 2103 This routine is called to open a directory where the directory was found 2104 in the path table. This routine is called in the case where this is the 2105 file to open for the user and where this is an intermediate node in the 2106 full path to open. 2107 2108 We first check that the desired access is legal for a directory. Then we 2109 construct the FileId for this and do a check to see if it is the Fcb 2110 Table. It is always possible that either it was created since or simply 2111 wasn't in the prefix table at the time of the prefix table search. 2112 Initialize the Fcb and store into the FcbTable if not present. 2113 2114 Next we will add this to the prefix table of our parent if needed. 2115 2116 Once we know that the new Fcb has been initialized then we move our pointer 2117 in the tree down to this position. 2118 2119 This routine does not own the Vcb lock on entry. We must be sure to release 2120 it on exit. 2121 2122 Arguments: 2123 2124 IrpSp - Stack location for this request. 2125 2126 Vcb - Vcb for this volume. 2127 2128 CurrentFcb - On input this is the parent of the Fcb to open. On output we 2129 store the Fcb for the file being opened. 2130 2131 DirName - This is always the exact name used to reach this file. 2132 2133 IgnoreCase - Indicates the type of case match for the open. 2134 2135 ShortNameMatch - Indicates if we are opening via the short name. 2136 2137 PathEntry - Path entry for the entry found. 2138 2139 PerformUserOpen - TRUE if we are to open this for a user, FALSE otherwise. 2140 2141 RelatedCcb - RelatedCcb for relative file object used to make this open. 2142 2143 Return Value: 2144 2145 NTSTATUS - Status indicating the result of the operation. 2146 2147 --*/ 2148 2149 { 2150 ULONG CcbFlags = 0; 2151 FILE_ID FileId; 2152 2153 BOOLEAN UnlockVcb = FALSE; 2154 BOOLEAN FcbExisted; 2155 2156 PFCB NextFcb; 2157 PFCB ParentFcb = NULL; 2158 2159 NTSTATUS Status = STATUS_SUCCESS; 2160 2161 PAGED_CODE(); 2162 2163 // 2164 // Check for illegal access to this file. 2165 // 2166 2167 if (PerformUserOpen && 2168 CdIllegalFcbAccess( IrpContext, 2169 UserDirectoryOpen, 2170 IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { 2171 2172 return STATUS_ACCESS_DENIED; 2173 } 2174 2175 // 2176 // Use a try-finally to facilitate cleanup. 2177 // 2178 2179 _SEH2_TRY { 2180 2181 // 2182 // Check the related Ccb to see if this was an OpenByFileId. 2183 // 2184 2185 if (ARGUMENT_PRESENT( RelatedCcb ) && 2186 FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { 2187 2188 CcbFlags = CCB_FLAG_OPEN_RELATIVE_BY_ID; 2189 } 2190 2191 if (IgnoreCase) { 2192 2193 SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); 2194 } 2195 2196 // 2197 // Build the file Id for this file. 2198 // 2199 2200 FileId.QuadPart = 0; 2201 CdSetFidPathTableOffset( FileId, PathEntry->PathTableOffset ); 2202 CdFidSetDirectory( FileId ); 2203 2204 // 2205 // Lock the Vcb so we can examine the Fcb Table. 2206 // 2207 2208 CdLockVcb( IrpContext, Vcb ); 2209 UnlockVcb = TRUE; 2210 2211 // 2212 // Get the Fcb for this directory. 2213 // 2214 2215 NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_INDEX, &FcbExisted ); 2216 2217 // 2218 // If the Fcb was created here then initialize from the values in the 2219 // path table entry. 2220 // 2221 2222 if (!FcbExisted) { 2223 2224 CdInitializeFcbFromPathEntry( IrpContext, NextFcb, *CurrentFcb, PathEntry ); 2225 } 2226 2227 // 2228 // Now try to acquire the new Fcb without waiting. We will reference 2229 // the Fcb and retry with wait if unsuccessful. 2230 // 2231 2232 if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { 2233 2234 NextFcb->FcbReference += 1; 2235 2236 CdUnlockVcb( IrpContext, Vcb ); 2237 2238 CdReleaseFcb( IrpContext, *CurrentFcb ); 2239 CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); 2240 CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE ); 2241 2242 CdLockVcb( IrpContext, Vcb ); 2243 NextFcb->FcbReference -= 1; 2244 CdUnlockVcb( IrpContext, Vcb ); 2245 2246 } else { 2247 2248 // 2249 // Unlock the Vcb and move down to this new Fcb. Remember that we still 2250 // own the parent however. 2251 // 2252 2253 CdUnlockVcb( IrpContext, Vcb ); 2254 } 2255 2256 UnlockVcb = FALSE; 2257 2258 ParentFcb = *CurrentFcb; 2259 *CurrentFcb = NextFcb; 2260 2261 // Lock object is acquired using internal state 2262 _Analysis_suppress_lock_checking_(NextFcb->FcbNonpaged->FcbResource); 2263 2264 // 2265 // Store this name into the prefix table for the parent. 2266 // 2267 2268 if (ShortNameMatch) { 2269 2270 // 2271 // Make sure the exact case is always in the tree. 2272 // 2273 2274 CdInsertPrefix( IrpContext, 2275 NextFcb, 2276 DirName, 2277 FALSE, 2278 TRUE, 2279 ParentFcb ); 2280 2281 if (IgnoreCase) { 2282 2283 CdInsertPrefix( IrpContext, 2284 NextFcb, 2285 DirName, 2286 TRUE, 2287 TRUE, 2288 ParentFcb ); 2289 } 2290 2291 } else { 2292 2293 // 2294 // Make sure the exact case is always in the tree. 2295 // 2296 2297 CdInsertPrefix( IrpContext, 2298 NextFcb, 2299 &PathEntry->CdDirName, 2300 FALSE, 2301 FALSE, 2302 ParentFcb ); 2303 2304 if (IgnoreCase) { 2305 2306 CdInsertPrefix( IrpContext, 2307 NextFcb, 2308 &PathEntry->CdCaseDirName, 2309 TRUE, 2310 FALSE, 2311 ParentFcb ); 2312 } 2313 } 2314 2315 // 2316 // Release the parent Fcb at this point. 2317 // 2318 2319 CdReleaseFcb( IrpContext, ParentFcb ); 2320 ParentFcb = NULL; 2321 2322 // 2323 // Call our worker routine to complete the open. 2324 // 2325 2326 if (PerformUserOpen) { 2327 2328 Status = CdCompleteFcbOpen( IrpContext, 2329 IrpSp, 2330 Vcb, 2331 CurrentFcb, 2332 UserDirectoryOpen, 2333 CcbFlags, 2334 IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); 2335 } 2336 2337 } _SEH2_FINALLY { 2338 2339 // 2340 // Unlock the Vcb if held. 2341 // 2342 2343 if (UnlockVcb) { 2344 2345 CdUnlockVcb( IrpContext, Vcb ); 2346 } 2347 2348 // 2349 // Release the parent if held. 2350 // 2351 2352 if (ParentFcb != NULL) { 2353 2354 CdReleaseFcb( IrpContext, ParentFcb ); 2355 } 2356 } _SEH2_END; 2357 2358 return Status; 2359 } 2360 2361 2362 // 2363 // Local support routine 2364 // 2365 2366 _Requires_lock_held_(_Global_critical_region_) 2367 NTSTATUS 2368 CdOpenFileFromFileContext ( 2369 _In_ PIRP_CONTEXT IrpContext, 2370 _In_ PIO_STACK_LOCATION IrpSp, 2371 _In_ PVCB Vcb, 2372 _Inout_ PFCB *CurrentFcb, 2373 _In_ PCD_NAME FileName, 2374 _In_ BOOLEAN IgnoreCase, 2375 _In_ BOOLEAN ShortNameMatch, 2376 _In_ PFILE_ENUM_CONTEXT FileContext, 2377 _In_opt_ PCCB RelatedCcb 2378 ) 2379 2380 /*++ 2381 2382 Routine Description: 2383 2384 This routine is called to open a file where the file was found in a directory scan. 2385 This should only be for a file in the case since we will find the directories in the 2386 path table. 2387 2388 We first check that the desired access is legal for this file. Then we 2389 construct the FileId for this and do a check to see if it is the Fcb 2390 Table. It is always possible that either it was created since or simply 2391 wasn't in the prefix table at the time of the prefix table search. 2392 Initialize the Fcb and store into the FcbTable if not present. 2393 2394 Next we will add this to the prefix table of our parent if needed. 2395 2396 Once we know that the new Fcb has been initialized then we move our pointer 2397 in the tree down to this position. 2398 2399 This routine does not own the Vcb lock on entry. We must be sure to release 2400 it on exit. 2401 2402 Arguments: 2403 2404 IrpSp - Stack location for this request. 2405 2406 Vcb - Vcb for the current volume. 2407 2408 CurrentFcb - On input this is the parent of the Fcb to open. On output we 2409 store the Fcb for the file being opened. 2410 2411 FileName - This is always the exact name used to reach this file. 2412 2413 IgnoreCase - Indicates the type of case of CaseName above. 2414 2415 ShortNameMatch - Indicates if we are opening via the short name. 2416 2417 FileContext - This is the context used to find the file. 2418 2419 RelatedCcb - RelatedCcb for relative file object used to make this open. 2420 2421 Return Value: 2422 2423 NTSTATUS - Status indicating the result of the operation. 2424 2425 --*/ 2426 2427 { 2428 ULONG CcbFlags = 0; 2429 FILE_ID FileId; 2430 2431 BOOLEAN UnlockVcb = FALSE; 2432 BOOLEAN FcbExisted; 2433 2434 PFCB NextFcb; 2435 PFCB ParentFcb = NULL; 2436 2437 NTSTATUS Status = STATUS_SUCCESS; 2438 2439 PAGED_CODE(); 2440 2441 // 2442 // Check for illegal access to this file. 2443 // 2444 2445 if (CdIllegalFcbAccess( IrpContext, 2446 UserFileOpen, 2447 IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { 2448 2449 return STATUS_ACCESS_DENIED; 2450 } 2451 2452 // 2453 // Use a try-finally to facilitate cleanup. 2454 // 2455 2456 _SEH2_TRY { 2457 2458 // 2459 // Check if a version number was used to open this file. 2460 // 2461 2462 if (FileName->VersionString.Length != 0) { 2463 2464 SetFlag( CcbFlags, CCB_FLAG_OPEN_WITH_VERSION ); 2465 } 2466 2467 // 2468 // Check the related Ccb to see if this was an OpenByFileId. 2469 // 2470 2471 if (ARGUMENT_PRESENT( RelatedCcb ) && 2472 FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { 2473 2474 SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID ); 2475 } 2476 2477 if (IgnoreCase) { 2478 2479 SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); 2480 } 2481 2482 // 2483 // Build the file Id for this file. We can use the path table offset from the 2484 // parent and the directory offset from the dirent. 2485 // 2486 2487 CdSetFidPathTableOffset( FileId, CdQueryFidPathTableOffset( (*CurrentFcb)->FileId )); 2488 CdSetFidDirentOffset( FileId, FileContext->InitialDirent->Dirent.DirentOffset ); 2489 2490 // 2491 // Lock the Vcb so we can examine the Fcb Table. 2492 // 2493 2494 CdLockVcb( IrpContext, Vcb ); 2495 UnlockVcb = TRUE; 2496 2497 // 2498 // Get the Fcb for this file. 2499 // 2500 2501 NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_DATA, &FcbExisted ); 2502 2503 // 2504 // If the Fcb was created here then initialize from the values in the 2505 // dirent. 2506 // 2507 2508 if (!FcbExisted) { 2509 2510 CdInitializeFcbFromFileContext( IrpContext, 2511 NextFcb, 2512 *CurrentFcb, 2513 FileContext ); 2514 } 2515 2516 // 2517 // Now try to acquire the new Fcb without waiting. We will reference 2518 // the Fcb and retry with wait if unsuccessful. 2519 // 2520 2521 if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { 2522 2523 NextFcb->FcbReference += 1; 2524 2525 CdUnlockVcb( IrpContext, Vcb ); 2526 2527 CdReleaseFcb( IrpContext, *CurrentFcb ); 2528 CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); 2529 CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE ); 2530 2531 CdLockVcb( IrpContext, Vcb ); 2532 NextFcb->FcbReference -= 1; 2533 CdUnlockVcb( IrpContext, Vcb ); 2534 2535 } else { 2536 2537 // 2538 // Unlock the Vcb and move down to this new Fcb. Remember that we still 2539 // own the parent however. 2540 // 2541 2542 CdUnlockVcb( IrpContext, Vcb ); 2543 } 2544 2545 UnlockVcb = FALSE; 2546 2547 ParentFcb = *CurrentFcb; 2548 *CurrentFcb = NextFcb; 2549 2550 // 2551 // Store this name into the prefix table for the parent. 2552 // 2553 2554 2555 if (ShortNameMatch) { 2556 2557 // 2558 // Make sure the exact case is always in the tree. 2559 // 2560 2561 CdInsertPrefix( IrpContext, 2562 NextFcb, 2563 FileName, 2564 FALSE, 2565 TRUE, 2566 ParentFcb ); 2567 2568 if (IgnoreCase) { 2569 2570 CdInsertPrefix( IrpContext, 2571 NextFcb, 2572 FileName, 2573 TRUE, 2574 TRUE, 2575 ParentFcb ); 2576 } 2577 2578 // 2579 // Insert this into the prefix table if we found this without 2580 // using a version string. 2581 // 2582 2583 } else if (FileName->VersionString.Length == 0) { 2584 2585 // 2586 // Make sure the exact case is always in the tree. 2587 // 2588 2589 CdInsertPrefix( IrpContext, 2590 NextFcb, 2591 &FileContext->InitialDirent->Dirent.CdFileName, 2592 FALSE, 2593 FALSE, 2594 ParentFcb ); 2595 2596 if (IgnoreCase) { 2597 2598 CdInsertPrefix( IrpContext, 2599 NextFcb, 2600 &FileContext->InitialDirent->Dirent.CdCaseFileName, 2601 TRUE, 2602 FALSE, 2603 ParentFcb ); 2604 } 2605 } 2606 2607 // 2608 // Release the parent Fcb at this point. 2609 // 2610 2611 _Analysis_assume_same_lock_(ParentFcb->FcbNonpaged->FcbResource, NextFcb->FcbNonpaged->FcbResource); 2612 CdReleaseFcb( IrpContext, ParentFcb ); 2613 ParentFcb = NULL; 2614 2615 // 2616 // Call our worker routine to complete the open. 2617 // 2618 2619 Status = CdCompleteFcbOpen( IrpContext, 2620 IrpSp, 2621 Vcb, 2622 CurrentFcb, 2623 UserFileOpen, 2624 CcbFlags, 2625 IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); 2626 2627 } _SEH2_FINALLY { 2628 2629 // 2630 // Unlock the Vcb if held. 2631 // 2632 2633 if (UnlockVcb) { 2634 2635 CdUnlockVcb( IrpContext, Vcb ); 2636 } 2637 2638 // 2639 // Release the parent if held. 2640 // 2641 2642 if (ParentFcb != NULL) { 2643 2644 CdReleaseFcb( IrpContext, ParentFcb ); 2645 } 2646 } _SEH2_END; 2647 2648 return Status; 2649 } 2650 2651 2652 // 2653 // Local support routine 2654 // 2655 2656 _Requires_lock_held_(_Global_critical_region_) 2657 NTSTATUS 2658 CdCompleteFcbOpen ( 2659 _In_ PIRP_CONTEXT IrpContext, 2660 _In_ PIO_STACK_LOCATION IrpSp, 2661 _In_ PVCB Vcb, 2662 _Inout_ PFCB *CurrentFcb, 2663 _In_ TYPE_OF_OPEN TypeOfOpen, 2664 _In_ ULONG UserCcbFlags, 2665 _In_ ACCESS_MASK DesiredAccess 2666 ) 2667 2668 /*++ 2669 2670 Routine Description: 2671 2672 This is the worker routine which takes an existing Fcb and completes 2673 the open. We will do any necessary oplock checks and sharing checks. 2674 Finally we will create the Ccb and update the file object and any 2675 file object flags. 2676 2677 Arguments: 2678 2679 IrpSp - Stack location for the current request. 2680 2681 Vcb - Vcb for the current volume. 2682 2683 CurrentFcb - Address of pointer to Fcb to open. We clear this field if 2684 we release the resource for this file. 2685 2686 TypeOfOpen - Type of open for this request. 2687 2688 UserCcbFlags - Flags to OR into the Ccb flags. 2689 2690 DesiredAccess - Desired access for this open. 2691 2692 Return Value: 2693 2694 NTSTATUS - STATUS_SUCCESS if we complete this request, STATUS_PENDING if 2695 the oplock package takes the Irp or SHARING_VIOLATION if there is a 2696 sharing check conflict. 2697 2698 --*/ 2699 2700 { 2701 NTSTATUS Status; 2702 NTSTATUS OplockStatus = STATUS_SUCCESS; 2703 ULONG Information = FILE_OPENED; 2704 2705 BOOLEAN LockVolume = FALSE; 2706 2707 PFCB Fcb = *CurrentFcb; 2708 PCCB Ccb; 2709 2710 PAGED_CODE(); 2711 2712 // 2713 // Expand maximum allowed to something sensible for share access checking 2714 // 2715 2716 if (MAXIMUM_ALLOWED == DesiredAccess) { 2717 2718 DesiredAccess = FILE_ALL_ACCESS & ~((TypeOfOpen != UserVolumeOpen ? 2719 (FILE_WRITE_ATTRIBUTES | 2720 FILE_WRITE_DATA | 2721 FILE_WRITE_EA | 2722 FILE_ADD_FILE | 2723 FILE_ADD_SUBDIRECTORY | 2724 FILE_APPEND_DATA) : 0) | 2725 FILE_DELETE_CHILD | 2726 DELETE | 2727 WRITE_DAC ); 2728 } 2729 2730 // 2731 // If this a volume open and the user wants to lock the volume then 2732 // purge and lock the volume. 2733 // 2734 2735 if ((TypeOfOpen <= UserVolumeOpen) && 2736 !FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ )) { 2737 2738 // 2739 // If there are open handles then fail this immediately. 2740 // 2741 2742 if (Vcb->VcbCleanup != 0) { 2743 2744 return STATUS_SHARING_VIOLATION; 2745 } 2746 2747 // 2748 // If we can't wait then force this to be posted. 2749 // 2750 2751 if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { 2752 2753 CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 2754 } 2755 2756 LockVolume = TRUE; 2757 2758 // 2759 // Purge the volume and make sure all of the user references 2760 // are gone. 2761 // 2762 2763 Status = CdPurgeVolume( IrpContext, Vcb, FALSE ); 2764 2765 if (Status != STATUS_SUCCESS) { 2766 2767 return Status; 2768 } 2769 2770 // 2771 // Now force all of the delayed close operations to go away. 2772 // 2773 2774 CdFspClose( Vcb ); 2775 2776 if (Vcb->VcbUserReference > CDFS_RESIDUAL_USER_REFERENCE) { 2777 2778 return STATUS_SHARING_VIOLATION; 2779 } 2780 } 2781 2782 // 2783 // If the Fcb already existed then we need to check the oplocks and 2784 // the share access. 2785 // 2786 2787 if (Fcb->FcbCleanup != 0) { 2788 2789 // 2790 // If this is a user file open then check whether there are any 2791 // batch oplock. 2792 // 2793 2794 if (TypeOfOpen == UserFileOpen) { 2795 2796 // 2797 // Store the address of the Fcb for a possible teardown into 2798 // the IrpContext. We will release this in the call to 2799 // prepost the Irp. 2800 // 2801 2802 IrpContext->TeardownFcb = CurrentFcb; 2803 2804 if (FsRtlCurrentBatchOplock( CdGetFcbOplock(Fcb) )) { 2805 2806 // 2807 // We remember if a batch oplock break is underway for the 2808 // case where the sharing check fails. 2809 // 2810 2811 Information = FILE_OPBATCH_BREAK_UNDERWAY; 2812 2813 OplockStatus = FsRtlCheckOplock( CdGetFcbOplock(Fcb), 2814 IrpContext->Irp, 2815 IrpContext, 2816 (PVOID)CdOplockComplete, /* ReactOS Change: GCC "assignment from incompatible pointer type" */ 2817 (PVOID)CdPrePostIrp ); /* ReactOS Change: GCC "assignment from incompatible pointer type" */ 2818 2819 if (OplockStatus == STATUS_PENDING) { 2820 2821 return STATUS_PENDING; 2822 } 2823 } 2824 2825 // 2826 // Check the share access before breaking any exclusive oplocks. 2827 // 2828 2829 Status = IoCheckShareAccess( DesiredAccess, 2830 IrpSp->Parameters.Create.ShareAccess, 2831 IrpSp->FileObject, 2832 &Fcb->ShareAccess, 2833 FALSE ); 2834 2835 if (!NT_SUCCESS( Status )) { 2836 2837 return Status; 2838 } 2839 2840 // 2841 // Now check that we can continue based on the oplock state of the 2842 // file. 2843 // 2844 2845 OplockStatus = FsRtlCheckOplock( CdGetFcbOplock(Fcb), 2846 IrpContext->Irp, 2847 IrpContext, 2848 (PVOID)CdOplockComplete,/* ReactOS Change: GCC "assignment from incompatible pointer type" */ 2849 (PVOID)CdPrePostIrp );/* ReactOS Change: GCC "assignment from incompatible pointer type" */ 2850 2851 if (OplockStatus == STATUS_PENDING) { 2852 2853 return STATUS_PENDING; 2854 } 2855 2856 IrpContext->TeardownFcb = NULL; 2857 2858 // 2859 // Otherwise just do the sharing check. 2860 // 2861 2862 } else { 2863 2864 Status = IoCheckShareAccess( DesiredAccess, 2865 IrpSp->Parameters.Create.ShareAccess, 2866 IrpSp->FileObject, 2867 &Fcb->ShareAccess, 2868 FALSE ); 2869 2870 if (!NT_SUCCESS( Status )) { 2871 2872 return Status; 2873 } 2874 } 2875 } 2876 2877 // 2878 // Create the Ccb now. 2879 // 2880 2881 Ccb = CdCreateCcb( IrpContext, Fcb, UserCcbFlags ); 2882 2883 // 2884 // Update the share access. 2885 // 2886 2887 if (Fcb->FcbCleanup == 0) { 2888 2889 IoSetShareAccess( DesiredAccess, 2890 IrpSp->Parameters.Create.ShareAccess, 2891 IrpSp->FileObject, 2892 &Fcb->ShareAccess ); 2893 2894 } else { 2895 2896 IoUpdateShareAccess( IrpSp->FileObject, &Fcb->ShareAccess ); 2897 } 2898 2899 // 2900 // Set the file object type. 2901 // 2902 2903 CdSetFileObject( IrpContext, IrpSp->FileObject, TypeOfOpen, Fcb, Ccb ); 2904 2905 // 2906 // Set the appropriate cache flags for a user file object. 2907 // 2908 2909 if (TypeOfOpen == UserFileOpen) { 2910 2911 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) { 2912 2913 SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ); 2914 2915 } else { 2916 2917 SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED ); 2918 } 2919 } 2920 else if (TypeOfOpen == UserVolumeOpen) { 2921 2922 SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ); 2923 } 2924 2925 // 2926 // Update the open and cleanup counts. Check the fast io state here. 2927 // 2928 2929 CdLockVcb( IrpContext, Vcb ); 2930 2931 CdIncrementCleanupCounts( IrpContext, Fcb ); 2932 CdIncrementReferenceCounts( IrpContext, Fcb, 1, 1 ); 2933 2934 if (LockVolume) { 2935 2936 Vcb->VolumeLockFileObject = IrpSp->FileObject; 2937 SetFlag( Vcb->VcbState, VCB_STATE_LOCKED ); 2938 } 2939 2940 CdUnlockVcb( IrpContext, Vcb ); 2941 2942 CdLockFcb( IrpContext, Fcb ); 2943 2944 if (TypeOfOpen == UserFileOpen) { 2945 2946 Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); 2947 2948 } else { 2949 2950 Fcb->IsFastIoPossible = FastIoIsNotPossible; 2951 } 2952 2953 CdUnlockFcb( IrpContext, Fcb ); 2954 2955 // 2956 // Show that we opened the file. 2957 // 2958 2959 IrpContext->Irp->IoStatus.Information = Information; 2960 2961 // 2962 // Point to the section object pointer in the non-paged Fcb. 2963 // 2964 2965 IrpSp->FileObject->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject; 2966 return OplockStatus; 2967 } 2968 2969 2970 2971 2972 2973