1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 FileInfo.c 8 9 Abstract: 10 11 This module implements the File Information routines for Fat called by 12 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_FILEINFO) 24 25 // 26 // The local debug trace level 27 // 28 29 #define Dbg (DEBUG_TRACE_FILEINFO) 30 31 VOID 32 FatQueryBasicInfo ( 33 IN PIRP_CONTEXT IrpContext, 34 IN PFCB Fcb, 35 IN PFILE_OBJECT FileObject, 36 IN OUT PFILE_BASIC_INFORMATION Buffer, 37 IN OUT PLONG Length 38 ); 39 40 _Requires_lock_held_(_Global_critical_region_) 41 VOID 42 FatQueryStandardInfo ( 43 IN PIRP_CONTEXT IrpContext, 44 IN PFCB Fcb, 45 IN OUT PFILE_STANDARD_INFORMATION Buffer, 46 IN OUT PLONG Length 47 ); 48 49 VOID 50 FatQueryInternalInfo ( 51 IN PIRP_CONTEXT IrpContext, 52 IN PFCB Fcb, 53 IN OUT PFILE_INTERNAL_INFORMATION Buffer, 54 IN OUT PLONG Length 55 ); 56 57 VOID 58 FatQueryEaInfo ( 59 IN PIRP_CONTEXT IrpContext, 60 IN PFCB Fcb, 61 IN OUT PFILE_EA_INFORMATION Buffer, 62 IN OUT PLONG Length 63 ); 64 65 VOID 66 FatQueryPositionInfo ( 67 IN PIRP_CONTEXT IrpContext, 68 IN PFILE_OBJECT FileObject, 69 IN OUT PFILE_POSITION_INFORMATION Buffer, 70 IN OUT PLONG Length 71 ); 72 73 _Requires_lock_held_(_Global_critical_region_) 74 VOID 75 FatQueryNameInfo ( 76 IN PIRP_CONTEXT IrpContext, 77 IN PFCB Fcb, 78 IN PCCB Ccb, 79 IN BOOLEAN Normalized, 80 IN OUT PFILE_NAME_INFORMATION Buffer, 81 IN OUT PLONG Length 82 ); 83 84 VOID 85 FatQueryShortNameInfo ( 86 IN PIRP_CONTEXT IrpContext, 87 IN PFCB Fcb, 88 IN OUT PFILE_NAME_INFORMATION Buffer, 89 IN OUT PLONG Length 90 ); 91 92 _Requires_lock_held_(_Global_critical_region_) 93 VOID 94 FatQueryNetworkInfo ( 95 IN PIRP_CONTEXT IrpContext, 96 IN PFCB Fcb, 97 IN PFILE_OBJECT FileObject, 98 IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, 99 IN OUT PLONG Length 100 ); 101 102 _Requires_lock_held_(_Global_critical_region_) 103 NTSTATUS 104 FatSetBasicInfo ( 105 IN PIRP_CONTEXT IrpContext, 106 IN PIRP Irp, 107 IN PFCB Fcb, 108 IN PCCB Ccb 109 ); 110 111 _Requires_lock_held_(_Global_critical_region_) 112 NTSTATUS 113 FatSetDispositionInfo ( 114 IN PIRP_CONTEXT IrpContext, 115 IN PIRP Irp, 116 IN PFILE_OBJECT FileObject, 117 IN PFCB Fcb 118 ); 119 120 NTSTATUS 121 FatSetRenameInfo ( 122 IN PIRP_CONTEXT IrpContext, 123 IN PIRP Irp, 124 IN PVCB Vcb, 125 IN PFCB Fcb, 126 IN PCCB Ccb 127 ); 128 129 NTSTATUS 130 FatSetPositionInfo ( 131 IN PIRP_CONTEXT IrpContext, 132 IN PIRP Irp, 133 IN PFILE_OBJECT FileObject 134 ); 135 136 _Requires_lock_held_(_Global_critical_region_) 137 NTSTATUS 138 FatSetAllocationInfo ( 139 IN PIRP_CONTEXT IrpContext, 140 IN PIRP Irp, 141 IN PFCB Fcb, 142 IN PFILE_OBJECT FileObject 143 ); 144 145 _Requires_lock_held_(_Global_critical_region_) 146 NTSTATUS 147 FatSetEndOfFileInfo ( 148 IN PIRP_CONTEXT IrpContext, 149 IN PIRP Irp, 150 IN PFILE_OBJECT FileObject, 151 IN PVCB Vcb, 152 IN PFCB Fcb 153 ); 154 155 _Requires_lock_held_(_Global_critical_region_) 156 NTSTATUS 157 FatSetValidDataLengthInfo ( 158 IN PIRP_CONTEXT IrpContext, 159 IN PIRP Irp, 160 IN PFILE_OBJECT FileObject, 161 IN PFCB Fcb, 162 IN PCCB Ccb 163 ); 164 165 _Requires_lock_held_(_Global_critical_region_) 166 VOID 167 FatRenameEAs ( 168 IN PIRP_CONTEXT IrpContext, 169 IN PFCB Fcb, 170 IN USHORT ExtendedAttributes, 171 IN POEM_STRING OldOemName 172 ); 173 174 #ifdef ALLOC_PRAGMA 175 #pragma alloc_text(PAGE, FatCommonQueryInformation) 176 #pragma alloc_text(PAGE, FatCommonSetInformation) 177 #pragma alloc_text(PAGE, FatFsdQueryInformation) 178 #pragma alloc_text(PAGE, FatFsdSetInformation) 179 #pragma alloc_text(PAGE, FatQueryBasicInfo) 180 #pragma alloc_text(PAGE, FatQueryEaInfo) 181 #pragma alloc_text(PAGE, FatQueryInternalInfo) 182 #pragma alloc_text(PAGE, FatQueryNameInfo) 183 #pragma alloc_text(PAGE, FatQueryNetworkInfo) 184 #pragma alloc_text(PAGE, FatQueryShortNameInfo) 185 #pragma alloc_text(PAGE, FatQueryPositionInfo) 186 #pragma alloc_text(PAGE, FatQueryStandardInfo) 187 #pragma alloc_text(PAGE, FatSetAllocationInfo) 188 #pragma alloc_text(PAGE, FatSetBasicInfo) 189 #pragma alloc_text(PAGE, FatSetDispositionInfo) 190 #pragma alloc_text(PAGE, FatSetEndOfFileInfo) 191 #pragma alloc_text(PAGE, FatSetValidDataLengthInfo) 192 #pragma alloc_text(PAGE, FatSetPositionInfo) 193 #pragma alloc_text(PAGE, FatSetRenameInfo) 194 #pragma alloc_text(PAGE, FatDeleteFile) 195 #pragma alloc_text(PAGE, FatRenameEAs) 196 #endif 197 198 199 _Function_class_(IRP_MJ_QUERY_INFORMATION) 200 _Function_class_(DRIVER_DISPATCH) 201 NTSTATUS 202 NTAPI 203 FatFsdQueryInformation ( 204 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, 205 _Inout_ PIRP Irp 206 ) 207 208 /*++ 209 210 Routine Description: 211 212 This routine implements the Fsd part of the NtQueryInformationFile API 213 call. 214 215 Arguments: 216 217 VolumeDeviceObject - Supplies the volume device object where the file 218 being queried exists. 219 220 Irp - Supplies the Irp being processed. 221 222 Return Value: 223 224 NTSTATUS - The FSD status for the Irp. 225 226 --*/ 227 228 { 229 NTSTATUS Status; 230 PIRP_CONTEXT IrpContext = NULL; 231 232 BOOLEAN TopLevel; 233 234 PAGED_CODE(); 235 236 DebugTrace(+1, Dbg, "FatFsdQueryInformation\n", 0); 237 238 // 239 // Call the common query routine, with blocking allowed if synchronous 240 // 241 242 FsRtlEnterFileSystem(); 243 244 TopLevel = FatIsIrpTopLevel( Irp ); 245 246 _SEH2_TRY { 247 248 IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); 249 250 Status = FatCommonQueryInformation( IrpContext, Irp ); 251 252 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 253 254 // 255 // We had some trouble trying to perform the requested 256 // operation, so we'll abort the I/O request with 257 // the error status that we get back from the 258 // execption code 259 // 260 261 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); 262 } _SEH2_END; 263 264 if (TopLevel) { IoSetTopLevelIrp( NULL ); } 265 266 FsRtlExitFileSystem(); 267 268 // 269 // And return to our caller 270 // 271 272 DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status); 273 274 UNREFERENCED_PARAMETER( VolumeDeviceObject ); 275 276 return Status; 277 } 278 279 280 _Function_class_(IRP_MJ_SET_INFORMATION) 281 _Function_class_(DRIVER_DISPATCH) 282 NTSTATUS 283 NTAPI 284 FatFsdSetInformation ( 285 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, 286 _Inout_ PIRP Irp 287 ) 288 289 /*++ 290 291 Routine Description: 292 293 This routine implements the FSD part of the NtSetInformationFile API 294 call. 295 296 Arguments: 297 298 VolumeDeviceObject - Supplies the volume device object where the file 299 being set exists. 300 301 Irp - Supplies the Irp being processed. 302 303 Return Value: 304 305 NTSTATUS - The FSD status for the Irp. 306 307 --*/ 308 309 { 310 NTSTATUS Status; 311 PIRP_CONTEXT IrpContext = NULL; 312 313 BOOLEAN TopLevel; 314 315 PAGED_CODE(); 316 317 DebugTrace(+1, Dbg, "FatFsdSetInformation\n", 0); 318 319 // 320 // Call the common set routine, with blocking allowed if synchronous 321 // 322 323 FsRtlEnterFileSystem(); 324 325 TopLevel = FatIsIrpTopLevel( Irp ); 326 327 _SEH2_TRY { 328 329 IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); 330 331 Status = FatCommonSetInformation( IrpContext, Irp ); 332 333 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 334 335 // 336 // We had some trouble trying to perform the requested 337 // operation, so we'll abort the I/O request with 338 // the error status that we get back from the 339 // execption code 340 // 341 342 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); 343 } _SEH2_END; 344 345 if (TopLevel) { IoSetTopLevelIrp( NULL ); } 346 347 FsRtlExitFileSystem(); 348 349 // 350 // And return to our caller 351 // 352 353 DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status); 354 355 UNREFERENCED_PARAMETER( VolumeDeviceObject ); 356 357 return Status; 358 } 359 360 361 _Requires_lock_held_(_Global_critical_region_) 362 NTSTATUS 363 FatCommonQueryInformation ( 364 IN PIRP_CONTEXT IrpContext, 365 IN PIRP Irp 366 ) 367 368 /*++ 369 370 Routine Description: 371 372 This is the common routine for querying file information called by both 373 the fsd and fsp threads. 374 375 Arguments: 376 377 Irp - Supplies the Irp being processed 378 379 Return Value: 380 381 NTSTATUS - The return status for the operation 382 383 --*/ 384 385 { 386 NTSTATUS Status; 387 388 PIO_STACK_LOCATION IrpSp; 389 390 PFILE_OBJECT FileObject; 391 392 LONG Length; 393 FILE_INFORMATION_CLASS FileInformationClass; 394 PVOID Buffer; 395 396 TYPE_OF_OPEN TypeOfOpen; 397 PVCB Vcb; 398 PFCB Fcb; 399 PCCB Ccb; 400 401 BOOLEAN FcbAcquired = FALSE; 402 BOOLEAN VcbAcquired = FALSE; 403 404 PFILE_ALL_INFORMATION AllInfo; 405 406 PAGED_CODE(); 407 408 // 409 // Get the current stack location 410 // 411 412 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 413 414 FileObject = IrpSp->FileObject; 415 416 DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0); 417 DebugTrace( 0, Dbg, "Irp = %p\n", Irp); 418 DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryFile.Length); 419 DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass); 420 DebugTrace( 0, Dbg, "->Buffer = %p\n", Irp->AssociatedIrp.SystemBuffer); 421 422 // 423 // Reference our input parameters to make things easier 424 // 425 426 Length = (LONG)IrpSp->Parameters.QueryFile.Length; 427 FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; 428 Buffer = Irp->AssociatedIrp.SystemBuffer; 429 430 // 431 // Decode the file object 432 // 433 434 TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); 435 436 Status = STATUS_SUCCESS; 437 438 _SEH2_TRY { 439 440 // 441 // Case on the type of open we're dealing with 442 // 443 444 switch (TypeOfOpen) { 445 446 case UserVolumeOpen: 447 448 // 449 // We cannot query the user volume open. 450 // 451 452 Status = STATUS_INVALID_PARAMETER; 453 break; 454 455 case UserFileOpen: 456 case UserDirectoryOpen: 457 case DirectoryFile: 458 459 460 // 461 // NameInfo requires synchronization with deletion in order to perform 462 // the full filename query. A lighter-weight way to do this would be per 463 // directory as the full name is built up and since the multiple Fcb 464 // lockorder is bottom up, this is conceivable. At this time, though, 465 // this change is safer. 466 // 467 468 if (FileInformationClass == FileNameInformation || 469 #if (NTDDI_VERSION >= NTDDI_VISTA) 470 FileInformationClass == FileNormalizedNameInformation || 471 #endif 472 FileInformationClass == FileAllInformation ) { 473 474 if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { 475 476 DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); 477 478 Status = FatFsdPostRequest( IrpContext, Irp ); 479 IrpContext = NULL; 480 Irp = NULL; 481 482 try_return( Status ); 483 } 484 485 VcbAcquired = TRUE; 486 } 487 488 // 489 // Acquire shared access to the fcb, except for a paging file 490 // in order to avoid deadlocks with Mm. 491 // 492 // The "removable" check was added specifically for ReadyBoost, 493 // which opens its cache file on a removable device as a paging file and 494 // relies on the file system to validate its mapping information after a 495 // power transition. 496 // 497 498 if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) || 499 FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { 500 501 if (!FatAcquireSharedFcb( IrpContext, Fcb )) { 502 503 DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0); 504 505 Status = FatFsdPostRequest( IrpContext, Irp ); 506 IrpContext = NULL; 507 Irp = NULL; 508 509 try_return( Status ); 510 } 511 512 FcbAcquired = TRUE; 513 } 514 515 // 516 // Make sure the Fcb is in a usable condition. This 517 // will raise an error condition if the fcb is unusable 518 // 519 520 FatVerifyFcb( IrpContext, Fcb ); 521 522 // 523 // Based on the information class we'll do different 524 // actions. Each of hte procedures that we're calling fills 525 // up the output buffer, if possible. They will raise the 526 // status STATUS_BUFFER_OVERFLOW for an insufficient buffer. 527 // This is considered a somewhat unusual case and is handled 528 // more cleanly with the exception mechanism rather than 529 // testing a return status value for each call. 530 // 531 532 switch (FileInformationClass) { 533 534 case FileAllInformation: 535 536 // 537 // For the all information class we'll typecast a local 538 // pointer to the output buffer and then call the 539 // individual routines to fill in the buffer. 540 // 541 542 AllInfo = Buffer; 543 Length -= (sizeof(FILE_ACCESS_INFORMATION) 544 + sizeof(FILE_MODE_INFORMATION) 545 + sizeof(FILE_ALIGNMENT_INFORMATION)); 546 547 FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length ); 548 FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length ); 549 FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length ); 550 FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length ); 551 FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length ); 552 FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, &AllInfo->NameInformation, &Length ); 553 554 break; 555 556 case FileBasicInformation: 557 558 FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); 559 break; 560 561 case FileStandardInformation: 562 563 FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length ); 564 break; 565 566 case FileInternalInformation: 567 568 FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length ); 569 break; 570 571 case FileEaInformation: 572 573 FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length ); 574 break; 575 576 case FilePositionInformation: 577 578 FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length ); 579 break; 580 581 case FileNameInformation: 582 583 FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, Buffer, &Length ); 584 break; 585 586 #if (NTDDI_VERSION >= NTDDI_VISTA) 587 case FileNormalizedNameInformation: 588 589 FatQueryNameInfo( IrpContext, Fcb, Ccb, TRUE, Buffer, &Length ); 590 break; 591 #endif 592 593 case FileAlternateNameInformation: 594 595 FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length ); 596 break; 597 598 case FileNetworkOpenInformation: 599 600 FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); 601 break; 602 603 default: 604 605 Status = STATUS_INVALID_PARAMETER; 606 break; 607 } 608 609 break; 610 611 default: 612 613 KdPrintEx((DPFLTR_FASTFAT_ID, 614 DPFLTR_INFO_LEVEL, 615 "FATQueryFile, Illegal TypeOfOpen = %08lx\n", 616 TypeOfOpen)); 617 618 Status = STATUS_INVALID_PARAMETER; 619 break; 620 } 621 622 // 623 // If we overflowed the buffer, set the length to 0 and change the 624 // status to STATUS_BUFFER_OVERFLOW. 625 // 626 627 if ( Length < 0 ) { 628 629 Status = STATUS_BUFFER_OVERFLOW; 630 631 Length = 0; 632 } 633 634 // 635 // Set the information field to the number of bytes actually filled in 636 // and then complete the request 637 // 638 639 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length; 640 641 try_exit: NOTHING; 642 } _SEH2_FINALLY { 643 644 DebugUnwind( FatCommonQueryInformation ); 645 646 if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } 647 if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } 648 649 if (!_SEH2_AbnormalTermination()) { 650 651 FatCompleteRequest( IrpContext, Irp, Status ); 652 } 653 654 DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status); 655 } _SEH2_END; 656 657 return Status; 658 } 659 660 661 _Requires_lock_held_(_Global_critical_region_) 662 NTSTATUS 663 FatCommonSetInformation ( 664 IN PIRP_CONTEXT IrpContext, 665 IN PIRP Irp 666 ) 667 668 /*++ 669 670 Routine Description: 671 672 This is the common routine for setting file information called by both 673 the fsd and fsp threads. 674 675 Arguments: 676 677 Irp - Supplies the Irp being processed 678 679 Return Value: 680 681 NTSTATUS - The return status for the operation 682 683 --*/ 684 685 { 686 NTSTATUS Status = STATUS_SUCCESS; 687 688 PIO_STACK_LOCATION IrpSp; 689 690 PFILE_OBJECT FileObject; 691 FILE_INFORMATION_CLASS FileInformationClass; 692 693 TYPE_OF_OPEN TypeOfOpen; 694 PVCB Vcb; 695 PFCB Fcb; 696 PCCB Ccb; 697 698 BOOLEAN VcbAcquired = FALSE; 699 BOOLEAN FcbAcquired = FALSE; 700 701 PAGED_CODE(); 702 703 // 704 // Get the current stack location 705 // 706 707 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 708 709 DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0); 710 DebugTrace( 0, Dbg, "Irp = %p\n", Irp); 711 DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.SetFile.Length); 712 DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass); 713 DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->Parameters.SetFile.FileObject); 714 DebugTrace( 0, Dbg, "->ReplaceIfExists = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists); 715 DebugTrace( 0, Dbg, "->Buffer = %p\n", Irp->AssociatedIrp.SystemBuffer); 716 717 // 718 // Reference our input parameters to make things easier 719 // 720 721 FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass; 722 FileObject = IrpSp->FileObject; 723 724 // 725 // Decode the file object 726 // 727 728 TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); 729 730 _SEH2_TRY { 731 732 // 733 // Case on the type of open we're dealing with 734 // 735 736 switch (TypeOfOpen) { 737 738 case UserVolumeOpen: 739 740 // 741 // We cannot query the user volume open. 742 // 743 744 try_return( Status = STATUS_INVALID_PARAMETER ); 745 break; 746 747 case UserFileOpen: 748 749 if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) && 750 ((FileInformationClass == FileEndOfFileInformation) || 751 (FileInformationClass == FileAllocationInformation) || 752 (FileInformationClass == FileValidDataLengthInformation))) { 753 754 // 755 // We check whether we can proceed 756 // based on the state of the file oplocks. 757 // 758 759 Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), 760 Irp, 761 IrpContext, 762 NULL, 763 NULL ); 764 765 if (Status != STATUS_SUCCESS) { 766 767 try_return( Status ); 768 } 769 770 // 771 // Set the flag indicating if Fast I/O is possible 772 // 773 774 Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); 775 } 776 break; 777 778 case UserDirectoryOpen: 779 780 break; 781 782 default: 783 784 try_return( Status = STATUS_INVALID_PARAMETER ); 785 } 786 787 // 788 // We can only do a set on a nonroot dcb, so we do the test 789 // and then fall through to the user file open code. 790 // 791 792 if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { 793 794 if (FileInformationClass == FileDispositionInformation) { 795 796 try_return( Status = STATUS_CANNOT_DELETE ); 797 } 798 799 try_return( Status = STATUS_INVALID_PARAMETER ); 800 } 801 802 // 803 // In the following two cases, we cannot have creates occuring 804 // while we are here, so acquire the volume exclusive. 805 // 806 807 if ((FileInformationClass == FileDispositionInformation) || 808 (FileInformationClass == FileRenameInformation)) { 809 810 if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { 811 812 DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); 813 814 Status = FatFsdPostRequest( IrpContext, Irp ); 815 Irp = NULL; 816 IrpContext = NULL; 817 818 try_return( Status ); 819 } 820 821 VcbAcquired = TRUE; 822 823 // 824 // Make sure we haven't been called recursively by a filter inside an existing 825 // create request. 826 // 827 828 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS)) { 829 830 #ifdef _MSC_VER 831 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 832 #endif 833 FatBugCheck( 0, 0, 0); 834 } 835 } 836 837 // 838 // Acquire exclusive access to the Fcb, We use exclusive 839 // because it is probable that one of the subroutines 840 // that we call will need to monkey with file allocation, 841 // create/delete extra fcbs. So we're willing to pay the 842 // cost of exclusive Fcb access. 843 // 844 // Note that we do not acquire the resource for paging file 845 // operations in order to avoid deadlock with Mm. 846 // 847 // The "removable" check was added specifically for ReadyBoost, 848 // which opens its cache file on a removable device as a paging file and 849 // relies on the file system to validate its mapping information after a 850 // power transition. 851 // 852 853 if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) || 854 FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { 855 856 if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) { 857 858 DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0); 859 860 Status = FatFsdPostRequest( IrpContext, Irp ); 861 Irp = NULL; 862 IrpContext = NULL; 863 864 try_return( Status ); 865 } 866 867 FcbAcquired = TRUE; 868 } 869 870 Status = STATUS_SUCCESS; 871 872 // 873 // Make sure the Fcb is in a usable condition. This 874 // will raise an error condition if the fcb is unusable 875 // 876 877 FatVerifyFcb( IrpContext, Fcb ); 878 879 // 880 // Now that we've acquired the file, do an oplock check if the operation 881 // so warrants. 882 // 883 884 if (FatIsFileOplockable( Fcb )&& 885 ((FileInformationClass == FileRenameInformation) || 886 ((FileInformationClass == FileDispositionInformation) && 887 ((PFILE_DISPOSITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer)->DeleteFile))) { 888 889 Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), 890 Irp, 891 IrpContext, 892 FatOplockComplete, 893 NULL ); 894 895 // 896 // Set the flag indicating if Fast I/O is possible 897 // 898 899 Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); 900 901 // 902 // If STATUS_PENDING is returned it means the oplock 903 // package has the Irp. Don't complete the request here. 904 // 905 906 if (Status == STATUS_PENDING) { 907 908 Irp = NULL; 909 IrpContext = NULL; 910 } 911 912 if (!NT_SUCCESS( Status ) || 913 (Status == STATUS_PENDING)) { 914 915 try_return( Status ); 916 } 917 } 918 919 // 920 // Based on the information class we'll do different 921 // actions. Each of the procedures that we're calling will either 922 // complete the request of send the request off to the fsp 923 // to do the work. 924 // 925 926 switch (FileInformationClass) { 927 928 case FileBasicInformation: 929 930 Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb ); 931 break; 932 933 case FileDispositionInformation: 934 935 // 936 // If this is on deferred flush media, we have to be able to wait. 937 // 938 939 if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && 940 !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) { 941 942 Status = FatFsdPostRequest( IrpContext, Irp ); 943 Irp = NULL; 944 IrpContext = NULL; 945 946 } else { 947 948 Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb ); 949 } 950 951 break; 952 953 case FileRenameInformation: 954 955 // 956 // We proceed with this operation only if we can wait 957 // 958 959 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { 960 961 Status = FatFsdPostRequest( IrpContext, Irp ); 962 Irp = NULL; 963 IrpContext = NULL; 964 965 } else { 966 967 Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb ); 968 969 // 970 // If STATUS_PENDING is returned it means the oplock 971 // package has the Irp. Don't complete the request here. 972 // 973 974 if (Status == STATUS_PENDING) { 975 Irp = NULL; 976 IrpContext = NULL; 977 } 978 } 979 980 break; 981 982 case FilePositionInformation: 983 984 Status = FatSetPositionInfo( IrpContext, Irp, FileObject ); 985 break; 986 987 case FileLinkInformation: 988 989 Status = STATUS_INVALID_DEVICE_REQUEST; 990 break; 991 992 case FileAllocationInformation: 993 994 Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject ); 995 break; 996 997 case FileEndOfFileInformation: 998 999 Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb ); 1000 break; 1001 1002 case FileValidDataLengthInformation: 1003 1004 Status = FatSetValidDataLengthInfo( IrpContext, Irp, FileObject, Fcb, Ccb ); 1005 break; 1006 1007 default: 1008 1009 Status = STATUS_INVALID_PARAMETER; 1010 break; 1011 } 1012 1013 if ( IrpContext != NULL ) { 1014 1015 FatUnpinRepinnedBcbs( IrpContext ); 1016 } 1017 1018 try_exit: NOTHING; 1019 } _SEH2_FINALLY { 1020 1021 DebugUnwind( FatCommonSetInformation ); 1022 1023 if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } 1024 1025 if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } 1026 1027 if (!_SEH2_AbnormalTermination()) { 1028 1029 FatCompleteRequest( IrpContext, Irp, Status ); 1030 } 1031 1032 DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status); 1033 } _SEH2_END; 1034 1035 return Status; 1036 } 1037 1038 1039 // 1040 // Internal Support Routine 1041 // 1042 1043 VOID 1044 FatQueryBasicInfo ( 1045 IN PIRP_CONTEXT IrpContext, 1046 IN PFCB Fcb, 1047 IN PFILE_OBJECT FileObject, 1048 IN OUT PFILE_BASIC_INFORMATION Buffer, 1049 IN OUT PLONG Length 1050 ) 1051 1052 /*++ 1053 Description: 1054 1055 This routine performs the query basic information function for fat. 1056 1057 Arguments: 1058 1059 Fcb - Supplies the Fcb being queried, it has been verified 1060 1061 FileObject - Supplies the flag bit that indicates the file was modified. 1062 1063 Buffer - Supplies a pointer to the buffer where the information is to 1064 be returned 1065 1066 Length - Supplies the length of the buffer in bytes, and receives the 1067 remaining bytes free in the buffer upon return. 1068 1069 Return Value: 1070 1071 None 1072 1073 --*/ 1074 1075 { 1076 PAGED_CODE(); 1077 1078 UNREFERENCED_PARAMETER( FileObject ); 1079 UNREFERENCED_PARAMETER( IrpContext ); 1080 1081 DebugTrace(+1, Dbg, "FatQueryBasicInfo...\n", 0); 1082 1083 // 1084 // Zero out the output buffer, and set it to indicate that 1085 // the query is a normal file. Later we might overwrite the 1086 // attribute. 1087 // 1088 1089 RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) ); 1090 1091 // 1092 // Extract the data and fill in the non zero fields of the output 1093 // buffer 1094 // 1095 1096 if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { 1097 1098 // 1099 // We have to munge a lie on the fly. Every time we have to 1100 // use 1/1/80 we need to convert to GMT since the TZ may have 1101 // changed on us. 1102 // 1103 1104 ExLocalTimeToSystemTime( &FatJanOne1980, 1105 &Buffer->LastWriteTime ); 1106 Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime; 1107 1108 } else { 1109 1110 Buffer->LastWriteTime = Fcb->LastWriteTime; 1111 Buffer->CreationTime = Fcb->CreationTime; 1112 Buffer->LastAccessTime = Fcb->LastAccessTime; 1113 } 1114 1115 Buffer->FileAttributes = Fcb->DirentFatFlags; 1116 1117 1118 // 1119 // If the temporary flag is set, then set it in the buffer. 1120 // 1121 1122 if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { 1123 1124 SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); 1125 } 1126 1127 // 1128 // If no attributes were set, set the normal bit. 1129 // 1130 1131 if (Buffer->FileAttributes == 0) { 1132 1133 Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; 1134 } 1135 1136 // 1137 // Update the length and status output variables 1138 // 1139 1140 *Length -= sizeof( FILE_BASIC_INFORMATION ); 1141 1142 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1143 1144 DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0); 1145 1146 return; 1147 } 1148 1149 1150 // 1151 // Internal Support Routine 1152 // 1153 1154 _Requires_lock_held_(_Global_critical_region_) 1155 VOID 1156 FatQueryStandardInfo ( 1157 IN PIRP_CONTEXT IrpContext, 1158 IN PFCB Fcb, 1159 IN OUT PFILE_STANDARD_INFORMATION Buffer, 1160 IN OUT PLONG Length 1161 ) 1162 1163 /*++ 1164 1165 Routine Description: 1166 1167 This routine performs the query standard information function for fat. 1168 1169 Arguments: 1170 1171 Fcb - Supplies the Fcb being queried, it has been verified 1172 1173 Buffer - Supplies a pointer to the buffer where the information is to 1174 be returned 1175 1176 Length - Supplies the length of the buffer in bytes, and receives the 1177 remaining bytes free in the buffer upon return. 1178 1179 Return Value: 1180 1181 None 1182 1183 --*/ 1184 1185 { 1186 PAGED_CODE(); 1187 1188 DebugTrace(+1, Dbg, "FatQueryStandardInfo...\n", 0); 1189 1190 // 1191 // Zero out the output buffer, and fill in the number of links 1192 // and the delete pending flag. 1193 // 1194 1195 RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) ); 1196 1197 Buffer->NumberOfLinks = 1; 1198 Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); 1199 1200 // 1201 // Case on whether this is a file or a directory, and extract 1202 // the information and fill in the fcb/dcb specific parts 1203 // of the output buffer 1204 // 1205 1206 if (NodeType(Fcb) == FAT_NTC_FCB) { 1207 1208 if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { 1209 1210 FatLookupFileAllocationSize( IrpContext, Fcb ); 1211 } 1212 1213 Buffer->AllocationSize = Fcb->Header.AllocationSize; 1214 Buffer->EndOfFile = Fcb->Header.FileSize; 1215 1216 Buffer->Directory = FALSE; 1217 1218 } else { 1219 1220 Buffer->Directory = TRUE; 1221 } 1222 1223 // 1224 // Update the length and status output variables 1225 // 1226 1227 *Length -= sizeof( FILE_STANDARD_INFORMATION ); 1228 1229 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1230 1231 DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0); 1232 1233 return; 1234 } 1235 1236 1237 // 1238 // Internal Support Routine 1239 // 1240 1241 VOID 1242 FatQueryInternalInfo ( 1243 IN PIRP_CONTEXT IrpContext, 1244 IN PFCB Fcb, 1245 IN OUT PFILE_INTERNAL_INFORMATION Buffer, 1246 IN OUT PLONG Length 1247 ) 1248 1249 /*++ 1250 1251 Routine Description: 1252 1253 This routine performs the query internal information function for fat. 1254 1255 Arguments: 1256 1257 Fcb - Supplies the Fcb being queried, it has been verified 1258 1259 Buffer - Supplies a pointer to the buffer where the information is to 1260 be returned 1261 1262 Length - Supplies the length of the buffer in bytes, and receives the 1263 remaining bytes free in the buffer upon return. 1264 1265 Return Value: 1266 1267 None 1268 1269 --*/ 1270 1271 { 1272 PAGED_CODE(); 1273 1274 UNREFERENCED_PARAMETER( IrpContext ); 1275 1276 DebugTrace(+1, Dbg, "FatQueryInternalInfo...\n", 0); 1277 1278 _SEH2_TRY { 1279 1280 Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb ); 1281 1282 // 1283 // Update the length and status output variables 1284 // 1285 1286 *Length -= sizeof( FILE_INTERNAL_INFORMATION ); 1287 1288 } _SEH2_FINALLY { 1289 1290 DebugUnwind( FatQueryInternalInfo ); 1291 1292 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1293 1294 DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0); 1295 } _SEH2_END; 1296 1297 return; 1298 } 1299 1300 1301 // 1302 // Internal Support Routine 1303 // 1304 1305 VOID 1306 FatQueryEaInfo ( 1307 IN PIRP_CONTEXT IrpContext, 1308 IN PFCB Fcb, 1309 IN OUT PFILE_EA_INFORMATION Buffer, 1310 IN OUT PLONG Length 1311 ) 1312 1313 /*++ 1314 1315 Routine Description: 1316 1317 This routine performs the query Ea information function for fat. 1318 1319 Arguments: 1320 1321 Fcb - Supplies the Fcb being queried, it has been verified 1322 1323 Buffer - Supplies a pointer to the buffer where the information is to 1324 be returned 1325 1326 Length - Supplies the length of the buffer in bytes, and receives the 1327 remaining bytes free in the buffer upon return. 1328 1329 Return Value: 1330 1331 None 1332 1333 --*/ 1334 1335 { 1336 PBCB Bcb; 1337 1338 PAGED_CODE(); 1339 1340 UNREFERENCED_PARAMETER( Fcb ); 1341 UNREFERENCED_PARAMETER( IrpContext ); 1342 1343 DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0); 1344 1345 Bcb = NULL; 1346 1347 _SEH2_TRY { 1348 1349 // 1350 // Zero out the output buffer 1351 // 1352 1353 RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) ); 1354 1355 #if 0 1356 // 1357 // The Root dcb does not have any EAs so don't look for any. Fat32 1358 // doesn't have any, either. 1359 // 1360 1361 if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB && 1362 !FatIsFat32( Fcb->Vcb )) { 1363 1364 PDIRENT Dirent; 1365 1366 // 1367 // Try to get the dirent for this file. 1368 // 1369 1370 FatGetDirentFromFcbOrDcb( IrpContext, 1371 Fcb, 1372 &Dirent, 1373 &Bcb ); 1374 1375 if (Dirent != NULL) { 1376 1377 // 1378 // Get a the size needed to store the full eas for the file. 1379 // 1380 1381 FatGetEaLength( IrpContext, 1382 Fcb->Vcb, 1383 Dirent, 1384 &Buffer->EaSize ); 1385 } 1386 } 1387 #endif 1388 1389 // 1390 // Update the length and status output variables 1391 // 1392 1393 *Length -= sizeof( FILE_EA_INFORMATION ); 1394 1395 } _SEH2_FINALLY { 1396 1397 DebugUnwind( FatQueryEaInfo ); 1398 1399 // 1400 // Unpin the dirent if pinned. 1401 // 1402 1403 FatUnpinBcb( IrpContext, Bcb ); 1404 1405 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1406 1407 DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0); 1408 } _SEH2_END; 1409 } 1410 1411 1412 // 1413 // Internal Support Routine 1414 // 1415 1416 VOID 1417 FatQueryPositionInfo ( 1418 IN PIRP_CONTEXT IrpContext, 1419 IN PFILE_OBJECT FileObject, 1420 IN OUT PFILE_POSITION_INFORMATION Buffer, 1421 IN OUT PLONG Length 1422 ) 1423 1424 /*++ 1425 1426 Routine Description: 1427 1428 This routine performs the query position information function for fat. 1429 1430 Arguments: 1431 1432 FileObject - Supplies the File object being queried 1433 1434 Buffer - Supplies a pointer to the buffer where the information is to 1435 be returned 1436 1437 Length - Supplies the length of the buffer in bytes, and receives the 1438 remaining bytes free in the buffer upon return. 1439 1440 Return Value: 1441 1442 None 1443 1444 --*/ 1445 1446 { 1447 PAGED_CODE(); 1448 1449 DebugTrace(+1, Dbg, "FatQueryPositionInfo...\n", 0); 1450 1451 // 1452 // Get the current position found in the file object. 1453 // 1454 1455 Buffer->CurrentByteOffset = FileObject->CurrentByteOffset; 1456 1457 // 1458 // Update the length and status output variables 1459 // 1460 1461 *Length -= sizeof( FILE_POSITION_INFORMATION ); 1462 1463 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1464 1465 DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0); 1466 1467 UNREFERENCED_PARAMETER( IrpContext ); 1468 1469 return; 1470 } 1471 1472 1473 // 1474 // Internal Support Routine 1475 // 1476 1477 _Requires_lock_held_(_Global_critical_region_) 1478 VOID 1479 FatQueryNameInfo ( 1480 IN PIRP_CONTEXT IrpContext, 1481 IN PFCB Fcb, 1482 IN PCCB Ccb, 1483 IN BOOLEAN Normalized, 1484 IN OUT PFILE_NAME_INFORMATION Buffer, 1485 IN OUT PLONG Length 1486 ) 1487 1488 /*++ 1489 1490 Routine Description: 1491 1492 This routine performs the query name information function for fat. 1493 1494 Arguments: 1495 1496 Fcb - Supplies the Fcb being queried, it has been verified 1497 1498 Ccb - Supplies the Ccb for the context of the user open 1499 1500 Normalized - if true the caller wants a normalized name (w/out short names). 1501 This means we're servicing a FileNormalizedNameInformation query. 1502 1503 Buffer - Supplies a pointer to the buffer where the information is to 1504 be returned 1505 1506 Length - Supplies the length of the buffer in bytes, and receives the 1507 remaining bytes free in the buffer upon return. 1508 1509 Return Value: 1510 1511 None 1512 1513 --*/ 1514 1515 { 1516 ULONG BytesToCopy; 1517 LONG TrimLength; 1518 BOOLEAN Overflow = FALSE; 1519 1520 PAGED_CODE(); 1521 1522 DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0); 1523 1524 // 1525 // Convert the name to UNICODE 1526 // 1527 1528 *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]); 1529 1530 // 1531 // Use the full filename to build the path up. If we wanted to be 1532 // slick in the future, we'd just build the path directly into the 1533 // return buffer and avoid constructing the full filename, but since 1534 // the full filename winds up being required so often lets not 1535 // over optimize this case yet. 1536 // 1537 1538 if (Fcb->FullFileName.Buffer == NULL) { 1539 1540 FatSetFullFileNameInFcb( IrpContext, Fcb ); 1541 } 1542 1543 // 1544 // Here is where it gets a smidge tricky. FinalNameLength is the length 1545 // of the LFN element if it exists, and since the long name is always used 1546 // to build FullFileName, we have two cases: 1547 // 1548 // 1) short name: use FinalNameLength to tear off the path from FullFileName 1549 // and append the UNICODE converted short name. 1550 // 2) long name: just use FullFileName 1551 // 1552 // We bias to the name the user thinks they opened by. This winds 1553 // up fixing some oddball tunneling cases where intermediate filters 1554 // translate operations like delete into renames - this lets them 1555 // do the operation in the context of the name the user was using. 1556 // 1557 // It also matches what NTFS does, and so we have the definition of 1558 // correct behavior. 1559 // 1560 1561 // 1562 // 1563 // Assume there is no long name and we are just going to use 1564 // FullFileName. 1565 // 1566 1567 TrimLength = 0; 1568 1569 // 1570 // If a LongName exists, the caller isn't asking for the normalized name, 1571 // and the original open was by the short name then set TrimLength to point 1572 // to the place where the short name goes. 1573 // 1574 // Note: The Ccb can be NULL. The lazy writer calls to get the name of 1575 // a DirectoryOpen FILE_OBJECT that it wants to display in the lost 1576 // delayed write popup. Handle this case by just using the FileFullName. 1577 // 1578 1579 if (!Normalized && 1580 (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL)) { 1581 1582 if ((Ccb != NULL) && 1583 FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) { 1584 1585 TrimLength = Fcb->FinalNameLength; 1586 } 1587 } 1588 1589 if (*Length < Fcb->FullFileName.Length - TrimLength) { 1590 1591 BytesToCopy = *Length; 1592 Overflow = TRUE; 1593 1594 } else { 1595 1596 BytesToCopy = Fcb->FullFileName.Length - TrimLength; 1597 *Length -= BytesToCopy; 1598 } 1599 1600 RtlCopyMemory( &Buffer->FileName[0], 1601 Fcb->FullFileName.Buffer, 1602 BytesToCopy ); 1603 1604 // 1605 // Note that this is just the amount of name we've copied so far. It'll 1606 // either be all of it (long) or the path element including the \ (short). 1607 // 1608 1609 Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength; 1610 1611 // 1612 // If we trimmed off the name element, this is the short name case. Pick 1613 // up the UNICODE conversion and append it. 1614 // 1615 1616 if (TrimLength != 0) { 1617 1618 UNICODE_STRING ShortName; 1619 WCHAR ShortNameBuffer[12]; 1620 NTSTATUS Status; 1621 1622 // 1623 // Convert the short name to UNICODE and figure out how much 1624 // of it can fit. Again, we always bump the returned length 1625 // to indicate how much is available even if we can't return it. 1626 // 1627 1628 ShortName.Length = 0; 1629 ShortName.MaximumLength = sizeof(ShortNameBuffer); 1630 ShortName.Buffer = ShortNameBuffer; 1631 1632 #ifdef _MSC_VER 1633 #pragma prefast( suppress:28931, "needed for debug build" ) 1634 #endif 1635 Status = RtlOemStringToCountedUnicodeString( &ShortName, 1636 &Fcb->ShortName.Name.Oem, 1637 FALSE ); 1638 1639 NT_ASSERT( Status == STATUS_SUCCESS ); 1640 1641 if (!Overflow) { 1642 1643 if (*Length < ShortName.Length) { 1644 1645 BytesToCopy = *Length; 1646 Overflow = TRUE; 1647 1648 } else { 1649 1650 BytesToCopy = ShortName.Length; 1651 *Length -= BytesToCopy; 1652 } 1653 1654 RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength, 1655 ShortName.Buffer, 1656 BytesToCopy ); 1657 } 1658 1659 Buffer->FileNameLength += ShortName.Length; 1660 } 1661 1662 if (Overflow) { 1663 1664 *Length = -1; 1665 } 1666 1667 // 1668 // Return to caller 1669 // 1670 1671 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1672 1673 DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0); 1674 1675 UNREFERENCED_PARAMETER( IrpContext ); 1676 1677 return; 1678 } 1679 1680 1681 // 1682 // Internal Support Routine 1683 // 1684 1685 VOID 1686 FatQueryShortNameInfo ( 1687 IN PIRP_CONTEXT IrpContext, 1688 IN PFCB Fcb, 1689 IN OUT PFILE_NAME_INFORMATION Buffer, 1690 IN OUT PLONG Length 1691 ) 1692 1693 /*++ 1694 1695 Routine Description: 1696 1697 This routine queries the short name of the file. 1698 1699 Arguments: 1700 1701 Fcb - Supplies the Fcb being queried, it has been verified 1702 1703 Buffer - Supplies a pointer to the buffer where the information is to 1704 be returned 1705 1706 Length - Supplies the length of the buffer in bytes, and receives the 1707 remaining bytes free in the buffer upon return. 1708 1709 Return Value: 1710 1711 None 1712 1713 --*/ 1714 1715 { 1716 NTSTATUS Status; 1717 1718 ULONG BytesToCopy; 1719 WCHAR ShortNameBuffer[12]; 1720 UNICODE_STRING ShortName; 1721 1722 PAGED_CODE(); 1723 1724 DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0); 1725 1726 // 1727 // Convert the name to UNICODE 1728 // 1729 1730 ShortName.Length = 0; 1731 ShortName.MaximumLength = sizeof(ShortNameBuffer); 1732 ShortName.Buffer = ShortNameBuffer; 1733 1734 *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]); 1735 1736 #ifdef _MSC_VER 1737 #pragma prefast( suppress:28931, "needed for debug build" ) 1738 #endif 1739 Status = RtlOemStringToCountedUnicodeString( &ShortName, 1740 &Fcb->ShortName.Name.Oem, 1741 FALSE ); 1742 1743 NT_ASSERT( Status == STATUS_SUCCESS ); 1744 1745 // 1746 // If we overflow, set *Length to -1 as a flag. 1747 // 1748 1749 if (*Length < ShortName.Length) { 1750 1751 BytesToCopy = *Length; 1752 *Length = -1; 1753 1754 } else { 1755 1756 BytesToCopy = ShortName.Length; 1757 *Length -= ShortName.Length; 1758 } 1759 1760 RtlCopyMemory( &Buffer->FileName[0], 1761 &ShortName.Buffer[0], 1762 BytesToCopy ); 1763 1764 Buffer->FileNameLength = ShortName.Length; 1765 1766 // 1767 // Return to caller 1768 // 1769 1770 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1771 1772 DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0); 1773 1774 UNREFERENCED_PARAMETER( IrpContext ); 1775 1776 return; 1777 } 1778 1779 1780 // 1781 // Internal Support Routine 1782 // 1783 1784 _Requires_lock_held_(_Global_critical_region_) 1785 VOID 1786 FatQueryNetworkInfo ( 1787 IN PIRP_CONTEXT IrpContext, 1788 IN PFCB Fcb, 1789 IN PFILE_OBJECT FileObject, 1790 IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, 1791 IN OUT PLONG Length 1792 ) 1793 1794 /*++ 1795 Description: 1796 1797 This routine performs the query network open information function for fat. 1798 1799 Arguments: 1800 1801 Fcb - Supplies the Fcb being queried, it has been verified 1802 1803 FileObject - Supplies the flag bit that indicates the file was modified. 1804 1805 Buffer - Supplies a pointer to the buffer where the information is to 1806 be returned 1807 1808 Length - Supplies the length of the buffer in bytes, and receives the 1809 remaining bytes free in the buffer upon return. 1810 1811 Return Value: 1812 1813 None 1814 1815 --*/ 1816 1817 { 1818 PAGED_CODE(); 1819 1820 UNREFERENCED_PARAMETER( FileObject ); 1821 1822 DebugTrace(+1, Dbg, "FatQueryNetworkInfo...\n", 0); 1823 1824 // 1825 // Zero out the output buffer, and set it to indicate that 1826 // the query is a normal file. Later we might overwrite the 1827 // attribute. 1828 // 1829 1830 RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) ); 1831 1832 // 1833 // Extract the data and fill in the non zero fields of the output 1834 // buffer 1835 // 1836 1837 if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { 1838 1839 // 1840 // We have to munge a lie on the fly. Every time we have to 1841 // use 1/1/80 we need to convert to GMT since the TZ may have 1842 // changed on us. 1843 // 1844 1845 ExLocalTimeToSystemTime( &FatJanOne1980, 1846 &Buffer->LastWriteTime ); 1847 Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime; 1848 1849 } else { 1850 1851 Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart; 1852 Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart; 1853 Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart; 1854 } 1855 1856 Buffer->FileAttributes = Fcb->DirentFatFlags; 1857 1858 1859 // 1860 // If the temporary flag is set, then set it in the buffer. 1861 // 1862 1863 if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { 1864 1865 SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); 1866 } 1867 1868 // 1869 // If no attributes were set, set the normal bit. 1870 // 1871 1872 if (Buffer->FileAttributes == 0) { 1873 1874 Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; 1875 } 1876 // 1877 // Case on whether this is a file or a directory, and extract 1878 // the information and fill in the fcb/dcb specific parts 1879 // of the output buffer 1880 // 1881 1882 if (NodeType(Fcb) == FAT_NTC_FCB) { 1883 1884 if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { 1885 1886 FatLookupFileAllocationSize( IrpContext, Fcb ); 1887 } 1888 1889 Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart; 1890 Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart; 1891 } 1892 1893 // 1894 // Update the length and status output variables 1895 // 1896 1897 *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION ); 1898 1899 DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); 1900 1901 DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0); 1902 1903 return; 1904 } 1905 1906 1907 // 1908 // Internal Support routine 1909 // 1910 1911 _Requires_lock_held_(_Global_critical_region_) 1912 NTSTATUS 1913 FatSetBasicInfo ( 1914 IN PIRP_CONTEXT IrpContext, 1915 IN PIRP Irp, 1916 IN PFCB Fcb, 1917 IN PCCB Ccb 1918 ) 1919 1920 /*++ 1921 1922 Routine Description: 1923 1924 This routine performs the set basic information for fat. It either 1925 completes the request or enqueues it off to the fsp. 1926 1927 Arguments: 1928 1929 Irp - Supplies the irp being processed 1930 1931 Fcb - Supplies the Fcb or Dcb being processed, already known not to 1932 be the root dcb 1933 1934 Ccb - Supplies the flag bit that control updating the last modify 1935 time on cleanup. 1936 1937 Return Value: 1938 1939 NTSTATUS - The result of this operation if it completes without 1940 an exception. 1941 1942 --*/ 1943 1944 { 1945 NTSTATUS Status; 1946 1947 PFILE_BASIC_INFORMATION Buffer; 1948 1949 PDIRENT Dirent; 1950 PBCB DirentBcb; 1951 1952 FAT_TIME_STAMP CreationTime = {0}; 1953 UCHAR CreationMSec = 0; 1954 FAT_TIME_STAMP LastWriteTime = {0}; 1955 FAT_TIME_STAMP LastAccessTime = {0}; 1956 FAT_DATE LastAccessDate = {0}; 1957 UCHAR Attributes; 1958 1959 BOOLEAN ModifyCreation = FALSE; 1960 BOOLEAN ModifyLastWrite = FALSE; 1961 BOOLEAN ModifyLastAccess = FALSE; 1962 1963 #if (NTDDI_VERSION >= NTDDI_WIN8) 1964 BOOLEAN ModifiedAttributes = FALSE; 1965 #endif 1966 1967 LARGE_INTEGER LargeCreationTime = {0}; 1968 LARGE_INTEGER LargeLastWriteTime = {0}; 1969 LARGE_INTEGER LargeLastAccessTime = {0}; 1970 1971 ULONG NotifyFilter = 0; 1972 1973 PAGED_CODE(); 1974 1975 DebugTrace(+1, Dbg, "FatSetBasicInfo...\n", 0); 1976 1977 Buffer = Irp->AssociatedIrp.SystemBuffer; 1978 1979 // 1980 // If the user is specifying -1 for a field, that means 1981 // we should leave that field unchanged, even if we might 1982 // have otherwise set it ourselves. We'll set the Ccb flag 1983 // saying that the user set the field so that we 1984 // don't do our default updating. 1985 // 1986 // We set the field to 0 then so we know not to actually 1987 // set the field to the user-specified (and in this case, 1988 // illegal) value. 1989 // 1990 1991 if (Buffer->LastWriteTime.QuadPart == -1) { 1992 1993 SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); 1994 Buffer->LastWriteTime.QuadPart = 0; 1995 } 1996 1997 if (Buffer->LastAccessTime.QuadPart == -1) { 1998 1999 SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); 2000 Buffer->LastAccessTime.QuadPart = 0; 2001 } 2002 2003 if (Buffer->CreationTime.QuadPart == -1) { 2004 2005 SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); 2006 Buffer->CreationTime.QuadPart = 0; 2007 } 2008 2009 DirentBcb = NULL; 2010 2011 Status = STATUS_SUCCESS; 2012 2013 _SEH2_TRY { 2014 2015 LARGE_INTEGER FatLocalDecThirtyOne1979; 2016 LARGE_INTEGER FatLocalJanOne1980; 2017 2018 ExLocalTimeToSystemTime( &FatDecThirtyOne1979, 2019 &FatLocalDecThirtyOne1979 ); 2020 2021 ExLocalTimeToSystemTime( &FatJanOne1980, 2022 &FatLocalJanOne1980 ); 2023 2024 // 2025 // Get a pointer to the dirent 2026 // 2027 2028 NT_ASSERT( Fcb->FcbCondition == FcbGood ); 2029 2030 FatGetDirentFromFcbOrDcb( IrpContext, 2031 Fcb, 2032 FALSE, 2033 &Dirent, 2034 &DirentBcb ); 2035 // 2036 // Check if the user specified a non-zero creation time 2037 // 2038 2039 if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) { 2040 2041 LargeCreationTime = Buffer->CreationTime; 2042 2043 // 2044 // Convert the Nt time to a Fat time 2045 // 2046 2047 if ( !FatNtTimeToFatTime( IrpContext, 2048 &LargeCreationTime, 2049 FALSE, 2050 &CreationTime, 2051 &CreationMSec )) { 2052 2053 // 2054 // Special case the value 12/31/79 and treat this as 1/1/80. 2055 // This '79 value can happen because of time zone issues. 2056 // 2057 2058 if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && 2059 (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) { 2060 2061 CreationTime = FatTimeJanOne1980; 2062 LargeCreationTime = FatLocalJanOne1980; 2063 2064 } else { 2065 2066 DebugTrace(0, Dbg, "Invalid CreationTime\n", 0); 2067 try_return( Status = STATUS_INVALID_PARAMETER ); 2068 } 2069 2070 // 2071 // Don't worry about CreationMSec 2072 // 2073 2074 CreationMSec = 0; 2075 } 2076 2077 ModifyCreation = TRUE; 2078 } 2079 2080 // 2081 // Check if the user specified a non-zero last access time 2082 // 2083 2084 if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) { 2085 2086 LargeLastAccessTime = Buffer->LastAccessTime; 2087 2088 // 2089 // Convert the Nt time to a Fat time 2090 // 2091 2092 if ( !FatNtTimeToFatTime( IrpContext, 2093 &LargeLastAccessTime, 2094 TRUE, 2095 &LastAccessTime, 2096 NULL )) { 2097 2098 // 2099 // Special case the value 12/31/79 and treat this as 1/1/80. 2100 // This '79 value can happen because of time zone issues. 2101 // 2102 2103 if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && 2104 (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) { 2105 2106 LastAccessTime = FatTimeJanOne1980; 2107 LargeLastAccessTime = FatLocalJanOne1980; 2108 2109 } else { 2110 2111 DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0); 2112 try_return( Status = STATUS_INVALID_PARAMETER ); 2113 } 2114 } 2115 2116 LastAccessDate = LastAccessTime.Date; 2117 ModifyLastAccess = TRUE; 2118 } 2119 2120 // 2121 // Check if the user specified a non-zero last write time 2122 // 2123 2124 if (Buffer->LastWriteTime.QuadPart != 0) { 2125 2126 // 2127 // First do a quick check here if the this time is the same 2128 // time as LastAccessTime. 2129 // 2130 2131 if (ModifyLastAccess && 2132 (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) { 2133 2134 ModifyLastWrite = TRUE; 2135 LastWriteTime = LastAccessTime; 2136 LargeLastWriteTime = LargeLastAccessTime; 2137 2138 } else { 2139 2140 LargeLastWriteTime = Buffer->LastWriteTime; 2141 2142 // 2143 // Convert the Nt time to a Fat time 2144 // 2145 2146 if ( !FatNtTimeToFatTime( IrpContext, 2147 &LargeLastWriteTime, 2148 TRUE, 2149 &LastWriteTime, 2150 NULL )) { 2151 2152 2153 // 2154 // Special case the value 12/31/79 and treat this as 1/1/80. 2155 // This '79 value can happen because of time zone issues. 2156 // 2157 2158 if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && 2159 (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) { 2160 2161 LastWriteTime = FatTimeJanOne1980; 2162 LargeLastWriteTime = FatLocalJanOne1980; 2163 2164 } else { 2165 2166 DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0); 2167 try_return( Status = STATUS_INVALID_PARAMETER ); 2168 } 2169 } 2170 2171 ModifyLastWrite = TRUE; 2172 } 2173 } 2174 2175 2176 // 2177 // Check if the user specified a non zero file attributes byte 2178 // 2179 2180 if (Buffer->FileAttributes != 0) { 2181 2182 // 2183 // Only permit the attributes that FAT understands. The rest are silently 2184 // dropped on the floor. 2185 // 2186 2187 Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY | 2188 FILE_ATTRIBUTE_HIDDEN | 2189 FILE_ATTRIBUTE_SYSTEM | 2190 FILE_ATTRIBUTE_DIRECTORY | 2191 FILE_ATTRIBUTE_ARCHIVE)); 2192 2193 // 2194 // Make sure that for a file the directory bit is not set 2195 // and that for a directory the bit is set. 2196 // 2197 2198 if (NodeType(Fcb) == FAT_NTC_FCB) { 2199 2200 if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) { 2201 2202 DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0); 2203 try_return( Status = STATUS_INVALID_PARAMETER ); 2204 } 2205 2206 } else { 2207 2208 Attributes |= FAT_DIRENT_ATTR_DIRECTORY; 2209 } 2210 2211 // 2212 // Mark the FcbState temporary flag correctly. 2213 // 2214 2215 if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) { 2216 2217 // 2218 // Don't allow the temporary bit to be set on directories. 2219 // 2220 2221 if (NodeType(Fcb) == FAT_NTC_DCB) { 2222 2223 DebugTrace(0, Dbg, "No temporary directories\n", 0); 2224 try_return( Status = STATUS_INVALID_PARAMETER ); 2225 } 2226 2227 SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); 2228 2229 SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, 2230 FO_TEMPORARY_FILE ); 2231 2232 } else { 2233 2234 ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); 2235 2236 ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, 2237 FO_TEMPORARY_FILE ); 2238 } 2239 2240 // 2241 // Now, only proceed if the requested file attributes are different 2242 // than the existing attributes. 2243 // 2244 2245 if (Dirent->Attributes != Attributes) { 2246 2247 // 2248 // Set the new attributes byte, and mark the bcb dirty 2249 // 2250 2251 Fcb->DirentFatFlags = Attributes; 2252 2253 Dirent->Attributes = Attributes; 2254 2255 NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 2256 2257 #if (NTDDI_VERSION >= NTDDI_WIN8) 2258 ModifiedAttributes = TRUE; 2259 #endif 2260 } 2261 } 2262 2263 if ( ModifyCreation ) { 2264 2265 // 2266 // Set the new last write time in the dirent, and mark 2267 // the bcb dirty 2268 // 2269 2270 Fcb->CreationTime = LargeCreationTime; 2271 Dirent->CreationTime = CreationTime; 2272 Dirent->CreationMSec = CreationMSec; 2273 2274 2275 NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; 2276 // 2277 // Now we have to round the time in the Fcb up to the 2278 // nearest tem msec. 2279 // 2280 2281 Fcb->CreationTime.QuadPart = 2282 2283 ((Fcb->CreationTime.QuadPart + AlmostTenMSec) / 2284 TenMSec) * TenMSec; 2285 2286 // 2287 // Now because the user just set the creation time we 2288 // better not set the creation time on close 2289 // 2290 2291 SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); 2292 } 2293 2294 if ( ModifyLastAccess ) { 2295 2296 // 2297 // Set the new last write time in the dirent, and mark 2298 // the bcb dirty 2299 // 2300 2301 Fcb->LastAccessTime = LargeLastAccessTime; 2302 Dirent->LastAccessDate = LastAccessDate; 2303 2304 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 2305 2306 // 2307 // Now we have to truncate the time in the Fcb down to the 2308 // current day. This has to be in LocalTime though, so first 2309 // convert to local, trunacate, then set back to GMT. 2310 // 2311 2312 ExSystemTimeToLocalTime( &Fcb->LastAccessTime, 2313 &Fcb->LastAccessTime ); 2314 2315 Fcb->LastAccessTime.QuadPart = 2316 2317 (Fcb->LastAccessTime.QuadPart / 2318 FatOneDay.QuadPart) * FatOneDay.QuadPart; 2319 2320 ExLocalTimeToSystemTime( &Fcb->LastAccessTime, 2321 &Fcb->LastAccessTime ); 2322 2323 // 2324 // Now because the user just set the last access time we 2325 // better not set the last access time on close 2326 // 2327 2328 SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); 2329 } 2330 2331 if ( ModifyLastWrite ) { 2332 2333 // 2334 // Set the new last write time in the dirent, and mark 2335 // the bcb dirty 2336 // 2337 2338 Fcb->LastWriteTime = LargeLastWriteTime; 2339 Dirent->LastWriteTime = LastWriteTime; 2340 2341 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 2342 2343 // 2344 // Now we have to round the time in the Fcb up to the 2345 // nearest two seconds. 2346 // 2347 2348 Fcb->LastWriteTime.QuadPart = 2349 2350 ((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) / 2351 TwoSeconds) * TwoSeconds; 2352 2353 // 2354 // Now because the user just set the last write time we 2355 // better not set the last write time on close 2356 // 2357 2358 SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); 2359 } 2360 2361 // 2362 // If we modified any of the values, we report this to the notify 2363 // package. 2364 // 2365 // We also take this opportunity to set the current file size and 2366 // first cluster in the Dirent in order to support a server hack. 2367 // 2368 2369 if (NotifyFilter != 0) { 2370 2371 if (NodeType(Fcb) == FAT_NTC_FCB) { 2372 2373 Dirent->FileSize = Fcb->Header.FileSize.LowPart; 2374 2375 2376 Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile; 2377 2378 if (FatIsFat32(Fcb->Vcb)) { 2379 2380 Dirent->FirstClusterOfFileHi = 2381 (USHORT)(Fcb->FirstClusterOfFile >> 16); 2382 } 2383 } 2384 2385 FatNotifyReportChange( IrpContext, 2386 Fcb->Vcb, 2387 Fcb, 2388 NotifyFilter, 2389 FILE_ACTION_MODIFIED ); 2390 2391 FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); 2392 } 2393 2394 #if (NTDDI_VERSION >= NTDDI_WIN8) 2395 // 2396 // If last-access, last-write, or any attribute bits changed, break 2397 // parent directory oplock. 2398 // 2399 2400 if ((Fcb->ParentDcb != NULL) && 2401 (ModifyLastAccess || 2402 ModifyLastWrite || 2403 ModifiedAttributes)) { 2404 2405 FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb), 2406 IrpContext->OriginatingIrp, 2407 OPLOCK_FLAG_PARENT_OBJECT, 2408 NULL, 2409 NULL, 2410 NULL ); 2411 } 2412 #endif 2413 2414 try_exit: NOTHING; 2415 } _SEH2_FINALLY { 2416 2417 DebugUnwind( FatSetBasicInfo ); 2418 2419 FatUnpinBcb( IrpContext, DirentBcb ); 2420 2421 DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status); 2422 } _SEH2_END; 2423 2424 return Status; 2425 } 2426 2427 // 2428 // Internal Support Routine 2429 // 2430 2431 _Requires_lock_held_(_Global_critical_region_) 2432 NTSTATUS 2433 FatSetDispositionInfo ( 2434 IN PIRP_CONTEXT IrpContext, 2435 IN PIRP Irp, 2436 IN PFILE_OBJECT FileObject, 2437 IN PFCB Fcb 2438 ) 2439 2440 /*++ 2441 2442 Routine Description: 2443 2444 This routine performs the set disposition information for fat. It either 2445 completes the request or enqueues it off to the fsp. 2446 2447 Arguments: 2448 2449 Irp - Supplies the irp being processed 2450 2451 FileObject - Supplies the file object being processed 2452 2453 Fcb - Supplies the Fcb or Dcb being processed, already known not to 2454 be the root dcb 2455 2456 Return Value: 2457 2458 NTSTATUS - The result of this operation if it completes without 2459 an exception. 2460 2461 --*/ 2462 2463 { 2464 PFILE_DISPOSITION_INFORMATION Buffer; 2465 PBCB Bcb; 2466 PDIRENT Dirent; 2467 2468 PAGED_CODE(); 2469 2470 DebugTrace(+1, Dbg, "FatSetDispositionInfo...\n", 0); 2471 2472 Buffer = Irp->AssociatedIrp.SystemBuffer; 2473 2474 // 2475 // Check if the user wants to delete the file or not delete 2476 // the file 2477 // 2478 2479 if (Buffer->DeleteFile) { 2480 2481 // 2482 // Check if the file is marked read only 2483 // 2484 2485 if (FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY)) { 2486 2487 DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0); 2488 2489 return STATUS_CANNOT_DELETE; 2490 } 2491 2492 // 2493 // Make sure there is no process mapping this file as an image. 2494 // 2495 2496 if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers, 2497 MmFlushForDelete )) { 2498 2499 DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0); 2500 2501 return STATUS_CANNOT_DELETE; 2502 } 2503 2504 // 2505 // Check if this is a dcb and if so then only allow 2506 // the request if the directory is empty. 2507 // 2508 2509 if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { 2510 2511 DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0); 2512 2513 return STATUS_CANNOT_DELETE; 2514 } 2515 2516 if (NodeType(Fcb) == FAT_NTC_DCB) { 2517 2518 DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0); 2519 2520 // 2521 // Check if the directory is empty 2522 // 2523 2524 if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) { 2525 2526 DebugTrace(-1, Dbg, "Directory is not empty\n", 0); 2527 2528 return STATUS_DIRECTORY_NOT_EMPTY; 2529 } 2530 } 2531 2532 // 2533 // If this is a floppy, touch the volume so to verify that it 2534 // is not write protected. 2535 // 2536 2537 if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { 2538 2539 PVCB Vcb; 2540 PBCB LocalBcb = NULL; 2541 UCHAR *LocalBuffer; 2542 UCHAR TmpChar; 2543 ULONG BytesToMap; 2544 2545 IO_STATUS_BLOCK Iosb; 2546 2547 Vcb = Fcb->Vcb; 2548 2549 BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ? 2550 FatReservedBytes(&Vcb->Bpb) + 2551 FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE; 2552 2553 FatReadVolumeFile( IrpContext, 2554 Vcb, 2555 0, 2556 BytesToMap, 2557 &LocalBcb, 2558 (PVOID *)&LocalBuffer ); 2559 2560 _SEH2_TRY { 2561 2562 if (!CcPinMappedData( Vcb->VirtualVolumeFile, 2563 &FatLargeZero, 2564 BytesToMap, 2565 BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), 2566 &LocalBcb )) { 2567 2568 // 2569 // Could not pin the data without waiting (cache miss). 2570 // 2571 2572 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 2573 } 2574 2575 // 2576 // Make Mm, myself, and Cc think the byte is dirty, and then 2577 // force a writethrough. 2578 // 2579 2580 LocalBuffer += FatReservedBytes(&Vcb->Bpb); 2581 2582 TmpChar = LocalBuffer[0]; 2583 LocalBuffer[0] = TmpChar; 2584 2585 FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, 2586 FatReservedBytes( &Vcb->Bpb ), 2587 FatReservedBytes( &Vcb->Bpb ), 2588 Vcb->Bpb.BytesPerSector ); 2589 2590 } _SEH2_FINALLY { 2591 2592 if (_SEH2_AbnormalTermination() && (LocalBcb != NULL)) { 2593 2594 FatUnpinBcb( IrpContext, LocalBcb ); 2595 } 2596 } _SEH2_END; 2597 2598 CcRepinBcb( LocalBcb ); 2599 CcSetDirtyPinnedData( LocalBcb, NULL ); 2600 CcUnpinData( LocalBcb ); 2601 DbgDoit( NT_ASSERT( IrpContext->PinCount )); 2602 DbgDoit( IrpContext->PinCount -= 1 ); 2603 CcUnpinRepinnedBcb( LocalBcb, TRUE, &Iosb ); 2604 2605 // 2606 // If this was not successful, raise the status. 2607 // 2608 2609 if ( !NT_SUCCESS(Iosb.Status) ) { 2610 2611 FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status ); 2612 } 2613 2614 } else { 2615 2616 // 2617 // Just set a Bcb dirty here. The above code was only there to 2618 // detect a write protected floppy, while the below code works 2619 // for any write protected media and only takes a hit when the 2620 // volume in clean. 2621 // 2622 2623 FatGetDirentFromFcbOrDcb( IrpContext, 2624 Fcb, 2625 FALSE, 2626 &Dirent, 2627 &Bcb ); 2628 2629 // 2630 // This has to work for the usual reasons (we verified the Fcb within 2631 // volume synch). 2632 // 2633 2634 _SEH2_TRY { 2635 2636 FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE ); 2637 2638 } _SEH2_FINALLY { 2639 2640 FatUnpinBcb( IrpContext, Bcb ); 2641 } _SEH2_END; 2642 } 2643 2644 // 2645 // At this point either we have a file or an empty directory 2646 // so we know the delete can proceed. 2647 // 2648 2649 SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); 2650 FileObject->DeletePending = TRUE; 2651 2652 // 2653 // If this is a directory then report this delete pending to 2654 // the dir notify package. 2655 // 2656 2657 if (NodeType(Fcb) == FAT_NTC_DCB) { 2658 2659 FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync, 2660 &Fcb->Vcb->DirNotifyList, 2661 FileObject->FsContext, 2662 NULL, 2663 FALSE, 2664 FALSE, 2665 0, 2666 NULL, 2667 NULL, 2668 NULL ); 2669 } 2670 } else { 2671 2672 // 2673 // The user doesn't want to delete the file so clear 2674 // the delete on close bit 2675 // 2676 2677 DebugTrace(0, Dbg, "User want to not delete file\n", 0); 2678 2679 ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); 2680 FileObject->DeletePending = FALSE; 2681 } 2682 2683 DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0); 2684 2685 return STATUS_SUCCESS; 2686 } 2687 2688 2689 // 2690 // Internal Support Routine 2691 // 2692 2693 NTSTATUS 2694 FatSetRenameInfo ( 2695 IN PIRP_CONTEXT IrpContext, 2696 IN PIRP Irp, 2697 IN PVCB Vcb, 2698 IN PFCB Fcb, 2699 IN PCCB Ccb 2700 ) 2701 2702 /*++ 2703 2704 Routine Description: 2705 2706 This routine performs the set name information for fat. It either 2707 completes the request or enqueues it off to the fsp. 2708 2709 Arguments: 2710 2711 Irp - Supplies the irp being processed 2712 2713 Vcb - Supplies the Vcb being processed 2714 2715 Fcb - Supplies the Fcb or Dcb being processed, already known not to 2716 be the root dcb 2717 2718 Ccb - Supplies the Ccb corresponding to the handle opening the source 2719 file 2720 2721 Return Value: 2722 2723 NTSTATUS - The result of this operation if it completes without 2724 an exception. 2725 2726 --*/ 2727 2728 { 2729 BOOLEAN AllLowerComponent; 2730 BOOLEAN AllLowerExtension; 2731 BOOLEAN CaseOnlyRename; 2732 BOOLEAN ContinueWithRename; 2733 BOOLEAN CreateLfn = FALSE; 2734 BOOLEAN DeleteSourceDirent; 2735 BOOLEAN DeleteTarget; 2736 BOOLEAN NewDirentFromPool; 2737 BOOLEAN RenamedAcrossDirectories; 2738 BOOLEAN ReplaceIfExists; 2739 2740 CCB LocalCcb; 2741 PCCB SourceCcb; 2742 2743 DIRENT Dirent; 2744 2745 NTSTATUS Status = STATUS_SUCCESS; 2746 2747 OEM_STRING OldOemName; 2748 OEM_STRING NewOemName; 2749 UCHAR OemNameBuffer[24*2]; 2750 2751 PBCB DotDotBcb; 2752 PBCB NewDirentBcb; 2753 PBCB OldDirentBcb; 2754 PBCB SecondPageBcb; 2755 PBCB TargetDirentBcb; 2756 2757 PDCB TargetDcb = NULL; 2758 PDCB OldParentDcb; 2759 2760 PDIRENT DotDotDirent = NULL; 2761 PDIRENT FirstPageDirent = NULL; 2762 PDIRENT NewDirent = NULL; 2763 PDIRENT OldDirent = NULL; 2764 PDIRENT SecondPageDirent = NULL; 2765 PDIRENT ShortDirent = NULL; 2766 PDIRENT TargetDirent = NULL; 2767 2768 PFCB TempFcb; 2769 2770 PFILE_OBJECT TargetFileObject; 2771 PFILE_OBJECT FileObject; 2772 2773 PIO_STACK_LOCATION IrpSp; 2774 2775 PLIST_ENTRY Links; 2776 2777 ULONG BytesInFirstPage = 0; 2778 ULONG DirentsInFirstPage = 0; 2779 ULONG DirentsRequired = 0; 2780 ULONG NewOffset = 0; 2781 ULONG NotifyAction = 0; 2782 ULONG SecondPageOffset = 0; 2783 ULONG ShortDirentOffset = 0; 2784 ULONG TargetDirentOffset = 0; 2785 ULONG TargetLfnOffset = 0; 2786 2787 // NewName comes from the IRP buffer or the TargetFileObject, so we can't 2788 // go around modifying it. Instead we modify NewNameCopy. 2789 UNICODE_STRING NewName; 2790 2791 // NB: these five UNICODE_STRINGS are allocated 2792 // from one chopped up pool allocation called UnicodeBuffer. 2793 UNICODE_STRING NewNameCopy; 2794 UNICODE_STRING NewUpcasedName; 2795 UNICODE_STRING OldName; 2796 UNICODE_STRING OldUpcasedName; 2797 UNICODE_STRING TargetLfn; 2798 PWCHAR UnicodeBuffer; 2799 2800 UNICODE_STRING TargetOrigLfn = {0}; 2801 2802 UNICODE_STRING UniTunneledShortName; 2803 WCHAR UniTunneledShortNameBuffer[12]; 2804 UNICODE_STRING UniTunneledLongName; 2805 WCHAR UniTunneledLongNameBuffer[26]; 2806 2807 LARGE_INTEGER TunneledCreationTime; 2808 ULONG TunneledDataSize; 2809 BOOLEAN HaveTunneledInformation = FALSE; 2810 BOOLEAN UsingTunneledLfn = FALSE; 2811 2812 BOOLEAN InvalidateFcbOnRaise = FALSE; 2813 2814 PFILE_OBJECT DirectoryFileObject = NULL; 2815 ULONG Flags = 0; 2816 2817 PAGED_CODE(); 2818 2819 DebugTrace(+1, Dbg, "FatSetRenameInfo...\n", 0); 2820 2821 // 2822 // P H A S E 0: Initialize some variables. 2823 // 2824 2825 CaseOnlyRename = FALSE; 2826 ContinueWithRename = FALSE; 2827 DeleteSourceDirent = FALSE; 2828 DeleteTarget = FALSE; 2829 NewDirentFromPool = FALSE; 2830 RenamedAcrossDirectories = FALSE; 2831 2832 DotDotBcb = NULL; 2833 NewDirentBcb = NULL; 2834 OldDirentBcb = NULL; 2835 SecondPageBcb = NULL; 2836 TargetDirentBcb = NULL; 2837 2838 NewOemName.Length = 0; 2839 NewOemName.MaximumLength = 24; 2840 NewOemName.Buffer = (PCHAR)&OemNameBuffer[0]; 2841 2842 OldOemName.Length = 0; 2843 OldOemName.MaximumLength = 24; 2844 OldOemName.Buffer = (PCHAR)&OemNameBuffer[24]; 2845 2846 UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool, 2847 5 * MAX_LFN_CHARACTERS * sizeof(WCHAR), 2848 TAG_FILENAME_BUFFER ); 2849 2850 NewUpcasedName.Length = 0; 2851 NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); 2852 NewUpcasedName.Buffer = &UnicodeBuffer[0]; 2853 2854 OldName.Length = 0; 2855 OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); 2856 OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS]; 2857 2858 OldUpcasedName.Length = 0; 2859 OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); 2860 OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2]; 2861 2862 TargetLfn.Length = 0; 2863 TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); 2864 TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3]; 2865 2866 NewNameCopy.Length = 0; 2867 NewNameCopy.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); 2868 NewNameCopy.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 4]; 2869 2870 UniTunneledShortName.Length = 0; 2871 UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer); 2872 UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0]; 2873 2874 UniTunneledLongName.Length = 0; 2875 UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer); 2876 UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0]; 2877 2878 // 2879 // Remember the name in case we have to modify the name 2880 // value in the ea. 2881 // 2882 2883 RtlCopyMemory( OldOemName.Buffer, 2884 Fcb->ShortName.Name.Oem.Buffer, 2885 OldOemName.Length ); 2886 2887 // 2888 // Get the current stack location 2889 // 2890 2891 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 2892 2893 // 2894 // Extract information from the Irp to make our life easier 2895 // 2896 2897 FileObject = IrpSp->FileObject; 2898 SourceCcb = FileObject->FsContext2; 2899 TargetFileObject = IrpSp->Parameters.SetFile.FileObject; 2900 ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists; 2901 2902 RtlZeroMemory( &LocalCcb, sizeof(CCB) ); 2903 2904 // 2905 // P H A S E 1: 2906 // 2907 // Test if rename is legal. Only small side-effects are not undone. 2908 // 2909 2910 _SEH2_TRY { 2911 2912 // 2913 // Can't rename the root directory 2914 // 2915 2916 if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) { 2917 2918 try_return( Status = STATUS_INVALID_PARAMETER ); 2919 } 2920 2921 // 2922 // Check that we were not given a dcb with open handles beneath 2923 // it. If there are only UncleanCount == 0 Fcbs beneath us, then 2924 // remove them from the prefix table, and they will just close 2925 // and go away naturally. 2926 // 2927 2928 if (NodeType(Fcb) == FAT_NTC_DCB) { 2929 2930 PFCB BatchOplockFcb; 2931 ULONG BatchOplockCount; 2932 2933 // 2934 // Loop until there are no batch oplocks in the subtree below 2935 // this directory. 2936 // 2937 2938 while (TRUE) { 2939 2940 BatchOplockFcb = NULL; 2941 BatchOplockCount = 0; 2942 2943 // 2944 // First look for any UncleanCount != 0 Fcbs, and fail if we 2945 // find any. 2946 // 2947 2948 for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); 2949 TempFcb != Fcb; 2950 TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) { 2951 2952 if ( TempFcb->UncleanCount != 0 ) { 2953 2954 // 2955 // If there is a batch oplock on this file then 2956 // increment our count and remember the Fcb if 2957 // this is the first. 2958 // 2959 2960 if (FatIsFileOplockable( TempFcb ) && 2961 (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) ) 2962 #if (NTDDI_VERSION >= NTDDI_WIN7) 2963 || 2964 FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) ) 2965 #endif 2966 )) { 2967 2968 BatchOplockCount += 1; 2969 if ( BatchOplockFcb == NULL ) { 2970 2971 BatchOplockFcb = TempFcb; 2972 } 2973 2974 } else { 2975 2976 try_return( Status = STATUS_ACCESS_DENIED ); 2977 } 2978 } 2979 } 2980 2981 // 2982 // If this is not the first pass for rename and the number 2983 // of batch oplocks has not decreased then give up. 2984 // 2985 2986 if ( BatchOplockFcb != NULL ) { 2987 2988 if ( (Irp->IoStatus.Information != 0) && 2989 (BatchOplockCount >= Irp->IoStatus.Information) ) { 2990 2991 try_return( Status = STATUS_ACCESS_DENIED ); 2992 } 2993 2994 // 2995 // Try to break this batch oplock. 2996 // 2997 2998 Irp->IoStatus.Information = BatchOplockCount; 2999 Status = FsRtlCheckOplock( FatGetFcbOplock(BatchOplockFcb), 3000 Irp, 3001 IrpContext, 3002 FatOplockComplete, 3003 NULL ); 3004 3005 // 3006 // If the oplock was already broken then look for more 3007 // batch oplocks. 3008 // 3009 3010 if (Status == STATUS_SUCCESS) { 3011 3012 continue; 3013 } 3014 3015 // 3016 // Otherwise the oplock package will post or complete the 3017 // request. 3018 // 3019 3020 try_return( Status = STATUS_PENDING ); 3021 } 3022 3023 break; 3024 } 3025 3026 // 3027 // Now try to get as many of these file object, and thus Fcbs 3028 // to go away as possible, flushing first, of course. 3029 // 3030 3031 FatPurgeReferencedFileObjects( IrpContext, Fcb, Flush ); 3032 3033 // 3034 // OK, so there are no UncleanCount != 0, Fcbs. Infact, there 3035 // shouldn't really be any Fcbs left at all, except obstinate 3036 // ones from user mapped sections .... Remove the full file name 3037 // and exact case lfn. 3038 // 3039 3040 for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); 3041 TempFcb != Fcb; 3042 TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) { 3043 3044 FatAcquireExclusiveFcb( IrpContext, TempFcb ); 3045 3046 if (TempFcb->FullFileName.Buffer != NULL) { 3047 3048 ExFreePool( TempFcb->FullFileName.Buffer ); 3049 TempFcb->FullFileName.Buffer = NULL; 3050 } 3051 3052 FatReleaseFcb( IrpContext, TempFcb ); 3053 } 3054 } 3055 3056 // 3057 // Check if this is a simple rename or a fully-qualified rename 3058 // In both cases we need to figure out what the TargetDcb, and 3059 // NewName are. 3060 // 3061 3062 if (TargetFileObject == NULL) { 3063 3064 // 3065 // In the case of a simple rename the target dcb is the 3066 // same as the source file's parent dcb, and the new file name 3067 // is taken from the system buffer 3068 // 3069 3070 PFILE_RENAME_INFORMATION Buffer; 3071 3072 Buffer = Irp->AssociatedIrp.SystemBuffer; 3073 3074 TargetDcb = Fcb->ParentDcb; 3075 3076 NewName.Length = (USHORT) Buffer->FileNameLength; 3077 NewName.Buffer = (PWSTR) &Buffer->FileName; 3078 3079 // 3080 // Make sure the name is of legal length. 3081 // 3082 3083 if (NewName.Length > 255*sizeof(WCHAR)) { 3084 3085 try_return( Status = STATUS_OBJECT_NAME_INVALID ); 3086 } 3087 3088 RtlCopyUnicodeString(&NewNameCopy,&NewName); 3089 3090 } else { 3091 3092 // 3093 // For a fully-qualified rename the target dcb is taken from 3094 // the target file object, which must be on the same vcb as 3095 // the source. 3096 // 3097 3098 PVCB TargetVcb; 3099 PCCB TargetCcb; 3100 3101 if ((FatDecodeFileObject( TargetFileObject, 3102 &TargetVcb, 3103 &TargetDcb, 3104 &TargetCcb ) != UserDirectoryOpen) || 3105 (TargetVcb != Vcb)) { 3106 3107 try_return( Status = STATUS_INVALID_PARAMETER ); 3108 } 3109 3110 // 3111 // This name is by definition legal. 3112 // 3113 3114 NewName = *((PUNICODE_STRING)&TargetFileObject->FileName); 3115 3116 RtlCopyUnicodeString(&NewNameCopy,&NewName); 3117 3118 } 3119 3120 // 3121 // We will need an upcased version of the unicode name and the 3122 // old name as well. 3123 // 3124 3125 Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE ); 3126 3127 if (!NT_SUCCESS(Status)) { 3128 3129 try_return( Status ); 3130 } 3131 3132 FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName ); 3133 3134 Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE ); 3135 3136 if (!NT_SUCCESS(Status)) { 3137 try_return(Status); 3138 } 3139 3140 // 3141 // Check if the current name and new name are equal, and the 3142 // DCBs are equal. If they are then our work is already done. 3143 // 3144 3145 if (TargetDcb == Fcb->ParentDcb) { 3146 3147 // 3148 // OK, now if we found something then check if it was an exact 3149 // match or just a case match. If it was an exact match, then 3150 // we can bail here. 3151 // 3152 3153 if (FsRtlAreNamesEqual( &NewName, 3154 &OldName, 3155 FALSE, 3156 NULL )) { 3157 3158 try_return( Status = STATUS_SUCCESS ); 3159 } 3160 3161 // 3162 // Check now for a case only rename. 3163 // 3164 3165 3166 if (FsRtlAreNamesEqual( &NewUpcasedName, 3167 &OldUpcasedName, 3168 FALSE, 3169 NULL )) { 3170 3171 CaseOnlyRename = TRUE; 3172 } 3173 3174 } else { 3175 3176 RenamedAcrossDirectories = TRUE; 3177 } 3178 3179 // 3180 // Upcase the name and convert it to the Oem code page. 3181 // 3182 // If the new UNICODE name is already more than 12 characters, 3183 // then we know the Oem name will not be valid 3184 // 3185 3186 if (NewName.Length <= 12*sizeof(WCHAR)) { 3187 3188 FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName ); 3189 3190 // 3191 // If the name is not valid 8.3, zero the length. 3192 // 3193 3194 if (FatSpaceInName( IrpContext, &NewName ) || 3195 !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) { 3196 3197 NewOemName.Length = 0; 3198 } 3199 3200 } else { 3201 3202 NewOemName.Length = 0; 3203 } 3204 3205 // 3206 // Look in the tunnel cache for names and timestamps to restore 3207 // 3208 3209 TunneledDataSize = sizeof(LARGE_INTEGER); 3210 HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel, 3211 FatDirectoryKey(TargetDcb), 3212 &NewName, 3213 &UniTunneledShortName, 3214 &UniTunneledLongName, 3215 &TunneledDataSize, 3216 &TunneledCreationTime ); 3217 NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER)); 3218 3219 3220 // 3221 // Now we need to determine how many dirents this new name will 3222 // require. 3223 // 3224 3225 if ((NewOemName.Length == 0) || 3226 (FatEvaluateNameCase( IrpContext, 3227 &NewNameCopy, 3228 &AllLowerComponent, 3229 &AllLowerExtension, 3230 &CreateLfn ), 3231 CreateLfn)) { 3232 3233 DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewNameCopy) + 1; 3234 3235 } else { 3236 3237 // 3238 // The user-given name is a short name, but we might still have 3239 // a tunneled long name we want to use. See if we can. 3240 // 3241 3242 if (UniTunneledLongName.Length && 3243 !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) { 3244 3245 UsingTunneledLfn = CreateLfn = TRUE; 3246 DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1; 3247 3248 } else { 3249 3250 // 3251 // This really is a simple dirent. Note that the two AllLower BOOLEANs 3252 // are correctly set now. 3253 // 3254 3255 DirentsRequired = 1; 3256 3257 3258 } 3259 } 3260 3261 // 3262 // Do some extra checks here if we are not in Chicago mode. 3263 // 3264 3265 if (!FatData.ChicagoMode) { 3266 3267 // 3268 // If the name was not 8.3 valid, fail the rename. 3269 // 3270 3271 if (NewOemName.Length == 0) { 3272 3273 try_return( Status = STATUS_OBJECT_NAME_INVALID ); 3274 } 3275 3276 // 3277 // Don't use the magic bits. 3278 // 3279 3280 AllLowerComponent = FALSE; 3281 AllLowerExtension = FALSE; 3282 CreateLfn = FALSE; 3283 UsingTunneledLfn = FALSE; 3284 } 3285 3286 if (!CaseOnlyRename) { 3287 3288 // 3289 // Check if the new name already exists, wait is known to be 3290 // true. 3291 // 3292 3293 if (NewOemName.Length != 0) { 3294 3295 FatStringTo8dot3( IrpContext, 3296 NewOemName, 3297 &LocalCcb.OemQueryTemplate.Constant ); 3298 3299 } else { 3300 3301 SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); 3302 } 3303 3304 LocalCcb.UnicodeQueryTemplate = NewUpcasedName; 3305 LocalCcb.ContainsWildCards = FALSE; 3306 3307 Flags = 0; 3308 3309 3310 FatLocateDirent( IrpContext, 3311 TargetDcb, 3312 &LocalCcb, 3313 0, 3314 &Flags, 3315 &TargetDirent, 3316 &TargetDirentBcb, 3317 (PVBO)&TargetDirentOffset, 3318 NULL, 3319 &TargetLfn, 3320 &TargetOrigLfn ); 3321 3322 if (TargetDirent != NULL) { 3323 3324 3325 // 3326 // The name already exists, check if the user wants 3327 // to overwrite the name, and has access to do the overwrite 3328 // We cannot overwrite a directory. 3329 // 3330 3331 if ((!ReplaceIfExists) || 3332 (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) || 3333 (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) { 3334 3335 try_return( Status = STATUS_OBJECT_NAME_COLLISION ); 3336 } 3337 3338 // 3339 // Check that the file has no open user handles, if it does 3340 // then we will deny access. We do the check by searching 3341 // down the list of fcbs opened under our parent Dcb, and making 3342 // sure none of the maching Fcbs have a non-zero unclean count or 3343 // outstanding image sections. 3344 // 3345 3346 for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; 3347 Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) { 3348 3349 TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); 3350 3351 // 3352 // Advance now. The image section flush may cause the final 3353 // close, which will recursively happen underneath of us here. 3354 // It would be unfortunate if we looked through free memory. 3355 // 3356 3357 Links = Links->Flink; 3358 3359 if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) && 3360 ((TempFcb->UncleanCount != 0) || 3361 !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers, 3362 MmFlushForDelete))) { 3363 3364 // 3365 // If there are batch oplocks on this file then break the 3366 // oplocks before failing the rename. 3367 // 3368 3369 Status = STATUS_ACCESS_DENIED; 3370 3371 if (FatIsFileOplockable( TempFcb ) && 3372 (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) ) 3373 #if (NTDDI_VERSION >= NTDDI_WIN7) 3374 || 3375 FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) ) 3376 #endif 3377 )) { 3378 3379 // 3380 // Do all of our cleanup now since the IrpContext 3381 // could go away when this request is posted. 3382 // 3383 3384 FatUnpinBcb( IrpContext, TargetDirentBcb ); 3385 3386 Status = FsRtlCheckOplock( FatGetFcbOplock(TempFcb), 3387 Irp, 3388 IrpContext, 3389 FatOplockComplete, 3390 NULL ); 3391 3392 if (Status != STATUS_PENDING) { 3393 3394 Status = STATUS_ACCESS_DENIED; 3395 } 3396 } 3397 3398 try_return( NOTHING ); 3399 } 3400 } 3401 3402 // 3403 // OK, this target is toast. Remember the Lfn offset. 3404 // 3405 3406 TargetLfnOffset = TargetDirentOffset - 3407 FAT_LFN_DIRENTS_NEEDED(&TargetOrigLfn) * 3408 sizeof(DIRENT); 3409 3410 DeleteTarget = TRUE; 3411 } 3412 } 3413 3414 3415 // 3416 // If we will need more dirents than we have, allocate them now. 3417 // 3418 3419 if ((TargetDcb != Fcb->ParentDcb) || 3420 (DirentsRequired != 3421 (Fcb->DirentOffsetWithinDirectory - 3422 Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) { 3423 3424 // 3425 // Get some new allocation 3426 // 3427 3428 NewOffset = FatCreateNewDirent( IrpContext, 3429 TargetDcb, 3430 DirentsRequired, 3431 FALSE ); 3432 3433 DeleteSourceDirent = TRUE; 3434 3435 } else { 3436 3437 NewOffset = Fcb->LfnOffsetWithinDirectory; 3438 } 3439 3440 ContinueWithRename = TRUE; 3441 3442 try_exit: NOTHING; 3443 3444 } _SEH2_FINALLY { 3445 3446 if (!ContinueWithRename) { 3447 3448 // 3449 // Undo everything from above. 3450 // 3451 3452 ExFreePool( UnicodeBuffer ); 3453 FatUnpinBcb( IrpContext, TargetDirentBcb ); 3454 3455 } 3456 } _SEH2_END; 3457 3458 // 3459 // Now, if we are already done, return here. 3460 // 3461 3462 if (!ContinueWithRename) { 3463 3464 return Status; 3465 } 3466 3467 // 3468 // P H A S E 2: Actually perform the rename. 3469 // 3470 3471 _SEH2_TRY { 3472 3473 // 3474 // Report the fact that we are going to remove this entry. 3475 // If we renamed within the same directory and the new name for the 3476 // file did not previously exist, we report this as a rename old 3477 // name. Otherwise this is a removed file. 3478 // 3479 3480 if (!RenamedAcrossDirectories && !DeleteTarget) { 3481 3482 NotifyAction = FILE_ACTION_RENAMED_OLD_NAME; 3483 3484 } else { 3485 3486 NotifyAction = FILE_ACTION_REMOVED; 3487 } 3488 3489 FatNotifyReportChange( IrpContext, 3490 Vcb, 3491 Fcb, 3492 ((NodeType( Fcb ) == FAT_NTC_FCB) 3493 ? FILE_NOTIFY_CHANGE_FILE_NAME 3494 : FILE_NOTIFY_CHANGE_DIR_NAME ), 3495 NotifyAction ); 3496 3497 _SEH2_TRY { 3498 3499 // 3500 // Capture a copy of the source dirent. 3501 // 3502 3503 FatGetDirentFromFcbOrDcb( IrpContext, Fcb, FALSE, &OldDirent, &OldDirentBcb ); 3504 3505 Dirent = *OldDirent; 3506 3507 // 3508 // Tunnel the source Fcb - the names are disappearing regardless of 3509 // whether the dirent allocation physically changed 3510 // 3511 3512 FatTunnelFcbOrDcb( Fcb, SourceCcb ); 3513 3514 // 3515 // From here until very nearly the end of the operation, if we raise there 3516 // is no reasonable way to suppose we'd be able to undo the damage. Not 3517 // being a transactional filesystem, FAT is at the mercy of a lot of things 3518 // (as the astute reader has no doubt realized by now). 3519 // 3520 3521 InvalidateFcbOnRaise = TRUE; 3522 3523 // 3524 // Delete our current dirent(s) if we got a new one. 3525 // 3526 3527 if (DeleteSourceDirent) { 3528 3529 FatDeleteDirent( IrpContext, Fcb, NULL, FALSE ); 3530 } 3531 3532 // 3533 // Delete a target conflict if we were meant to. 3534 // 3535 3536 if (DeleteTarget) { 3537 3538 FatDeleteFile( IrpContext, 3539 TargetDcb, 3540 TargetLfnOffset, 3541 TargetDirentOffset, 3542 TargetDirent, 3543 &TargetLfn ); 3544 } 3545 3546 // 3547 // We need to evaluate any short names required. If there were any 3548 // conflicts in existing short names, they would have been deleted above. 3549 // 3550 // It isn't neccesary to worry about the UsingTunneledLfn case. Since we 3551 // actually already know whether CreateLfn will be set either NewName is 3552 // an Lfn and !UsingTunneledLfn is implied or NewName is a short name and 3553 // we can handle that externally. 3554 // 3555 3556 FatSelectNames( IrpContext, 3557 TargetDcb, 3558 &NewOemName, 3559 &NewNameCopy, 3560 &NewOemName, 3561 (HaveTunneledInformation ? &UniTunneledShortName : NULL), 3562 &AllLowerComponent, 3563 &AllLowerExtension, 3564 &CreateLfn ); 3565 3566 if (!CreateLfn && UsingTunneledLfn) { 3567 3568 CreateLfn = TRUE; 3569 RtlCopyUnicodeString( &NewNameCopy, &UniTunneledLongName ); 3570 3571 // 3572 // Short names are always upcase if an LFN exists 3573 // 3574 3575 AllLowerComponent = FALSE; 3576 AllLowerExtension = FALSE; 3577 } 3578 3579 // 3580 // OK, now setup the new dirent(s) for the new name. 3581 // 3582 3583 FatPrepareWriteDirectoryFile( IrpContext, 3584 TargetDcb, 3585 NewOffset, 3586 sizeof(DIRENT), 3587 &NewDirentBcb, 3588 #ifndef __REACTOS__ 3589 &NewDirent, 3590 #else 3591 (PVOID *)&NewDirent, 3592 #endif 3593 FALSE, 3594 TRUE, 3595 &Status ); 3596 3597 NT_ASSERT( NT_SUCCESS( Status ) ); 3598 3599 // 3600 // Deal with the special case of an LFN + Dirent structure crossing 3601 // a page boundry. 3602 // 3603 3604 if ((NewOffset / PAGE_SIZE) != 3605 ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) { 3606 3607 SecondPageOffset = (NewOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE; 3608 3609 BytesInFirstPage = SecondPageOffset - NewOffset; 3610 3611 DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT); 3612 3613 FatPrepareWriteDirectoryFile( IrpContext, 3614 TargetDcb, 3615 SecondPageOffset, 3616 sizeof(DIRENT), 3617 &SecondPageBcb, 3618 #ifndef __REACTOS__ 3619 &SecondPageDirent, 3620 #else 3621 (PVOID *)&SecondPageDirent, 3622 #endif 3623 FALSE, 3624 TRUE, 3625 &Status ); 3626 3627 NT_ASSERT( NT_SUCCESS( Status ) ); 3628 3629 FirstPageDirent = NewDirent; 3630 3631 NewDirent = FsRtlAllocatePoolWithTag( PagedPool, 3632 DirentsRequired * sizeof(DIRENT), 3633 TAG_DIRENT ); 3634 3635 NewDirentFromPool = TRUE; 3636 } 3637 3638 // 3639 // Bump up Dirent and DirentOffset 3640 // 3641 3642 ShortDirent = NewDirent + DirentsRequired - 1; 3643 ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT); 3644 3645 // 3646 // Fill in the fields of the dirent. 3647 // 3648 3649 *ShortDirent = Dirent; 3650 3651 FatConstructDirent( IrpContext, 3652 ShortDirent, 3653 &NewOemName, 3654 AllLowerComponent, 3655 AllLowerExtension, 3656 CreateLfn ? &NewNameCopy : NULL, 3657 Dirent.Attributes, 3658 FALSE, 3659 (HaveTunneledInformation ? &TunneledCreationTime : NULL) ); 3660 3661 if (HaveTunneledInformation) { 3662 3663 // 3664 // Need to go in and fix the timestamps in the FCB. Note that we can't use 3665 // the TunneledCreationTime since the conversions may have failed. 3666 // 3667 3668 Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec); 3669 Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0); 3670 Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate); 3671 } 3672 3673 3674 // 3675 // If the dirent crossed pages, split the contents of the 3676 // temporary pool between the two pages. 3677 // 3678 3679 if (NewDirentFromPool) { 3680 3681 RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage ); 3682 3683 RtlCopyMemory( SecondPageDirent, 3684 NewDirent + DirentsInFirstPage, 3685 DirentsRequired*sizeof(DIRENT) - BytesInFirstPage ); 3686 3687 ShortDirent = SecondPageDirent + 3688 (DirentsRequired - DirentsInFirstPage) - 1; 3689 } 3690 3691 Dirent = *ShortDirent; 3692 3693 } _SEH2_FINALLY { 3694 3695 // 3696 // Remove the entry from the splay table, and then remove the 3697 // full file name and exact case lfn. It is important that we 3698 // always remove the name from the prefix table regardless of 3699 // other errors. 3700 // 3701 3702 FatRemoveNames( IrpContext, Fcb ); 3703 3704 if (Fcb->FullFileName.Buffer != NULL) { 3705 3706 ExFreePool( Fcb->FullFileName.Buffer ); 3707 Fcb->FullFileName.Buffer = NULL; 3708 } 3709 3710 if (Fcb->ExactCaseLongName.Buffer) { 3711 3712 ExFreePool( Fcb->ExactCaseLongName.Buffer ); 3713 Fcb->ExactCaseLongName.Buffer = NULL; 3714 } 3715 3716 FatUnpinBcb( IrpContext, OldDirentBcb ); 3717 FatUnpinBcb( IrpContext, TargetDirentBcb ); 3718 FatUnpinBcb( IrpContext, NewDirentBcb ); 3719 FatUnpinBcb( IrpContext, SecondPageBcb ); 3720 } _SEH2_END; 3721 3722 // 3723 // Now we need to update the location of the file's directory 3724 // offset and move the fcb from its current parent dcb to 3725 // the target dcb. 3726 // 3727 3728 Fcb->LfnOffsetWithinDirectory = NewOffset; 3729 Fcb->DirentOffsetWithinDirectory = ShortDirentOffset; 3730 3731 RemoveEntryList( &Fcb->ParentDcbLinks ); 3732 3733 // 3734 // There is a deep reason we put files on the tail, others on the head, 3735 // which is to allow us to easily enumerate all child directories before 3736 // child files. This is important to let us maintain whole-volume lockorder 3737 // via BottomUp enumeration. 3738 // 3739 3740 if (NodeType(Fcb) == FAT_NTC_FCB) { 3741 3742 InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue, 3743 &Fcb->ParentDcbLinks ); 3744 3745 } else { 3746 3747 InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue, 3748 &Fcb->ParentDcbLinks ); 3749 } 3750 3751 OldParentDcb = Fcb->ParentDcb; 3752 Fcb->ParentDcb = TargetDcb; 3753 3754 #if (NTDDI_VERSION >= NTDDI_WIN8) 3755 // 3756 // Break parent directory oplock on the old parent. Directory oplock 3757 // breaks are always advisory, so we will never block/get STATUS_PENDING 3758 // here. 3759 // 3760 3761 FsRtlCheckOplockEx( FatGetFcbOplock(OldParentDcb), 3762 IrpContext->OriginatingIrp, 3763 OPLOCK_FLAG_PARENT_OBJECT, 3764 NULL, 3765 NULL, 3766 NULL ); 3767 #endif 3768 3769 // 3770 // If we renamed across directories, some cleanup is now in order. 3771 // 3772 3773 if (RenamedAcrossDirectories) { 3774 3775 #if (NTDDI_VERSION >= NTDDI_WIN8) 3776 // 3777 // Break parent directory oplock on the new parent. Directory oplock 3778 // breaks are always advisory, so we will never block/get STATUS_PENDING 3779 // here. 3780 // 3781 3782 FsRtlCheckOplockEx( FatGetFcbOplock(TargetDcb), 3783 IrpContext->OriginatingIrp, 3784 OPLOCK_FLAG_PARENT_OBJECT, 3785 NULL, 3786 NULL, 3787 NULL ); 3788 #endif 3789 3790 // 3791 // See if we need to uninitialize the cachemap for the source directory. 3792 // Do this now in case we get unlucky and raise trying to finalize the 3793 // operation. 3794 // 3795 3796 if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) && 3797 (OldParentDcb->OpenCount == 0) && 3798 (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) { 3799 3800 NT_ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB ); 3801 3802 DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile; 3803 3804 OldParentDcb->Specific.Dcb.DirectoryFile = NULL; 3805 } 3806 3807 // 3808 // If we move a directory across directories, we have to change 3809 // the cluster number in its .. entry 3810 // 3811 3812 if (NodeType(Fcb) == FAT_NTC_DCB) { 3813 3814 FatPrepareWriteDirectoryFile( IrpContext, 3815 Fcb, 3816 sizeof(DIRENT), 3817 sizeof(DIRENT), 3818 &DotDotBcb, 3819 #ifndef __REACTOS__ 3820 &DotDotDirent, 3821 #else 3822 (PVOID *)&DotDotDirent, 3823 #endif 3824 FALSE, 3825 TRUE, 3826 &Status ); 3827 3828 NT_ASSERT( NT_SUCCESS( Status ) ); 3829 3830 DotDotDirent->FirstClusterOfFile = (USHORT) 3831 ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ? 3832 0 : TargetDcb->FirstClusterOfFile); 3833 3834 if (FatIsFat32( Vcb )) { 3835 3836 DotDotDirent->FirstClusterOfFileHi = (USHORT) 3837 ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ? 3838 0 : (TargetDcb->FirstClusterOfFile >> 16)); 3839 } 3840 } 3841 } 3842 3843 // 3844 // Now we need to setup the splay table and the name within 3845 // the fcb. Free the old short name at this point. 3846 // 3847 3848 ExFreePool( Fcb->ShortName.Name.Oem.Buffer ); 3849 Fcb->ShortName.Name.Oem.Buffer = NULL; 3850 3851 3852 FatConstructNamesInFcb( IrpContext, 3853 Fcb, 3854 &Dirent, 3855 CreateLfn ? &NewName : NULL ); 3856 3857 FatSetFullNameInFcb( IrpContext, Fcb, &NewName ); 3858 3859 // 3860 // The rest of the actions taken are not related to correctness of 3861 // the in-memory structures, so we shouldn't toast the Fcb if we 3862 // raise from here to the end. 3863 // 3864 3865 InvalidateFcbOnRaise = FALSE; 3866 3867 // 3868 // If a file, set the file as modified so that the archive bit 3869 // is set. We prevent this from adjusting the write time by 3870 // indicating the user flag in the ccb. 3871 // 3872 3873 if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) { 3874 3875 SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); 3876 SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); 3877 } 3878 3879 // 3880 // We have three cases to report. 3881 // 3882 // 1. If we overwrote an existing file, we report this as 3883 // a modified file. 3884 // 3885 // 2. If we renamed to a new directory, then we added a file. 3886 // 3887 // 3. If we renamed in the same directory, then we report the 3888 // the renamednewname. 3889 // 3890 3891 if (DeleteTarget) { 3892 3893 FatNotifyReportChange( IrpContext, 3894 Vcb, 3895 Fcb, 3896 FILE_NOTIFY_CHANGE_ATTRIBUTES 3897 | FILE_NOTIFY_CHANGE_SIZE 3898 | FILE_NOTIFY_CHANGE_LAST_WRITE 3899 | FILE_NOTIFY_CHANGE_LAST_ACCESS 3900 | FILE_NOTIFY_CHANGE_CREATION 3901 | FILE_NOTIFY_CHANGE_EA, 3902 FILE_ACTION_MODIFIED ); 3903 3904 } else if (RenamedAcrossDirectories) { 3905 3906 FatNotifyReportChange( IrpContext, 3907 Vcb, 3908 Fcb, 3909 ((NodeType( Fcb ) == FAT_NTC_FCB) 3910 ? FILE_NOTIFY_CHANGE_FILE_NAME 3911 : FILE_NOTIFY_CHANGE_DIR_NAME ), 3912 FILE_ACTION_ADDED ); 3913 3914 } else { 3915 3916 FatNotifyReportChange( IrpContext, 3917 Vcb, 3918 Fcb, 3919 ((NodeType( Fcb ) == FAT_NTC_FCB) 3920 ? FILE_NOTIFY_CHANGE_FILE_NAME 3921 : FILE_NOTIFY_CHANGE_DIR_NAME ), 3922 FILE_ACTION_RENAMED_NEW_NAME ); 3923 } 3924 3925 // 3926 // We need to update the file name in the dirent. This value 3927 // is never used elsewhere, so we don't concern ourselves 3928 // with any error we may encounter. We let chkdsk fix the 3929 // disk at some later time. 3930 // 3931 3932 if (!FatIsFat32(Vcb) && 3933 Dirent.ExtendedAttributes != 0) { 3934 3935 FatRenameEAs( IrpContext, 3936 Fcb, 3937 Dirent.ExtendedAttributes, 3938 &OldOemName ); 3939 } 3940 3941 FatUnpinBcb( IrpContext, DotDotBcb ); 3942 3943 FatUnpinRepinnedBcbs( IrpContext ); 3944 3945 // 3946 // Set our final status 3947 // 3948 3949 Status = STATUS_SUCCESS; 3950 3951 } _SEH2_FINALLY { 3952 3953 DebugUnwind( FatSetRenameInfo ); 3954 3955 ExFreePool( UnicodeBuffer ); 3956 3957 if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) { 3958 3959 // 3960 // Free pool if the buffer was grown on tunneling lookup 3961 // 3962 3963 ExFreePool(UniTunneledLongName.Buffer); 3964 } 3965 3966 if (NewDirentFromPool) { 3967 3968 ExFreePool( NewDirent ); 3969 } 3970 3971 FatUnpinBcb( IrpContext, TargetDirentBcb ); 3972 FatUnpinBcb( IrpContext, DotDotBcb ); 3973 3974 // 3975 // Uninitialize the cachemap for the source directory if we need to. 3976 // 3977 3978 if (DirectoryFileObject) { 3979 3980 DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0); 3981 3982 CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); 3983 3984 ObDereferenceObject( DirectoryFileObject ); 3985 } 3986 3987 // 3988 // If this was an abnormal termination, then we are in trouble. 3989 // Should the operation have been in a sensitive state there is 3990 // nothing we can do but invalidate the Fcb. 3991 // 3992 3993 if (_SEH2_AbnormalTermination() && InvalidateFcbOnRaise) { 3994 3995 Fcb->FcbCondition = FcbBad; 3996 } 3997 3998 DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status); 3999 } _SEH2_END; 4000 4001 return Status; 4002 } 4003 4004 4005 // 4006 // Internal Support Routine 4007 // 4008 4009 NTSTATUS 4010 FatSetPositionInfo ( 4011 IN PIRP_CONTEXT IrpContext, 4012 IN PIRP Irp, 4013 IN PFILE_OBJECT FileObject 4014 ) 4015 4016 /*++ 4017 4018 Routine Description: 4019 4020 This routine performs the set position information for fat. It either 4021 completes the request or enqueues it off to the fsp. 4022 4023 Arguments: 4024 4025 Irp - Supplies the irp being processed 4026 4027 FileObject - Supplies the file object being processed 4028 4029 Return Value: 4030 4031 NTSTATUS - The result of this operation if it completes without 4032 an exception. 4033 4034 --*/ 4035 4036 { 4037 PFILE_POSITION_INFORMATION Buffer; 4038 4039 PAGED_CODE(); 4040 4041 DebugTrace(+1, Dbg, "FatSetPositionInfo...\n", 0); 4042 4043 Buffer = Irp->AssociatedIrp.SystemBuffer; 4044 4045 // 4046 // Check if the file does not use intermediate buffering. If it 4047 // does not use intermediate buffering then the new position we're 4048 // supplied must be aligned properly for the device 4049 // 4050 4051 if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) { 4052 4053 PDEVICE_OBJECT DeviceObject; 4054 4055 DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject; 4056 4057 if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) { 4058 4059 DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0); 4060 DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER); 4061 4062 return STATUS_INVALID_PARAMETER; 4063 } 4064 } 4065 4066 // 4067 // The input parameter is fine so set the current byte offset and 4068 // complete the request 4069 // 4070 4071 DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset); 4072 4073 FileObject->CurrentByteOffset = Buffer->CurrentByteOffset; 4074 4075 DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS); 4076 4077 UNREFERENCED_PARAMETER( IrpContext ); 4078 4079 return STATUS_SUCCESS; 4080 } 4081 4082 4083 // 4084 // Internal Support Routine 4085 // 4086 4087 _Requires_lock_held_(_Global_critical_region_) 4088 NTSTATUS 4089 FatSetAllocationInfo ( 4090 IN PIRP_CONTEXT IrpContext, 4091 IN PIRP Irp, 4092 IN PFCB Fcb, 4093 IN PFILE_OBJECT FileObject 4094 ) 4095 4096 /*++ 4097 4098 Routine Description: 4099 4100 This routine performs the set Allocation information for fat. It either 4101 completes the request or enqueues it off to the fsp. 4102 4103 Arguments: 4104 4105 Irp - Supplies the irp being processed 4106 4107 Fcb - Supplies the Fcb or Dcb being processed, already known not to 4108 be the root dcb 4109 4110 FileObject - Supplies the FileObject being processed, already known not to 4111 be the root dcb 4112 4113 Return Value: 4114 4115 NTSTATUS - The result of this operation if it completes without 4116 an exception. 4117 4118 --*/ 4119 4120 { 4121 NTSTATUS Status = STATUS_SUCCESS; 4122 PFILE_ALLOCATION_INFORMATION Buffer; 4123 ULONG NewAllocationSize = 0; 4124 ULONG HeaderSize = 0; 4125 4126 BOOLEAN FileSizeTruncated = FALSE; 4127 BOOLEAN CacheMapInitialized = FALSE; 4128 BOOLEAN ResourceAcquired = FALSE; 4129 ULONG OriginalFileSize = 0; 4130 ULONG OriginalValidDataLength = 0; 4131 ULONG OriginalValidDataToDisk = 0; 4132 4133 PAGED_CODE(); 4134 4135 Buffer = Irp->AssociatedIrp.SystemBuffer; 4136 4137 NewAllocationSize = Buffer->AllocationSize.LowPart; 4138 4139 DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize); 4140 4141 // 4142 // Allocation is only allowed on a file and not a directory 4143 // 4144 4145 if (NodeType(Fcb) == FAT_NTC_DCB) { 4146 4147 DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0); 4148 4149 return STATUS_INVALID_DEVICE_REQUEST; 4150 } 4151 4152 // 4153 // Check that the new file allocation is legal 4154 // 4155 4156 4157 if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0)) { 4158 4159 DebugTrace(-1, Dbg, "Illegal allocation size\n", 0); 4160 4161 return STATUS_DISK_FULL; 4162 } 4163 4164 4165 // 4166 // If we haven't yet looked up the correct AllocationSize, do so. 4167 // 4168 4169 if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { 4170 4171 FatLookupFileAllocationSize( IrpContext, Fcb ); 4172 } 4173 4174 // 4175 // This is kinda gross, but if the file is not cached, but there is 4176 // a data section, we have to cache the file to avoid a bunch of 4177 // extra work. 4178 // 4179 4180 if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && 4181 (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && 4182 !FlagOn(Irp->Flags, IRP_PAGING_IO)) { 4183 4184 NT_ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) ); 4185 4186 // 4187 // Now initialize the cache map. 4188 // 4189 4190 FatInitializeCacheMap( FileObject, 4191 (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, 4192 FALSE, 4193 &FatData.CacheManagerCallbacks, 4194 Fcb ); 4195 4196 CacheMapInitialized = TRUE; 4197 } 4198 4199 // 4200 // Now mark the fact that the file needs to be truncated on close 4201 // 4202 4203 Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; 4204 4205 // 4206 // Now mark that the time on the dirent needs to be updated on close. 4207 // 4208 4209 SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); 4210 4211 _SEH2_TRY { 4212 4213 // 4214 // Increase or decrease the allocation size. 4215 // 4216 4217 if (NewAllocationSize+HeaderSize > Fcb->Header.AllocationSize.LowPart) { 4218 4219 FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize+HeaderSize); 4220 4221 } else { 4222 4223 // 4224 // Check here if we will be decreasing file size and synchonize with 4225 // paging IO. 4226 // 4227 4228 if ( Fcb->Header.FileSize.LowPart > NewAllocationSize+HeaderSize ) { 4229 4230 // 4231 // The way Sections for DataScan are created and used, an AV's 4232 // memory-mapping can come into being after we check it's safe 4233 // to truncate a file while continuing to hold the file here. 4234 // This leads to data corruption because Purge eventually fails 4235 // (during call to Cc to set file sizes) and stale data continues 4236 // to live in the cache/memory. 4237 // 4238 4239 if (Fcb->PurgeFailureModeEnableCount != 0) { 4240 4241 try_return( Status = STATUS_PURGE_FAILED ); 4242 } 4243 4244 // 4245 // Before we actually truncate, check to see if the purge 4246 // is going to fail. 4247 // 4248 4249 if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, 4250 &Buffer->AllocationSize )) { 4251 4252 try_return( Status = STATUS_USER_MAPPED_FILE ); 4253 } 4254 4255 FileSizeTruncated = TRUE; 4256 4257 OriginalFileSize = Fcb->Header.FileSize.LowPart; 4258 OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart; 4259 OriginalValidDataToDisk = Fcb->ValidDataToDisk; 4260 4261 (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); 4262 ResourceAcquired = TRUE; 4263 4264 Fcb->Header.FileSize.LowPart = NewAllocationSize; 4265 4266 // 4267 // If we reduced the file size to less than the ValidDataLength, 4268 // adjust the VDL. Likewise ValidDataToDisk. 4269 // 4270 4271 if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) { 4272 4273 Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart; 4274 } 4275 if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) { 4276 4277 Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart; 4278 } 4279 4280 } 4281 4282 // 4283 // Now that File Size is down, actually do the truncate. 4284 // 4285 4286 FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize+HeaderSize ); 4287 4288 // 4289 // Now check if we needed to decrease the file size accordingly. 4290 // 4291 4292 if ( FileSizeTruncated ) { 4293 4294 // 4295 // Tell the cache manager we reduced the file size. 4296 // The call is unconditional, because MM always wants to know. 4297 // 4298 4299 #if DBG 4300 _SEH2_TRY { 4301 #endif 4302 4303 CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); 4304 4305 #if DBG 4306 } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) { 4307 4308 NOTHING; 4309 } _SEH2_END; 4310 #endif 4311 4312 NT_ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess ); 4313 4314 // 4315 // There is no going back from this. If we run into problems updating 4316 // the dirent we will have to live with the consequences. Not sending 4317 // the notifies is likewise pretty benign compared to failing the entire 4318 // operation and trying to back out everything, which could fail for the 4319 // same reasons. 4320 // 4321 // If you want a transacted filesystem, use NTFS ... 4322 // 4323 4324 FileSizeTruncated = FALSE; 4325 4326 FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); 4327 4328 // 4329 // Report that we just reduced the file size. 4330 // 4331 4332 FatNotifyReportChange( IrpContext, 4333 Fcb->Vcb, 4334 Fcb, 4335 FILE_NOTIFY_CHANGE_SIZE, 4336 FILE_ACTION_MODIFIED ); 4337 } 4338 } 4339 4340 try_exit: NOTHING; 4341 4342 } _SEH2_FINALLY { 4343 4344 if ( _SEH2_AbnormalTermination() && FileSizeTruncated ) { 4345 4346 Fcb->Header.FileSize.LowPart = OriginalFileSize; 4347 Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength; 4348 Fcb->ValidDataToDisk = OriginalValidDataToDisk; 4349 4350 // 4351 // Make sure Cc knows the right filesize. 4352 // 4353 4354 if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { 4355 4356 *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; 4357 } 4358 4359 NT_ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart ); 4360 } 4361 4362 if (CacheMapInitialized) { 4363 4364 CcUninitializeCacheMap( FileObject, NULL, NULL ); 4365 } 4366 4367 if (ResourceAcquired) { 4368 4369 ExReleaseResourceLite( Fcb->Header.PagingIoResource ); 4370 4371 } 4372 4373 } _SEH2_END; 4374 4375 DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS); 4376 4377 return Status; 4378 } 4379 4380 4381 // 4382 // Internal Support Routine 4383 // 4384 4385 _Requires_lock_held_(_Global_critical_region_) 4386 NTSTATUS 4387 FatSetEndOfFileInfo ( 4388 IN PIRP_CONTEXT IrpContext, 4389 IN PIRP Irp, 4390 IN PFILE_OBJECT FileObject, 4391 IN PVCB Vcb, 4392 IN PFCB Fcb 4393 ) 4394 4395 /*++ 4396 4397 Routine Description: 4398 4399 This routine performs the set End of File information for fat. It either 4400 completes the request or enqueues it off to the fsp. 4401 4402 Arguments: 4403 4404 Irp - Supplies the irp being processed 4405 4406 FileObject - Supplies the file object being processed 4407 4408 Vcb - Supplies the Vcb being processed 4409 4410 Fcb - Supplies the Fcb or Dcb being processed, already known not to 4411 be the root dcb 4412 4413 Return Value: 4414 4415 NTSTATUS - The result of this operation if it completes without 4416 an exception. 4417 4418 --*/ 4419 4420 { 4421 NTSTATUS Status = STATUS_SUCCESS; 4422 4423 PFILE_END_OF_FILE_INFORMATION Buffer; 4424 4425 ULONG NewFileSize = 0; 4426 ULONG InitialFileSize = 0; 4427 ULONG InitialValidDataLength = 0; 4428 ULONG InitialValidDataToDisk = 0; 4429 4430 BOOLEAN CacheMapInitialized = FALSE; 4431 BOOLEAN UnwindFileSizes = FALSE; 4432 BOOLEAN ResourceAcquired = FALSE; 4433 4434 4435 PAGED_CODE(); 4436 4437 DebugTrace(+1, Dbg, "FatSetEndOfFileInfo...\n", 0); 4438 4439 Buffer = Irp->AssociatedIrp.SystemBuffer; 4440 4441 _SEH2_TRY { 4442 4443 // 4444 // File Size changes are only allowed on a file and not a directory 4445 // 4446 4447 if (NodeType(Fcb) != FAT_NTC_FCB) { 4448 4449 DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0); 4450 4451 try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); 4452 } 4453 4454 4455 // 4456 // Check that the new file size is legal 4457 // 4458 4459 4460 if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0)) { 4461 4462 DebugTrace(0, Dbg, "Illegal allocation size\n", 0); 4463 4464 try_return( Status = STATUS_DISK_FULL ); 4465 } 4466 4467 NewFileSize = Buffer->EndOfFile.LowPart; 4468 4469 4470 // 4471 // If we haven't yet looked up the correct AllocationSize, do so. 4472 // 4473 4474 if ((Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) && 4475 (Fcb->FcbCondition == FcbGood)) { 4476 4477 FatLookupFileAllocationSize( IrpContext, Fcb ); 4478 } 4479 4480 // 4481 // This is kinda gross, but if the file is not cached, but there is 4482 // a data section, we have to cache the file to avoid a bunch of 4483 // extra work. 4484 // 4485 4486 if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && 4487 (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && 4488 !FlagOn(Irp->Flags, IRP_PAGING_IO)) { 4489 4490 if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) { 4491 4492 // 4493 // This IRP has raced (and lost) with a close (=>cleanup) 4494 // on the same fileobject. We don't want to reinitialise the 4495 // cachemap here now because we'll leak it (unless we do so & 4496 // then tear it down again here, which is too much of a change at 4497 // this stage). So we'll just say the file is closed - which 4498 // is arguably the right thing to do anyway, since a caller 4499 // racing operations in this way is broken. The only stumbling 4500 // block is possibly filters - do they operate on cleaned 4501 // up fileobjects? 4502 // 4503 4504 FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED); 4505 } 4506 4507 // 4508 // Now initialize the cache map. 4509 // 4510 4511 FatInitializeCacheMap( FileObject, 4512 (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, 4513 FALSE, 4514 &FatData.CacheManagerCallbacks, 4515 Fcb ); 4516 4517 CacheMapInitialized = TRUE; 4518 } 4519 4520 // 4521 // Do a special case here for the lazy write of file sizes. 4522 // 4523 4524 if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) { 4525 4526 // 4527 // Only attempt this if the file hasn't been "deleted on close" and 4528 // this is a good FCB. 4529 // 4530 4531 if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) { 4532 4533 PDIRENT Dirent = NULL; 4534 PBCB DirentBcb; 4535 4536 4537 // 4538 // Never have the dirent filesize larger than the fcb filesize 4539 // 4540 4541 if (NewFileSize >= Fcb->Header.FileSize.LowPart) { 4542 4543 NewFileSize = Fcb->Header.FileSize.LowPart; 4544 } 4545 4546 // 4547 // Make sure we don't set anything higher than the alloc size. 4548 // 4549 4550 4551 NT_ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart ); 4552 4553 4554 // 4555 // Only advance the file size, never reduce it with this call 4556 // 4557 4558 FatGetDirentFromFcbOrDcb( IrpContext, 4559 Fcb, 4560 FALSE, 4561 &Dirent, 4562 &DirentBcb ); 4563 _SEH2_TRY { 4564 4565 4566 if ( NewFileSize > Dirent->FileSize ) { 4567 Dirent->FileSize = NewFileSize; 4568 4569 FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); 4570 4571 // 4572 // Report that we just changed the file size. 4573 // 4574 4575 FatNotifyReportChange( IrpContext, 4576 Vcb, 4577 Fcb, 4578 FILE_NOTIFY_CHANGE_SIZE, 4579 FILE_ACTION_MODIFIED ); 4580 4581 } 4582 4583 } _SEH2_FINALLY { 4584 4585 FatUnpinBcb( IrpContext, DirentBcb ); 4586 } _SEH2_END; 4587 4588 } else { 4589 4590 DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0); 4591 } 4592 4593 try_return( Status = STATUS_SUCCESS ); 4594 } 4595 4596 // 4597 // Check if the new file size is greater than the current 4598 // allocation size. If it is then we need to increase the 4599 // allocation size. 4600 // 4601 4602 4603 if ( (NewFileSize) > Fcb->Header.AllocationSize.LowPart ) { 4604 4605 // 4606 // Change the file allocation 4607 // 4608 4609 FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize ); 4610 } 4611 4612 4613 // 4614 // At this point we have enough allocation for the file. 4615 // So check if we are really changing the file size 4616 // 4617 4618 if (Fcb->Header.FileSize.LowPart != NewFileSize) { 4619 4620 if ( NewFileSize < Fcb->Header.FileSize.LowPart ) { 4621 4622 // 4623 // The way Sections for DataScan are created and used, an AV's 4624 // memory-mapping can come into being after we check it's safe 4625 // to truncate a file while continuing to hold the file here. 4626 // This leads to data corruption because Purge eventually fails 4627 // (during call to Cc to set file sizes) and stale data continues 4628 // to live in the cache/memory. 4629 // 4630 4631 if (Fcb->PurgeFailureModeEnableCount != 0) { 4632 4633 try_return( Status = STATUS_PURGE_FAILED ); 4634 } 4635 4636 // 4637 // Before we actually truncate, check to see if the purge 4638 // is going to fail. 4639 // 4640 4641 if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, 4642 &Buffer->EndOfFile )) { 4643 4644 try_return( Status = STATUS_USER_MAPPED_FILE ); 4645 } 4646 4647 // 4648 // This call is unconditional, because MM always wants to know. 4649 // Also serialize here with paging io since we are truncating 4650 // the file size. 4651 // 4652 4653 ResourceAcquired = 4654 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); 4655 } 4656 4657 // 4658 // Set the new file size 4659 // 4660 4661 InitialFileSize = Fcb->Header.FileSize.LowPart; 4662 InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart; 4663 InitialValidDataToDisk = Fcb->ValidDataToDisk; 4664 UnwindFileSizes = TRUE; 4665 4666 Fcb->Header.FileSize.LowPart = NewFileSize; 4667 4668 // 4669 // If we reduced the file size to less than the ValidDataLength, 4670 // adjust the VDL. Likewise ValidDataToDisk. 4671 // 4672 4673 if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) { 4674 4675 Fcb->Header.ValidDataLength.LowPart = NewFileSize; 4676 } 4677 4678 if (Fcb->ValidDataToDisk > NewFileSize) { 4679 4680 Fcb->ValidDataToDisk = NewFileSize; 4681 } 4682 4683 DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize); 4684 4685 // 4686 // We must now update the cache mapping (benign if not cached). 4687 // 4688 4689 CcSetFileSizes( FileObject, 4690 (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); 4691 4692 FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); 4693 4694 // 4695 // Report that we just changed the file size. 4696 // 4697 4698 FatNotifyReportChange( IrpContext, 4699 Vcb, 4700 Fcb, 4701 FILE_NOTIFY_CHANGE_SIZE, 4702 FILE_ACTION_MODIFIED ); 4703 4704 // 4705 // Mark the fact that the file will need to checked for 4706 // truncation on cleanup. 4707 // 4708 4709 SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE ); 4710 } 4711 4712 // 4713 // Set this handle as having modified the file 4714 // 4715 4716 FileObject->Flags |= FO_FILE_MODIFIED; 4717 4718 // 4719 // Set our return status to success 4720 // 4721 4722 Status = STATUS_SUCCESS; 4723 4724 try_exit: NOTHING; 4725 4726 FatUnpinRepinnedBcbs( IrpContext ); 4727 4728 } _SEH2_FINALLY { 4729 4730 DebugUnwind( FatSetEndOfFileInfo ); 4731 4732 if (_SEH2_AbnormalTermination() && UnwindFileSizes) { 4733 4734 Fcb->Header.FileSize.LowPart = InitialFileSize; 4735 Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength; 4736 Fcb->ValidDataToDisk = InitialValidDataToDisk; 4737 4738 if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { 4739 4740 *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; 4741 } 4742 4743 // 4744 // WinSE bug #307418 "Occasional data corruption when 4745 // standby/resume while copying files to removable FAT 4746 // formatted media". 4747 // On system suspend FatUnpinRepinnedBcbs() can fail 4748 // because the underlying drive is already marked with DO_VERIFY 4749 // flag. FatUnpinRepinnedBcbs() will raise in this case and 4750 // the file size changes will be un-rolled in FCB but the change 4751 // to Dirent file still can make it to the disk since its BCB 4752 // will not be purged by FatUnpinRepinnedBcbs(). In this case 4753 // we'll also try to un-roll the change to Dirent to keep 4754 // in-memory and on-disk metadata in sync. 4755 // 4756 4757 FatSetFileSizeInDirentNoRaise( IrpContext, Fcb, NULL ); 4758 4759 } 4760 4761 if (CacheMapInitialized) { 4762 4763 CcUninitializeCacheMap( FileObject, NULL, NULL ); 4764 } 4765 4766 if ( ResourceAcquired ) { 4767 4768 ExReleaseResourceLite( Fcb->Header.PagingIoResource ); 4769 } 4770 4771 DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status); 4772 } _SEH2_END; 4773 4774 return Status; 4775 } 4776 4777 4778 // 4779 // Internal Support Routine 4780 // 4781 4782 _Requires_lock_held_(_Global_critical_region_) 4783 NTSTATUS 4784 FatSetValidDataLengthInfo ( 4785 IN PIRP_CONTEXT IrpContext, 4786 IN PIRP Irp, 4787 IN PFILE_OBJECT FileObject, 4788 IN PFCB Fcb, 4789 IN PCCB Ccb 4790 ) 4791 4792 /*++ 4793 4794 Routine Description: 4795 4796 This routine performs the set valid data length information for fat. It either 4797 completes the request or enqueues it off to the fsp. 4798 4799 Arguments: 4800 4801 Irp - Supplies the irp being processed 4802 4803 FileObject - Supplies the file object being processed 4804 4805 Fcb - Supplies the Fcb or Dcb being processed, already known not to 4806 be the root dcb 4807 4808 Ccb - Supplies the Ccb corresponding to the handle opening the source 4809 file 4810 4811 Return Value: 4812 4813 NTSTATUS - The result of this operation if it completes without 4814 an exception. 4815 4816 --*/ 4817 4818 { 4819 NTSTATUS Status = STATUS_SUCCESS; 4820 4821 PFILE_VALID_DATA_LENGTH_INFORMATION Buffer; 4822 4823 ULONG NewValidDataLength; 4824 BOOLEAN ResourceAcquired = FALSE; 4825 4826 PAGED_CODE(); 4827 4828 UNREFERENCED_PARAMETER( IrpContext ); 4829 4830 DebugTrace(+1, Dbg, "FatSetValidDataLengthInfo...\n", 0); 4831 4832 Buffer = Irp->AssociatedIrp.SystemBuffer; 4833 4834 _SEH2_TRY { 4835 4836 // 4837 // User must have manage volume privilege to explicitly tweak the VDL 4838 // 4839 4840 if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { 4841 4842 try_return( Status = STATUS_INVALID_PARAMETER ); 4843 } 4844 4845 // 4846 // Valid data length changes are only allowed on a file and not a directory 4847 // 4848 4849 if (NodeType(Fcb) != FAT_NTC_FCB) { 4850 4851 DebugTrace(0, Dbg, "Cannot change VDL of a directory\n", 0); 4852 4853 try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); 4854 } 4855 4856 // 4857 // Check that the new file size is legal 4858 // 4859 4860 4861 if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->ValidDataLength, 0)) { 4862 4863 DebugTrace(0, Dbg, "Illegal allocation size\n", 0); 4864 4865 try_return( Status = STATUS_DISK_FULL ); 4866 } 4867 4868 4869 NewValidDataLength = Buffer->ValidDataLength.LowPart; 4870 4871 // 4872 // VDL can only move forward 4873 // 4874 4875 if ((NewValidDataLength < Fcb->Header.ValidDataLength.LowPart) || 4876 (NewValidDataLength > Fcb->Header.FileSize.LowPart)) { 4877 4878 try_return( Status = STATUS_INVALID_PARAMETER ); 4879 } 4880 4881 // 4882 // We can't change the VDL without being able to purge. This should stay 4883 // constant since we own everything exclusive 4884 // 4885 4886 if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, 4887 &Buffer->ValidDataLength )) { 4888 4889 try_return( Status = STATUS_USER_MAPPED_FILE ); 4890 } 4891 4892 // 4893 // Flush old data out and purge the cache so we can see new data. 4894 // 4895 4896 if (FileObject->SectionObjectPointer->DataSectionObject != NULL) { 4897 4898 ResourceAcquired = 4899 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); 4900 4901 CcFlushCache( FileObject->SectionObjectPointer, 4902 NULL, 4903 0, 4904 &Irp->IoStatus ); 4905 4906 if (!NT_SUCCESS( Irp->IoStatus.Status )) { 4907 4908 try_return( Irp->IoStatus.Status ); 4909 } 4910 4911 CcPurgeCacheSection( FileObject->SectionObjectPointer, 4912 NULL, 4913 0, 4914 FALSE ); 4915 } 4916 4917 // 4918 // Set the new ValidDataLength, Likewise ValidDataToDisk. 4919 // 4920 4921 Fcb->Header.ValidDataLength.LowPart = NewValidDataLength; 4922 Fcb->ValidDataToDisk = NewValidDataLength; 4923 4924 DebugTrace(0, Dbg, "New VDL is 0x%08lx.\n", NewValidDataLength); 4925 4926 // 4927 // We must now update the cache mapping. 4928 // 4929 4930 if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { 4931 4932 CcSetFileSizes( FileObject, 4933 (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); 4934 } 4935 4936 // 4937 // Set this handle as having modified the file 4938 // 4939 4940 FileObject->Flags |= FO_FILE_MODIFIED; 4941 4942 // 4943 // Set our return status to success 4944 // 4945 4946 Status = STATUS_SUCCESS; 4947 4948 try_exit: NOTHING; 4949 4950 } _SEH2_FINALLY { 4951 4952 DebugUnwind( FatSetValidDataLengthInfo ); 4953 4954 if (ResourceAcquired) { 4955 4956 ExReleaseResourceLite( Fcb->Header.PagingIoResource ); 4957 } 4958 4959 DebugTrace(-1, Dbg, "FatSetValidDataLengthInfo -> %08lx\n", Status); 4960 } _SEH2_END; 4961 4962 return Status; 4963 } 4964 4965 4966 4967 // 4968 // Internal Support Routine 4969 // 4970 4971 _Requires_lock_held_(_Global_critical_region_) 4972 VOID 4973 FatRenameEAs ( 4974 IN PIRP_CONTEXT IrpContext, 4975 IN PFCB Fcb, 4976 IN USHORT ExtendedAttributes, 4977 IN POEM_STRING OldOemName 4978 ) 4979 { 4980 BOOLEAN LockedEaFcb = FALSE; 4981 4982 PBCB EaBcb = NULL; 4983 PDIRENT EaDirent; 4984 EA_RANGE EaSetRange; 4985 PEA_SET_HEADER EaSetHeader; 4986 4987 PVCB Vcb; 4988 4989 PAGED_CODE(); 4990 4991 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); 4992 4993 Vcb = Fcb->Vcb; 4994 4995 _SEH2_TRY { 4996 4997 // 4998 // Use a try-except to catch any errors. 4999 // 5000 5001 _SEH2_TRY { 5002 5003 5004 // 5005 // Try to get the Ea file object. Return FALSE on failure. 5006 // 5007 5008 FatGetEaFile( IrpContext, 5009 Vcb, 5010 &EaDirent, 5011 &EaBcb, 5012 FALSE, 5013 FALSE ); 5014 5015 LockedEaFcb = TRUE; 5016 5017 // 5018 // If we didn't get the file because it doesn't exist, then the 5019 // disk is corrupted. We do nothing here. 5020 // 5021 5022 if (Vcb->VirtualEaFile != NULL) { 5023 5024 // 5025 // Try to pin down the Ea set header for the index in the 5026 // dirent. If the operation doesn't complete, return FALSE 5027 // from this routine. 5028 // 5029 5030 FatReadEaSet( IrpContext, 5031 Vcb, 5032 ExtendedAttributes, 5033 OldOemName, 5034 FALSE, 5035 &EaSetRange ); 5036 5037 EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; 5038 5039 // 5040 // We now have the Ea set header for this file. We simply 5041 // overwrite the owning file name. 5042 // 5043 5044 RtlZeroMemory( EaSetHeader->OwnerFileName, 14 ); 5045 5046 RtlCopyMemory( EaSetHeader->OwnerFileName, 5047 Fcb->ShortName.Name.Oem.Buffer, 5048 Fcb->ShortName.Name.Oem.Length ); 5049 5050 FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); 5051 FatUnpinEaRange( IrpContext, &EaSetRange ); 5052 5053 CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); 5054 } 5055 5056 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 5057 5058 // 5059 // We catch all exceptions that Fat catches, but don't do 5060 // anything with them. 5061 // 5062 } _SEH2_END; 5063 5064 } _SEH2_FINALLY { 5065 5066 // 5067 // Unpin the EaDirent and the EaSetHeader if pinned. 5068 // 5069 5070 FatUnpinBcb( IrpContext, EaBcb ); 5071 FatUnpinEaRange( IrpContext, &EaSetRange ); 5072 5073 // 5074 // Release the Fcb for the Ea file if locked. 5075 // 5076 5077 if (LockedEaFcb) { 5078 5079 FatReleaseFcb( IrpContext, Vcb->EaFcb ); 5080 } 5081 } _SEH2_END; 5082 5083 return; 5084 } 5085 5086 _Requires_lock_held_(_Global_critical_region_) 5087 VOID 5088 FatDeleteFile ( 5089 IN PIRP_CONTEXT IrpContext, 5090 IN PDCB TargetDcb, 5091 IN ULONG LfnOffset, 5092 IN ULONG DirentOffset, 5093 IN PDIRENT Dirent, 5094 IN PUNICODE_STRING Lfn 5095 ) 5096 { 5097 PFCB Fcb; 5098 PLIST_ENTRY Links; 5099 5100 PAGED_CODE(); 5101 5102 // 5103 // We can do the replace by removing the other Fcb(s) from 5104 // the prefix table. 5105 // 5106 5107 for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; 5108 Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; 5109 Links = Links->Flink) { 5110 5111 Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); 5112 5113 if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) && 5114 (Fcb->DirentOffsetWithinDirectory == DirentOffset)) { 5115 5116 NT_ASSERT( NodeType(Fcb) == FAT_NTC_FCB ); 5117 NT_ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset ); 5118 5119 if ( Fcb->UncleanCount != 0 ) { 5120 5121 #ifdef _MSC_VER 5122 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 5123 #endif 5124 FatBugCheck(0,0,0); 5125 5126 } else { 5127 5128 PERESOURCE Resource; 5129 5130 // 5131 // Make this fcb "appear" deleted, synchronizing with 5132 // paging IO. 5133 // 5134 5135 FatRemoveNames( IrpContext, Fcb ); 5136 5137 Resource = Fcb->Header.PagingIoResource; 5138 5139 (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); 5140 5141 SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE); 5142 5143 Fcb->ValidDataToDisk = 0; 5144 Fcb->Header.FileSize.QuadPart = 5145 Fcb->Header.ValidDataLength.QuadPart = 0; 5146 5147 Fcb->FirstClusterOfFile = 0; 5148 5149 ExReleaseResourceLite( Resource ); 5150 } 5151 } 5152 } 5153 5154 // 5155 // The file is not currently opened so we can delete the file 5156 // that is being overwritten. To do the operation we dummy 5157 // up an fcb, truncate allocation, delete the fcb, and delete 5158 // the dirent. 5159 // 5160 5161 Fcb = FatCreateFcb( IrpContext, 5162 TargetDcb->Vcb, 5163 TargetDcb, 5164 LfnOffset, 5165 DirentOffset, 5166 Dirent, 5167 Lfn, 5168 NULL, 5169 FALSE, 5170 FALSE ); 5171 5172 Fcb->Header.FileSize.LowPart = 0; 5173 5174 _SEH2_TRY { 5175 5176 FatTruncateFileAllocation( IrpContext, Fcb, 0 ); 5177 5178 FatDeleteDirent( IrpContext, Fcb, NULL, TRUE ); 5179 5180 } _SEH2_FINALLY { 5181 5182 FatDeleteFcb( IrpContext, &Fcb ); 5183 } _SEH2_END; 5184 } 5185 5186 5187