1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 DirCtrl.c 8 9 Abstract: 10 11 This module implements the File Directory Control routines for Fat called 12 by the dispatch driver. 13 14 15 --*/ 16 17 #include "fatprocs.h" 18 19 // 20 // The Bug check file id for this module 21 // 22 23 #define BugCheckFileId (FAT_BUG_CHECK_DIRCTRL) 24 25 // 26 // The local debug trace level 27 // 28 29 #define Dbg (DEBUG_TRACE_DIRCTRL) 30 31 WCHAR Fat8QMdot3QM[12] = { DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, 32 L'.', DOS_QM, DOS_QM, DOS_QM}; 33 34 // 35 // Local procedure prototypes 36 // 37 38 _Requires_lock_held_(_Global_critical_region_) 39 NTSTATUS 40 FatQueryDirectory ( 41 IN PIRP_CONTEXT IrpContext, 42 IN PIRP Irp 43 ); 44 45 VOID 46 FatGetDirTimes( 47 PIRP_CONTEXT IrpContext, 48 PDIRENT Dirent, 49 PFILE_DIRECTORY_INFORMATION DirInfo 50 ); 51 52 _Requires_lock_held_(_Global_critical_region_) 53 NTSTATUS 54 FatNotifyChangeDirectory ( 55 IN PIRP_CONTEXT IrpContext, 56 IN PIRP Irp 57 ); 58 59 #ifdef ALLOC_PRAGMA 60 #pragma alloc_text(PAGE, FatCommonDirectoryControl) 61 #pragma alloc_text(PAGE, FatFsdDirectoryControl) 62 #pragma alloc_text(PAGE, FatNotifyChangeDirectory) 63 #pragma alloc_text(PAGE, FatQueryDirectory) 64 #pragma alloc_text(PAGE, FatGetDirTimes) 65 66 #endif 67 68 69 _Function_class_(IRP_MJ_DIRECTORY_CONTROL) 70 _Function_class_(DRIVER_DISPATCH) 71 NTSTATUS 72 NTAPI 73 FatFsdDirectoryControl ( 74 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, 75 _Inout_ PIRP Irp 76 ) 77 78 /*++ 79 80 Routine Description: 81 82 This routine implements the FSD part of directory control 83 84 Arguments: 85 86 VolumeDeviceObject - Supplies the volume device object where the 87 file exists 88 89 Irp - Supplies the Irp being processed 90 91 Return Value: 92 93 NTSTATUS - The FSD status for the IRP 94 95 --*/ 96 97 { 98 NTSTATUS Status; 99 PIRP_CONTEXT IrpContext = NULL; 100 101 BOOLEAN TopLevel; 102 103 PAGED_CODE(); 104 105 DebugTrace(+1, Dbg, "FatFsdDirectoryControl\n", 0); 106 107 // 108 // Call the common directory Control routine, with blocking allowed if 109 // synchronous 110 // 111 112 FsRtlEnterFileSystem(); 113 114 TopLevel = FatIsIrpTopLevel( Irp ); 115 116 _SEH2_TRY { 117 118 IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); 119 120 Status = FatCommonDirectoryControl( IrpContext, Irp ); 121 122 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 123 124 // 125 // We had some trouble trying to perform the requested 126 // operation, so we'll abort the I/O request with 127 // the error status that we get back from the 128 // execption code 129 // 130 131 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); 132 } _SEH2_END; 133 134 if (TopLevel) { IoSetTopLevelIrp( NULL ); } 135 136 FsRtlExitFileSystem(); 137 138 // 139 // And return to our caller 140 // 141 142 DebugTrace(-1, Dbg, "FatFsdDirectoryControl -> %08lx\n", Status); 143 144 UNREFERENCED_PARAMETER( VolumeDeviceObject ); 145 146 return Status; 147 } 148 149 150 _Requires_lock_held_(_Global_critical_region_) 151 NTSTATUS 152 FatCommonDirectoryControl ( 153 IN PIRP_CONTEXT IrpContext, 154 IN PIRP Irp 155 ) 156 157 /*++ 158 159 Routine Description: 160 161 This is the common routine for doing directory control operations called 162 by both the fsd and fsp threads 163 164 Arguments: 165 166 Irp - Supplies the Irp to process 167 168 Return Value: 169 170 NTSTATUS - The return status for the operation 171 172 --*/ 173 174 { 175 NTSTATUS Status; 176 PIO_STACK_LOCATION IrpSp; 177 178 PAGED_CODE(); 179 180 // 181 // Get a pointer to the current Irp stack location 182 // 183 184 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 185 186 DebugTrace(+1, Dbg, "FatCommonDirectoryControl\n", 0); 187 DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); 188 DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction ); 189 190 // 191 // We know this is a directory control so we'll case on the 192 // minor function, and call a internal worker routine to complete 193 // the irp. 194 // 195 196 switch ( IrpSp->MinorFunction ) { 197 198 case IRP_MN_QUERY_DIRECTORY: 199 200 Status = FatQueryDirectory( IrpContext, Irp ); 201 break; 202 203 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: 204 205 Status = FatNotifyChangeDirectory( IrpContext, Irp ); 206 break; 207 208 default: 209 210 DebugTrace(0, Dbg, "Invalid Directory Control Minor Function %08lx\n", IrpSp->MinorFunction); 211 212 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); 213 Status = STATUS_INVALID_DEVICE_REQUEST; 214 break; 215 } 216 217 DebugTrace(-1, Dbg, "FatCommonDirectoryControl -> %08lx\n", Status); 218 219 return Status; 220 } 221 222 223 // 224 // Local Support Routine 225 // 226 227 _Requires_lock_held_(_Global_critical_region_) 228 NTSTATUS 229 FatQueryDirectory ( 230 IN PIRP_CONTEXT IrpContext, 231 IN PIRP Irp 232 ) 233 234 /*++ 235 236 Routine Description: 237 238 This routine performs the query directory operation. It is responsible 239 for either completing of enqueuing the input Irp. 240 241 Arguments: 242 243 Irp - Supplies the Irp to process 244 245 Return Value: 246 247 NTSTATUS - The return status for the operation 248 249 --*/ 250 251 { 252 NTSTATUS Status; 253 PIO_STACK_LOCATION IrpSp; 254 255 PVCB Vcb; 256 PDCB Dcb; 257 PCCB Ccb; 258 PBCB Bcb; 259 260 ULONG i; 261 PUCHAR Buffer; 262 CLONG UserBufferLength; 263 264 PUNICODE_STRING UniArgFileName; 265 WCHAR LongFileNameBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; 266 UNICODE_STRING LongFileName; 267 UNICODE_STRING OrigFileName; 268 FILE_INFORMATION_CLASS FileInformationClass; 269 ULONG FileIndex; 270 ULONG MatchFlags = 0; 271 BOOLEAN RestartScan; 272 BOOLEAN ReturnSingleEntry; 273 BOOLEAN IndexSpecified; 274 275 BOOLEAN InitialQuery; 276 VBO CurrentVbo = 0; 277 BOOLEAN UpdateCcb; 278 PDIRENT Dirent; 279 UCHAR Fat8Dot3Buffer[12]; 280 OEM_STRING Fat8Dot3String; 281 ULONG DiskAllocSize; 282 283 ULONG NextEntry; 284 ULONG LastEntry; 285 286 PFILE_DIRECTORY_INFORMATION DirInfo; 287 PFILE_FULL_DIR_INFORMATION FullDirInfo; 288 PFILE_BOTH_DIR_INFORMATION BothDirInfo; 289 PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo; 290 PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo; 291 PFILE_NAMES_INFORMATION NamesInfo; 292 293 294 PAGED_CODE(); 295 296 // 297 // Get the current Stack location 298 // 299 300 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 301 302 // 303 // Display the input values. 304 // 305 DebugTrace(+1, Dbg, "FatQueryDirectory...\n", 0); 306 DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); 307 DebugTrace( 0, Dbg, " Irp = %p\n", Irp); 308 DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length); 309 DebugTrace( 0, Dbg, " ->FileName = %wZ\n", IrpSp->Parameters.QueryDirectory.FileName); 310 DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); 311 DebugTrace( 0, Dbg, " ->FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex); 312 DebugTrace( 0, Dbg, " ->UserBuffer = %p\n", Irp->AssociatedIrp.SystemBuffer); 313 DebugTrace( 0, Dbg, " ->RestartScan = %08lx\n", FlagOn( IrpSp->Flags, SL_RESTART_SCAN )); 314 DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )); 315 DebugTrace( 0, Dbg, " ->IndexSpecified = %08lx\n", FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )); 316 317 // 318 // Reference our input parameters to make things easier 319 // 320 321 UserBufferLength = IrpSp->Parameters.QueryDirectory.Length; 322 323 FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; 324 FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex; 325 326 UniArgFileName = IrpSp->Parameters.QueryDirectory.FileName; 327 328 RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); 329 ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); 330 IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); 331 332 // 333 // Check on the type of open. We return invalid parameter for all 334 // but UserDirectoryOpens. Also check that the filename is a valid 335 // UNICODE string. 336 // 337 338 if (FatDecodeFileObject( IrpSp->FileObject, 339 &Vcb, 340 &Dcb, 341 &Ccb) != UserDirectoryOpen || 342 (UniArgFileName && 343 UniArgFileName->Length % sizeof(WCHAR))) { 344 345 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); 346 DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0); 347 348 return STATUS_INVALID_PARAMETER; 349 } 350 351 // 352 // Initialize the local variables. 353 // 354 355 Bcb = NULL; 356 UpdateCcb = TRUE; 357 Dirent = NULL; 358 359 Fat8Dot3String.MaximumLength = 12; 360 Fat8Dot3String.Buffer = (PCHAR)Fat8Dot3Buffer; 361 362 LongFileName.Length = 0; 363 LongFileName.MaximumLength = sizeof( LongFileNameBuffer); 364 LongFileName.Buffer = LongFileNameBuffer; 365 366 InitialQuery = (BOOLEAN)((Ccb->UnicodeQueryTemplate.Buffer == NULL) && 367 !FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)); 368 Status = STATUS_SUCCESS; 369 Irp->IoStatus.Information = 0; 370 371 DiskAllocSize = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; 372 373 // 374 // If this is the initial query, then grab exclusive access in 375 // order to update the search string in the Ccb. We may 376 // discover that we are not the initial query once we grab the Fcb 377 // and downgrade our status. 378 // 379 // If restartscan is set, we may be replacing the query template, 380 // so take the FCB exclusive to protect against multiple people 381 // changing the CCB at once. 382 // 383 384 if (InitialQuery || RestartScan) { 385 386 if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) { 387 388 DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); 389 Status = FatFsdPostRequest( IrpContext, Irp ); 390 DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); 391 392 return Status; 393 } 394 395 if (!RestartScan && (Ccb->UnicodeQueryTemplate.Buffer != NULL)) { 396 397 InitialQuery = FALSE; 398 399 FatConvertToSharedFcb( IrpContext, Dcb ); 400 } 401 402 } else { 403 404 if (!FatAcquireSharedFcb( IrpContext, Dcb )) { 405 406 DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); 407 Status = FatFsdPostRequest( IrpContext, Irp ); 408 DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); 409 410 return Status; 411 412 } 413 } 414 415 _SEH2_TRY { 416 417 ULONG BaseLength; 418 ULONG BytesConverted; 419 420 // 421 // If we are in the Fsp now because we had to wait earlier, 422 // we must map the user buffer, otherwise we can use the 423 // user's buffer directly. 424 // 425 426 Buffer = FatMapUserBuffer( IrpContext, Irp ); 427 428 // 429 // Make sure the Dcb is still good. 430 // 431 432 FatVerifyFcb( IrpContext, Dcb ); 433 434 // 435 // Determine where to start the scan. Highest priority is given 436 // to the file index. Lower priority is the restart flag. If 437 // neither of these is specified, then the Vbo offset field in the 438 // Ccb is used. 439 // 440 441 if (IndexSpecified) { 442 443 CurrentVbo = FileIndex + sizeof( DIRENT ); 444 445 } else if (RestartScan) { 446 447 CurrentVbo = 0; 448 Ccb->OffsetToStartSearchFrom = 0; 449 450 } else { 451 452 CurrentVbo = Ccb->OffsetToStartSearchFrom; 453 } 454 455 // 456 // If this is the first try then allocate a buffer for the file 457 // name. 458 // 459 460 if (InitialQuery || 461 (RestartScan && UniArgFileName != NULL && UniArgFileName->Length != 0)) { 462 463 // 464 // If we're restarting the scan, clear out the pattern in the Ccb and regenerate it, 465 // 466 467 if (RestartScan) { 468 469 if (Ccb->UnicodeQueryTemplate.Buffer) { 470 471 if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) { 472 473 ExFreePoolWithTag(Ccb->UnicodeQueryTemplate.Buffer, TAG_FILENAME_BUFFER); 474 ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE); 475 } 476 477 Ccb->UnicodeQueryTemplate.Buffer = NULL; 478 Ccb->UnicodeQueryTemplate.Length = 0; 479 Ccb->UnicodeQueryTemplate.MaximumLength = 0; 480 } 481 482 if (Ccb->OemQueryTemplate.Wild.Buffer) { 483 484 if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { 485 486 RtlFreeOemString( &Ccb->OemQueryTemplate.Wild ); 487 ClearFlag(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT); 488 } 489 490 Ccb->OemQueryTemplate.Wild.Buffer = NULL; 491 Ccb->OemQueryTemplate.Wild.Length = 0; 492 Ccb->OemQueryTemplate.Wild.MaximumLength = 0; 493 } 494 495 Ccb->ContainsWildCards = FALSE; 496 ClearFlag(Ccb->Flags, CCB_FLAG_MATCH_ALL); 497 ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE); 498 ClearFlag(Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE); 499 ClearFlag(Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED); 500 501 } 502 503 // 504 // If either: 505 // 506 // - No name was specified 507 // - An empty name was specified 508 // - We received a '*' 509 // - The user specified the DOS equivolent of ????????.??? 510 // 511 // then match all names. 512 // 513 514 if ((UniArgFileName == NULL) || 515 (UniArgFileName->Length == 0) || 516 (UniArgFileName->Buffer == NULL) || 517 ((UniArgFileName->Length == sizeof(WCHAR)) && 518 (UniArgFileName->Buffer[0] == L'*')) || 519 ((UniArgFileName->Length == 12*sizeof(WCHAR)) && 520 (RtlEqualMemory( UniArgFileName->Buffer, 521 Fat8QMdot3QM, 522 12*sizeof(WCHAR) )))) { 523 524 Ccb->ContainsWildCards = TRUE; 525 526 SetFlag( Ccb->Flags, CCB_FLAG_MATCH_ALL ); 527 528 } else { 529 530 BOOLEAN ExtendedName = FALSE; 531 OEM_STRING LocalBestFit; 532 533 // 534 // First and formost, see if the name has wild cards. 535 // 536 537 Ccb->ContainsWildCards = 538 FsRtlDoesNameContainWildCards( UniArgFileName ); 539 540 // 541 // Now check to see if the name contains any extended 542 // characters 543 // 544 545 for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) { 546 547 if (UniArgFileName->Buffer[i] >= 0x80) { 548 549 ExtendedName = TRUE; 550 break; 551 } 552 } 553 554 // 555 // OK, now do the conversions we need. 556 // 557 558 if (ExtendedName) { 559 560 Status = RtlUpcaseUnicodeString( &Ccb->UnicodeQueryTemplate, 561 UniArgFileName, 562 TRUE ); 563 564 if (!NT_SUCCESS(Status)) { 565 566 try_return( Status ); 567 } 568 569 SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE ); 570 571 // 572 // Upcase the name and convert it to the Oem code page. 573 // 574 575 Status = RtlUpcaseUnicodeStringToCountedOemString( &LocalBestFit, 576 UniArgFileName, 577 TRUE ); 578 579 // 580 // If this conversion failed for any reason other than 581 // an unmappable character fail the request. 582 // 583 584 if (!NT_SUCCESS(Status)) { 585 586 if (Status == STATUS_UNMAPPABLE_CHARACTER) { 587 588 SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); 589 590 } else { 591 592 try_return( Status ); 593 } 594 595 } else { 596 597 SetFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); 598 } 599 600 } else { 601 602 PVOID Buffers; 603 604 // 605 // This case is optimized because I know I only have to 606 // worry about a-z. 607 // 608 609 Buffers = FsRtlAllocatePoolWithTag( PagedPool, 610 UniArgFileName->Length + 611 UniArgFileName->Length / sizeof(WCHAR), 612 TAG_FILENAME_BUFFER ); 613 614 Ccb->UnicodeQueryTemplate.Buffer = Buffers; 615 Ccb->UnicodeQueryTemplate.Length = UniArgFileName->Length; 616 Ccb->UnicodeQueryTemplate.MaximumLength = UniArgFileName->Length; 617 618 LocalBestFit.Buffer = (PCHAR)Buffers + UniArgFileName->Length; 619 LocalBestFit.Length = UniArgFileName->Length / sizeof(WCHAR); 620 LocalBestFit.MaximumLength = LocalBestFit.Length; 621 622 SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE ); 623 624 for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) { 625 626 WCHAR c = UniArgFileName->Buffer[i]; 627 628 LocalBestFit.Buffer[i] = (UCHAR) 629 (Ccb->UnicodeQueryTemplate.Buffer[i] = 630 (c < 'a' ? c : c <= 'z' ? c - ('a' - 'A') : c)); 631 } 632 } 633 634 // 635 // At this point we now have the upcased unicode name, 636 // and the two Oem names if they could be represented in 637 // this code page. 638 // 639 // Now determine if the Oem names are legal for what we 640 // going to try and do. Mark them as not usable is they 641 // are not legal. Note that we can optimize extended names 642 // since they are actually both the same string. 643 // 644 645 if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ) && 646 !FatIsNameShortOemValid( IrpContext, 647 LocalBestFit, 648 Ccb->ContainsWildCards, 649 FALSE, 650 FALSE )) { 651 652 if (ExtendedName) { 653 654 RtlFreeOemString( &LocalBestFit ); 655 ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); 656 } 657 658 SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); 659 } 660 661 // 662 // OK, now both locals oem strings correctly reflect their 663 // usability. Now we want to load up the Ccb structure. 664 // 665 // Now we will branch on two paths of wheather the name 666 // is wild or not. 667 // 668 669 if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) { 670 671 if (Ccb->ContainsWildCards) { 672 673 Ccb->OemQueryTemplate.Wild = LocalBestFit; 674 675 } else { 676 677 FatStringTo8dot3( IrpContext, 678 LocalBestFit, 679 &Ccb->OemQueryTemplate.Constant ); 680 681 if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { 682 683 RtlFreeOemString( &LocalBestFit ); 684 ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); 685 } 686 } 687 } 688 } 689 690 // 691 // We convert to shared access. 692 // 693 694 FatConvertToSharedFcb( IrpContext, Dcb ); 695 } 696 697 LastEntry = 0; 698 NextEntry = 0; 699 700 switch (FileInformationClass) { 701 702 case FileDirectoryInformation: 703 704 BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, 705 FileName[0] ); 706 break; 707 708 case FileFullDirectoryInformation: 709 710 BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, 711 FileName[0] ); 712 break; 713 714 case FileIdFullDirectoryInformation: 715 716 BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, 717 FileName[0] ); 718 break; 719 720 case FileNamesInformation: 721 722 BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, 723 FileName[0] ); 724 break; 725 726 case FileBothDirectoryInformation: 727 728 BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, 729 FileName[0] ); 730 break; 731 732 case FileIdBothDirectoryInformation: 733 734 BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, 735 FileName[0] ); 736 break; 737 738 default: 739 740 try_return( Status = STATUS_INVALID_INFO_CLASS ); 741 } 742 743 // 744 // At this point we are about to enter our query loop. We have 745 // determined the index into the directory file to begin the 746 // search. LastEntry and NextEntry are used to index into the user 747 // buffer. LastEntry is the last entry we've added, NextEntry is 748 // current one we're working on. If NextEntry is non-zero, then 749 // at least one entry was added. 750 // 751 752 while ( TRUE ) { 753 754 VBO NextVbo; 755 ULONG FileNameLength; 756 ULONG BytesRemainingInBuffer; 757 BOOLEAN FileNameDos; 758 759 DebugTrace(0, Dbg, "FatQueryDirectory -> Top of loop\n", 0); 760 761 // 762 // If the user had requested only a single match and we have 763 // returned that, then we stop at this point. 764 // 765 766 if (ReturnSingleEntry && NextEntry != 0) { 767 768 try_return( Status ); 769 } 770 771 772 // 773 // We call FatLocateDirent to lock down the next matching dirent. 774 // 775 776 FatLocateDirent( IrpContext, 777 Dcb, 778 Ccb, 779 CurrentVbo, 780 &MatchFlags, 781 &Dirent, 782 &Bcb, 783 &NextVbo, 784 &FileNameDos, 785 &LongFileName, 786 &OrigFileName ); 787 788 // 789 // If we didn't receive a dirent, then we are at the end of the 790 // directory. If we have returned any files, we exit with 791 // success, otherwise we return STATUS_NO_MORE_FILES. 792 // 793 794 if (!Dirent) { 795 796 DebugTrace(0, Dbg, "FatQueryDirectory -> No dirent\n", 0); 797 798 if (NextEntry == 0) { 799 800 UpdateCcb = FALSE; 801 802 if (InitialQuery) { 803 804 Status = STATUS_NO_SUCH_FILE; 805 806 } else { 807 808 Status = STATUS_NO_MORE_FILES; 809 } 810 } 811 812 try_return( Status ); 813 } 814 815 816 // 817 // Protect access to the user buffer with an exception handler. 818 // Since (at our request) IO doesn't buffer these requests, we have 819 // to guard against a user messing with the page protection and other 820 // such trickery. 821 // 822 823 _SEH2_TRY { 824 825 Fat8dot3ToString( IrpContext, Dirent, TRUE, &Fat8Dot3String ); 826 827 828 if (LongFileName.Length == 0) { 829 830 // 831 // Now we have an entry to return to our caller. We'll convert 832 // the name from the form in the dirent to a <name>.<ext> form. 833 // We'll case on the type of information requested and fill up 834 // the user buffer if everything fits. 835 // 836 837 // 838 // Determine the UNICODE length of the file name. 839 // 840 841 FileNameLength = RtlOemStringToCountedUnicodeSize(&Fat8Dot3String); 842 843 // 844 // Here are the rules concerning filling up the buffer: 845 // 846 // 1. The Io system garentees that there will always be 847 // enough room for at least one base record. 848 // 849 // 2. If the full first record (including file name) cannot 850 // fit, as much of the name as possible is copied and 851 // STATUS_BUFFER_OVERFLOW is returned. 852 // 853 // 3. If a subsequent record cannot completely fit into the 854 // buffer, none of it (as in 0 bytes) is copied, and 855 // STATUS_SUCCESS is returned. A subsequent query will 856 // pick up with this record. 857 // 858 859 BytesRemainingInBuffer = UserBufferLength - NextEntry; 860 861 if ( (NextEntry != 0) && 862 ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || 863 (UserBufferLength < NextEntry) ) ) { 864 865 DebugTrace(0, Dbg, "Next entry won't fit\n", 0); 866 867 try_return( Status = STATUS_SUCCESS ); 868 } 869 870 NT_ASSERT( BytesRemainingInBuffer >= BaseLength ); 871 872 // 873 // Zero the base part of the structure. 874 // 875 876 RtlZeroMemory( &Buffer[NextEntry], BaseLength ); 877 878 switch ( FileInformationClass ) { 879 880 // 881 // Now fill the base parts of the strucure that are applicable. 882 // 883 884 case FileBothDirectoryInformation: 885 case FileFullDirectoryInformation: 886 case FileIdBothDirectoryInformation: 887 case FileIdFullDirectoryInformation: 888 889 DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0); 890 891 // 892 // Get the Ea file length. 893 // 894 895 FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry]; 896 897 // 898 // If the EAs are corrupt, ignore the error. We don't want 899 // to abort the directory query. 900 // 901 902 _SEH2_TRY { 903 904 FatGetEaLength( IrpContext, 905 Vcb, 906 Dirent, 907 &FullDirInfo->EaSize ); 908 909 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 910 911 FatResetExceptionState( IrpContext ); 912 FullDirInfo->EaSize = 0; 913 } _SEH2_END; 914 915 case FileDirectoryInformation: 916 917 DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; 918 919 FatGetDirTimes( IrpContext, Dirent, DirInfo ); 920 921 DirInfo->EndOfFile.QuadPart = Dirent->FileSize; 922 923 if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { 924 925 926 DirInfo->AllocationSize.QuadPart = 927 (((Dirent->FileSize + DiskAllocSize - 1) / DiskAllocSize) * 928 DiskAllocSize ); 929 } 930 931 if (Dirent->Attributes != 0) { 932 DirInfo->FileAttributes = Dirent->Attributes; 933 934 935 } else { 936 937 DirInfo->FileAttributes = 0; 938 939 DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; 940 } 941 942 DirInfo->FileIndex = NextVbo; 943 944 DirInfo->FileNameLength = FileNameLength; 945 946 DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String); 947 948 break; 949 950 case FileNamesInformation: 951 952 DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0); 953 954 NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; 955 956 NamesInfo->FileIndex = NextVbo; 957 958 NamesInfo->FileNameLength = FileNameLength; 959 960 DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String ); 961 962 break; 963 964 default: 965 966 #ifdef _MSC_VER 967 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 968 #endif 969 FatBugCheck( FileInformationClass, 0, 0 ); 970 } 971 972 BytesConverted = 0; 973 974 Status = RtlOemToUnicodeN( (PWCH)&Buffer[NextEntry + BaseLength], 975 BytesRemainingInBuffer - BaseLength, 976 &BytesConverted, 977 Fat8Dot3String.Buffer, 978 Fat8Dot3String.Length ); 979 980 // 981 // Check for the case that a single entry doesn't fit. 982 // This should only get this far on the first entry 983 // 984 985 if (BytesConverted < FileNameLength) { 986 987 NT_ASSERT( NextEntry == 0 ); 988 Status = STATUS_BUFFER_OVERFLOW; 989 } 990 991 // 992 // Set up the previous next entry offset 993 // 994 995 *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; 996 997 // 998 // And indicate how much of the user buffer we have currently 999 // used up. We must compute this value before we long align 1000 // ourselves for the next entry 1001 // 1002 1003 Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) + 1004 BaseLength + BytesConverted; 1005 1006 // 1007 // If something happened with the conversion, bail here. 1008 // 1009 1010 if ( !NT_SUCCESS( Status ) ) { 1011 1012 try_return( NOTHING ); 1013 } 1014 1015 } else { 1016 1017 ULONG ShortNameLength; 1018 1019 FileNameLength = LongFileName.Length; 1020 1021 // 1022 // Here are the rules concerning filling up the buffer: 1023 // 1024 // 1. The Io system garentees that there will always be 1025 // enough room for at least one base record. 1026 // 1027 // 2. If the full first record (including file name) cannot 1028 // fit, as much of the name as possible is copied and 1029 // STATUS_BUFFER_OVERFLOW is returned. 1030 // 1031 // 3. If a subsequent record cannot completely fit into the 1032 // buffer, none of it (as in 0 bytes) is copied, and 1033 // STATUS_SUCCESS is returned. A subsequent query will 1034 // pick up with this record. 1035 // 1036 1037 BytesRemainingInBuffer = UserBufferLength - NextEntry; 1038 1039 if ( (NextEntry != 0) && 1040 ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || 1041 (UserBufferLength < NextEntry) ) ) { 1042 1043 DebugTrace(0, Dbg, "Next entry won't fit\n", 0); 1044 1045 try_return( Status = STATUS_SUCCESS ); 1046 } 1047 1048 NT_ASSERT( BytesRemainingInBuffer >= BaseLength ); 1049 1050 // 1051 // Zero the base part of the structure. 1052 // 1053 1054 RtlZeroMemory( &Buffer[NextEntry], BaseLength ); 1055 1056 switch ( FileInformationClass ) { 1057 1058 // 1059 // Now fill the base parts of the strucure that are applicable. 1060 // 1061 1062 case FileBothDirectoryInformation: 1063 case FileIdBothDirectoryInformation: 1064 1065 BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry]; 1066 1067 // 1068 // Now we have an entry to return to our caller. We'll convert 1069 // the name from the form in the dirent to a <name>.<ext> form. 1070 // We'll case on the type of information requested and fill up 1071 // the user buffer if everything fits. 1072 // 1073 1074 Fat8dot3ToString( IrpContext, Dirent, FALSE, &Fat8Dot3String ); 1075 1076 NT_ASSERT( Fat8Dot3String.Length <= 12 ); 1077 1078 Status = RtlOemToUnicodeN( &BothDirInfo->ShortName[0], 1079 12*sizeof(WCHAR), 1080 &ShortNameLength, 1081 Fat8Dot3String.Buffer, 1082 Fat8Dot3String.Length ); 1083 1084 NT_ASSERT( Status != STATUS_BUFFER_OVERFLOW ); 1085 NT_ASSERT( ShortNameLength <= 12*sizeof(WCHAR) ); 1086 1087 // 1088 // Copy the length into the dirinfo structure. Note 1089 // that the LHS below is a USHORT, so it can not 1090 // be specificed as the OUT parameter above. 1091 // 1092 1093 BothDirInfo->ShortNameLength = (UCHAR)ShortNameLength; 1094 1095 // 1096 // If something happened with the conversion, bail here. 1097 // 1098 1099 if ( !NT_SUCCESS( Status ) ) { 1100 1101 try_return( NOTHING ); 1102 } 1103 1104 case FileFullDirectoryInformation: 1105 case FileIdFullDirectoryInformation: 1106 1107 DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0); 1108 1109 // 1110 // Get the Ea file length. 1111 // 1112 1113 FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry]; 1114 1115 // 1116 // If the EAs are corrupt, ignore the error. We don't want 1117 // to abort the directory query. 1118 // 1119 1120 _SEH2_TRY { 1121 1122 FatGetEaLength( IrpContext, 1123 Vcb, 1124 Dirent, 1125 &FullDirInfo->EaSize ); 1126 1127 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 1128 1129 FatResetExceptionState( IrpContext ); 1130 FullDirInfo->EaSize = 0; 1131 } _SEH2_END; 1132 1133 case FileDirectoryInformation: 1134 1135 DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; 1136 1137 FatGetDirTimes( IrpContext, Dirent, DirInfo ); 1138 1139 DirInfo->EndOfFile.QuadPart = Dirent->FileSize; 1140 1141 if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { 1142 1143 1144 DirInfo->AllocationSize.QuadPart = ( 1145 (( Dirent->FileSize 1146 + DiskAllocSize - 1 ) 1147 / DiskAllocSize ) 1148 * DiskAllocSize ); 1149 } 1150 1151 if (Dirent->Attributes != 0) { 1152 DirInfo->FileAttributes = Dirent->Attributes; 1153 1154 1155 } else { 1156 1157 DirInfo->FileAttributes = 0; 1158 1159 1160 DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; 1161 } 1162 1163 1164 DirInfo->FileIndex = NextVbo; 1165 1166 DirInfo->FileNameLength = FileNameLength; 1167 1168 DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String); 1169 1170 break; 1171 1172 case FileNamesInformation: 1173 1174 DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0); 1175 1176 NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; 1177 1178 NamesInfo->FileIndex = NextVbo; 1179 1180 NamesInfo->FileNameLength = FileNameLength; 1181 1182 DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String ); 1183 1184 break; 1185 1186 default: 1187 1188 #ifdef _MSC_VER 1189 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 1190 #endif 1191 FatBugCheck( FileInformationClass, 0, 0 ); 1192 } 1193 1194 BytesConverted = BytesRemainingInBuffer - BaseLength >= FileNameLength ? 1195 FileNameLength : 1196 BytesRemainingInBuffer - BaseLength; 1197 1198 RtlCopyMemory( &Buffer[NextEntry + BaseLength], 1199 &LongFileName.Buffer[0], 1200 BytesConverted ); 1201 1202 // 1203 // Set up the previous next entry offset 1204 // 1205 1206 *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; 1207 1208 // 1209 // And indicate how much of the user buffer we have currently 1210 // used up. We must compute this value before we long align 1211 // ourselves for the next entry 1212 // 1213 1214 Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) + 1215 BaseLength + BytesConverted; 1216 1217 // 1218 // Check for the case that a single entry doesn't fit. 1219 // This should only get this far on the first entry. 1220 // 1221 1222 if (BytesConverted < FileNameLength) { 1223 1224 NT_ASSERT( NextEntry == 0 ); 1225 1226 try_return( Status = STATUS_BUFFER_OVERFLOW ); 1227 } 1228 } 1229 1230 // 1231 // Finish up by filling in the FileId 1232 // 1233 1234 switch ( FileInformationClass ) { 1235 1236 case FileIdBothDirectoryInformation: 1237 1238 IdBothDirInfo = (PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry]; 1239 IdBothDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); 1240 break; 1241 1242 case FileIdFullDirectoryInformation: 1243 1244 IdFullDirInfo = (PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry]; 1245 IdFullDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); 1246 break; 1247 1248 default: 1249 break; 1250 } 1251 1252 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 1253 1254 // 1255 // We had a problem filling in the user's buffer, so stop and 1256 // fail this request. This is the only reason any exception 1257 // would have occured at this level. 1258 // 1259 1260 Irp->IoStatus.Information = 0; 1261 UpdateCcb = FALSE; 1262 try_return( Status = _SEH2_GetExceptionCode()); 1263 } _SEH2_END; 1264 1265 // 1266 // Set ourselves up for the next iteration 1267 // 1268 1269 LastEntry = NextEntry; 1270 NextEntry += (ULONG)QuadAlign(BaseLength + BytesConverted); 1271 1272 CurrentVbo = NextVbo + sizeof( DIRENT ); 1273 } 1274 1275 try_exit: NOTHING; 1276 } _SEH2_FINALLY { 1277 1278 DebugUnwind( FatQueryDirectory ); 1279 1280 FatReleaseFcb( IrpContext, Dcb ); 1281 1282 // 1283 // Unpin data in cache if still held. 1284 // 1285 1286 FatUnpinBcb( IrpContext, Bcb ); 1287 1288 // 1289 // Free any dynamically allocated string buffer 1290 // 1291 1292 FatFreeStringBuffer( &LongFileName); 1293 1294 // 1295 // Perform any cleanup. If this is the first query, then store 1296 // the filename in the Ccb if successful. Also update the 1297 // VBO index for the next search. This is done by transferring 1298 // from shared access to exclusive access and copying the 1299 // data from the local copies. 1300 // 1301 1302 if (!_SEH2_AbnormalTermination()) { 1303 1304 if (UpdateCcb) { 1305 1306 // 1307 // Store the most recent VBO to use as a starting point for 1308 // the next search. 1309 // 1310 1311 Ccb->OffsetToStartSearchFrom = CurrentVbo; 1312 } 1313 1314 FatCompleteRequest( IrpContext, Irp, Status ); 1315 } 1316 1317 DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); 1318 1319 } _SEH2_END; 1320 1321 return Status; 1322 } 1323 1324 1325 // 1326 // Local Support Routine 1327 // 1328 1329 VOID 1330 FatGetDirTimes( 1331 PIRP_CONTEXT IrpContext, 1332 PDIRENT Dirent, 1333 PFILE_DIRECTORY_INFORMATION DirInfo 1334 ) 1335 1336 /*++ 1337 1338 Routine Description: 1339 1340 This routine pulls the date/time information from a dirent and fills 1341 in the DirInfo structure. 1342 1343 Arguments: 1344 1345 Dirent - Supplies the dirent 1346 DirInfo - Supplies the target structure 1347 1348 Return Value: 1349 1350 VOID 1351 1352 --*/ 1353 1354 1355 { 1356 PAGED_CODE(); 1357 1358 // 1359 // Start with the Last Write Time. 1360 // 1361 1362 DirInfo->LastWriteTime = 1363 FatFatTimeToNtTime( IrpContext, 1364 Dirent->LastWriteTime, 1365 0 ); 1366 1367 // 1368 // These fields are only non-zero when in Chicago mode. 1369 // 1370 1371 if (FatData.ChicagoMode) { 1372 1373 // 1374 // Do a quick check here for Creation and LastAccess 1375 // times that are the same as the LastWriteTime. 1376 // 1377 1378 if (*((UNALIGNED LONG *)&Dirent->CreationTime) == 1379 *((UNALIGNED LONG *)&Dirent->LastWriteTime)) { 1380 1381 DirInfo->CreationTime.QuadPart = 1382 1383 DirInfo->LastWriteTime.QuadPart + 1384 Dirent->CreationMSec * 10 * 1000 * 10; 1385 1386 } else { 1387 1388 // 1389 // Only do the really hard work if this field is non-zero. 1390 // 1391 1392 if (((PUSHORT)Dirent)[8] != 0) { 1393 1394 DirInfo->CreationTime = 1395 FatFatTimeToNtTime( IrpContext, 1396 Dirent->CreationTime, 1397 Dirent->CreationMSec ); 1398 1399 } else { 1400 1401 ExLocalTimeToSystemTime( &FatJanOne1980, 1402 &DirInfo->CreationTime ); 1403 } 1404 } 1405 1406 // 1407 // Do a quick check for LastAccessDate. 1408 // 1409 1410 if (*((PUSHORT)&Dirent->LastAccessDate) == 1411 *((PUSHORT)&Dirent->LastWriteTime.Date)) { 1412 1413 PFAT_TIME WriteTime; 1414 1415 WriteTime = &Dirent->LastWriteTime.Time; 1416 1417 DirInfo->LastAccessTime.QuadPart = 1418 DirInfo->LastWriteTime.QuadPart - 1419 UInt32x32To64(((WriteTime->DoubleSeconds * 2) + 1420 (WriteTime->Minute * 60) + 1421 (WriteTime->Hour * 60 * 60)), 1422 1000 * 1000 * 10); 1423 1424 } else { 1425 1426 // 1427 // Only do the really hard work if this field is non-zero. 1428 // 1429 1430 if (((PUSHORT)Dirent)[9] != 0) { 1431 1432 DirInfo->LastAccessTime = 1433 FatFatDateToNtTime( IrpContext, 1434 Dirent->LastAccessDate ); 1435 1436 } else { 1437 1438 ExLocalTimeToSystemTime( &FatJanOne1980, 1439 &DirInfo->LastAccessTime ); 1440 } 1441 } 1442 } 1443 } 1444 1445 1446 // 1447 // Local Support Routine 1448 // 1449 1450 _Requires_lock_held_(_Global_critical_region_) 1451 NTSTATUS 1452 FatNotifyChangeDirectory ( 1453 IN PIRP_CONTEXT IrpContext, 1454 IN PIRP Irp 1455 ) 1456 1457 /*++ 1458 1459 Routine Description: 1460 1461 This routine performs the notify change directory operation. It is 1462 responsible for either completing of enqueuing the input Irp. 1463 1464 Arguments: 1465 1466 Irp - Supplies the Irp to process 1467 1468 Return Value: 1469 1470 NTSTATUS - The return status for the operation 1471 1472 --*/ 1473 1474 { 1475 NTSTATUS Status = STATUS_SUCCESS; 1476 PIO_STACK_LOCATION IrpSp; 1477 PVCB Vcb; 1478 PDCB Dcb; 1479 PCCB Ccb; 1480 ULONG CompletionFilter; 1481 BOOLEAN WatchTree; 1482 1483 BOOLEAN CompleteRequest; 1484 1485 PAGED_CODE(); 1486 1487 // 1488 // Get the current Stack location 1489 // 1490 1491 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 1492 1493 DebugTrace(+1, Dbg, "FatNotifyChangeDirectory...\n", 0); 1494 DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); 1495 DebugTrace( 0, Dbg, " Irp = %p\n", Irp); 1496 DebugTrace( 0, Dbg, " ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter); 1497 1498 // 1499 // Always set the wait flag in the Irp context for the original request. 1500 // 1501 1502 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); 1503 1504 // 1505 // Assume we don't complete request. 1506 // 1507 1508 CompleteRequest = FALSE; 1509 1510 // 1511 // Check on the type of open. We return invalid parameter for all 1512 // but UserDirectoryOpens. 1513 // 1514 1515 if (FatDecodeFileObject( IrpSp->FileObject, 1516 &Vcb, 1517 &Dcb, 1518 &Ccb ) != UserDirectoryOpen) { 1519 1520 FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); 1521 DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0); 1522 1523 return STATUS_INVALID_PARAMETER; 1524 1525 } 1526 1527 // 1528 // Reference our input parameter to make things easier 1529 // 1530 1531 CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter; 1532 WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ); 1533 1534 // 1535 // Try to acquire exclusive access to the Dcb and enqueue the Irp to the 1536 // Fsp if we didn't get access 1537 // 1538 1539 if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) { 1540 1541 DebugTrace(0, Dbg, "FatNotifyChangeDirectory -> Cannot Acquire Fcb\n", 0); 1542 1543 Status = FatFsdPostRequest( IrpContext, Irp ); 1544 1545 DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); 1546 return Status; 1547 } 1548 1549 _SEH2_TRY { 1550 1551 // 1552 // Make sure the Fcb is still good 1553 // 1554 1555 FatVerifyFcb( IrpContext, Dcb ); 1556 1557 // 1558 // We need the full name. 1559 // 1560 1561 FatSetFullFileNameInFcb( IrpContext, Dcb ); 1562 1563 // 1564 // If the file is marked as DELETE_PENDING then complete this 1565 // request immediately. 1566 // 1567 1568 if (FlagOn( Dcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) { 1569 1570 FatRaiseStatus( IrpContext, STATUS_DELETE_PENDING ); 1571 } 1572 1573 // 1574 // Call the Fsrtl package to process the request. 1575 // 1576 1577 FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, 1578 &Vcb->DirNotifyList, 1579 Ccb, 1580 (PSTRING)&Dcb->FullFileName, 1581 WatchTree, 1582 FALSE, 1583 CompletionFilter, 1584 Irp, 1585 NULL, 1586 NULL ); 1587 1588 Status = STATUS_PENDING; 1589 1590 CompleteRequest = TRUE; 1591 1592 } _SEH2_FINALLY { 1593 1594 DebugUnwind( FatNotifyChangeDirectory ); 1595 1596 FatReleaseFcb( IrpContext, Dcb ); 1597 1598 // 1599 // If the dir notify package is holding the Irp, we discard the 1600 // the IrpContext. 1601 // 1602 1603 if (CompleteRequest) { 1604 1605 FatCompleteRequest( IrpContext, FatNull, 0 ); 1606 } 1607 1608 DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); 1609 } _SEH2_END; 1610 1611 return Status; 1612 } 1613 1614