1 /*++
2
3 Copyright (c) 1989-2000 Microsoft Corporation
4
5 Module Name:
6
7 VerfySup.c
8
9 Abstract:
10
11 This module implements the Fat Verify volume and fcb/dcb support
12 routines
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_VERFYSUP)
24
25 //
26 // The Debug trace level for this module
27 //
28
29 #define Dbg (DEBUG_TRACE_VERFYSUP)
30
31 //
32 // Local procedure prototypes
33 //
34
35 VOID
36 FatResetFcb (
37 IN PIRP_CONTEXT IrpContext,
38 IN PFCB Fcb
39 );
40
41 BOOLEAN
42 FatMatchFileSize (
43 __in PIRP_CONTEXT IrpContext,
44 __in PDIRENT Dirent,
45 __in PFCB Fcb
46 );
47
48 _Requires_lock_held_(_Global_critical_region_)
49 VOID
50 FatDetermineAndMarkFcbCondition (
51 IN PIRP_CONTEXT IrpContext,
52 IN PFCB Fcb
53 );
54
55 WORKER_THREAD_ROUTINE FatDeferredCleanVolume;
56
57 VOID
58 NTAPI
59 FatDeferredCleanVolume (
60 _In_ PVOID Parameter
61 );
62
63 IO_COMPLETION_ROUTINE FatMarkVolumeCompletionRoutine;
64
65 NTSTATUS
66 NTAPI
67 FatMarkVolumeCompletionRoutine(
68 _In_ PDEVICE_OBJECT DeviceObject,
69 _In_ PIRP Irp,
70 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
71 );
72
73 #ifdef ALLOC_PRAGMA
74 #pragma alloc_text(PAGE, FatCheckDirtyBit)
75 #pragma alloc_text(PAGE, FatVerifyOperationIsLegal)
76 #pragma alloc_text(PAGE, FatDeferredCleanVolume)
77 #pragma alloc_text(PAGE, FatMatchFileSize)
78 #pragma alloc_text(PAGE, FatDetermineAndMarkFcbCondition)
79 #pragma alloc_text(PAGE, FatQuickVerifyVcb)
80 #pragma alloc_text(PAGE, FatPerformVerify)
81 #pragma alloc_text(PAGE, FatMarkFcbCondition)
82 #pragma alloc_text(PAGE, FatResetFcb)
83 #pragma alloc_text(PAGE, FatVerifyVcb)
84 #pragma alloc_text(PAGE, FatVerifyFcb)
85 #endif
86
87
88 VOID
FatMarkFcbCondition(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb,IN FCB_CONDITION FcbCondition,IN BOOLEAN Recursive)89 FatMarkFcbCondition (
90 IN PIRP_CONTEXT IrpContext,
91 IN PFCB Fcb,
92 IN FCB_CONDITION FcbCondition,
93 IN BOOLEAN Recursive
94 )
95
96 /*++
97
98 Routine Description:
99
100 This routines marks the entire Fcb/Dcb structure from Fcb down with
101 FcbCondition.
102
103 Arguments:
104
105 Fcb - Supplies the Fcb/Dcb being marked
106
107 FcbCondition - Supplies the setting to use for the Fcb Condition
108
109 Recursive - Specifies whether this condition should be applied to
110 all children (see the case where we are invalidating a live volume
111 for a case where this is now desireable).
112
113 Return Value:
114
115 None.
116
117 --*/
118
119 {
120 PAGED_CODE();
121
122 DebugTrace(+1, Dbg, "FatMarkFcbCondition, Fcb = %p\n", Fcb );
123
124 //
125 // If we are marking this Fcb something other than Good, we will need
126 // to have the Vcb exclusive.
127 //
128
129 NT_ASSERT( FcbCondition != FcbNeedsToBeVerified ? TRUE :
130 FatVcbAcquiredExclusive(IrpContext, Fcb->Vcb) );
131
132 //
133 // If this is a PagingFile it has to be good unless media underneath is
134 // removable. The "removable" check was added specifically for ReadyBoost,
135 // which opens its cache file on a removable device as a paging file and
136 // relies on the file system to validate its mapping information after a
137 // power transition.
138 //
139
140 if (!FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
141 FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
142
143 Fcb->FcbCondition = FcbGood;
144 return;
145 }
146
147 //
148 // Update the condition of the Fcb.
149 //
150
151 Fcb->FcbCondition = FcbCondition;
152
153 DebugTrace(0, Dbg, "MarkFcb: %wZ\n", &Fcb->FullFileName);
154
155 //
156 // This FastIo flag is based on FcbCondition, so update it now. This only
157 // applies to regular FCBs, of course.
158 //
159
160 if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) {
161
162 Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
163 }
164
165 if (FcbCondition == FcbNeedsToBeVerified) {
166 FatResetFcb( IrpContext, Fcb );
167 }
168
169 //
170 // Now if we marked NeedsVerify or Bad a directory then we also need to
171 // go and mark all of our children with the same condition.
172 //
173
174 if ( ((FcbCondition == FcbNeedsToBeVerified) ||
175 (FcbCondition == FcbBad)) &&
176 Recursive &&
177 ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) ||
178 (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) ) {
179
180 PFCB OriginalFcb = Fcb;
181
182 while ( (Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, OriginalFcb)) != NULL ) {
183
184 DebugTrace(0, Dbg, "MarkFcb: %wZ\n", &Fcb->FullFileName);
185
186 Fcb->FcbCondition = FcbCondition;
187
188 //
189 // We already know that FastIo is not possible since we are propagating
190 // a parent's bad/verify flag down the tree - IO to the children must
191 // take the long route for now.
192 //
193
194 Fcb->Header.IsFastIoPossible = FastIoIsNotPossible;
195
196 //
197 // Leave all the Fcbs in a condition to be verified.
198 //
199
200 if (FcbCondition == FcbNeedsToBeVerified) {
201 FatResetFcb( IrpContext, Fcb );
202 }
203
204 }
205 }
206
207 DebugTrace(-1, Dbg, "FatMarkFcbCondition -> VOID\n", 0);
208
209 return;
210 }
211
212 BOOLEAN
FatMarkDevForVerifyIfVcbMounted(IN PVCB Vcb)213 FatMarkDevForVerifyIfVcbMounted(
214 IN PVCB Vcb
215 )
216
217 /*++
218
219 Routine Description:
220
221 This routine checks to see if the specified Vcb is currently mounted on
222 the device or not. If it is, it sets the verify flag on the device, if
223 not then the state is noted in the Vcb.
224
225 Arguments:
226
227 Vcb - This is the volume to check.
228
229 Return Value:
230
231 TRUE if the device has been marked for verify here, FALSE otherwise.
232
233 --*/
234 {
235 BOOLEAN Marked = FALSE;
236 KIRQL SavedIrql;
237
238 IoAcquireVpbSpinLock( &SavedIrql );
239
240 #ifdef _MSC_VER
241 #pragma prefast( push )
242 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
243 #endif
244
245 if (Vcb->Vpb->RealDevice->Vpb == Vcb->Vpb) {
246
247 SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
248 Marked = TRUE;
249 }
250 else {
251
252 //
253 // Flag this to avoid the VPB spinlock in future passes.
254 //
255
256 SetFlag( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE);
257 }
258
259 #ifdef _MSC_VER
260 #pragma prefast( pop )
261 #endif
262
263 IoReleaseVpbSpinLock( SavedIrql );
264
265 return Marked;
266 }
267
268
269 VOID
FatVerifyVcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)270 FatVerifyVcb (
271 IN PIRP_CONTEXT IrpContext,
272 IN PVCB Vcb
273 )
274
275 /*++
276
277 Routine Description:
278
279 This routines verifies that the Vcb still denotes a valid Volume
280 If the Vcb is bad it raises an error condition.
281
282 Arguments:
283
284 Vcb - Supplies the Vcb being verified
285
286 Return Value:
287
288 None.
289
290 --*/
291
292 {
293 BOOLEAN DevMarkedForVerify;
294
295 PAGED_CODE();
296
297 DebugTrace(+1, Dbg, "FatVerifyVcb, Vcb = %p\n", Vcb );
298
299 //
300 // If the verify volume flag in the device object is set
301 // this means the media has potentially changed.
302 //
303 // Note that we only force this ping for create operations.
304 // For others we take a sporting chance. If in the end we
305 // have to physically access the disk, the right thing will happen.
306 //
307
308 DevMarkedForVerify = BooleanFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
309
310 //
311 // We ALWAYS force CREATE requests on unmounted volumes through the
312 // verify path. These requests could have been in limbo between
313 // IoCheckMountedVpb and us, when a verify/mount took place and caused
314 // a completely different fs/volume to be mounted. In this case the
315 // checks above may not have caught the condition, since we may already
316 // have verified (wrong volume) and decided that we have nothing to do.
317 // We want the requests to be re routed to the currently mounted volume,
318 // since they were directed at the 'drive', not our volume. So we take
319 // the verify path for synchronisation, and the request will eventually
320 // be bounced back to IO with STATUS_REPARSE by our verify handler.
321 //
322
323 if (!DevMarkedForVerify &&
324 (IrpContext->MajorFunction == IRP_MJ_CREATE) &&
325 (IrpContext->OriginatingIrp != NULL)) {
326
327 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp);
328
329 if ((IrpSp->FileObject->RelatedFileObject == NULL) &&
330 (Vcb->VcbCondition == VcbNotMounted)) {
331
332 DevMarkedForVerify = TRUE;
333 }
334 }
335
336 //
337 // Raise any error condition otherwise.
338 //
339
340 if (DevMarkedForVerify) {
341
342 DebugTrace(0, Dbg, "The Vcb needs to be verified\n", 0);
343
344 IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
345 Vcb->Vpb->RealDevice );
346
347 FatNormalizeAndRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
348 }
349
350 //
351 // Check the operation is legal for current Vcb state.
352 //
353
354 FatQuickVerifyVcb( IrpContext, Vcb );
355
356 DebugTrace(-1, Dbg, "FatVerifyVcb -> VOID\n", 0);
357 }
358
359
_Requires_lock_held_(_Global_critical_region_)360 _Requires_lock_held_(_Global_critical_region_)
361 VOID
362 FatVerifyFcb (
363 IN PIRP_CONTEXT IrpContext,
364 IN PFCB Fcb
365 )
366
367 /*++
368
369 Routine Description:
370
371 This routines verifies that the Fcb still denotes the same file.
372 If the Fcb is bad it raises a error condition.
373
374 Arguments:
375
376 Fcb - Supplies the Fcb being verified
377
378 Return Value:
379
380 None.
381
382 --*/
383
384 {
385 PFCB CurrentFcb;
386
387 PAGED_CODE();
388
389 DebugTrace(+1, Dbg, "FatVerifyFcb, Vcb = %p\n", Fcb );
390
391 //
392 // Always refuse operations on dismounted volumes.
393 //
394
395 if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
396
397 FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED );
398 }
399
400 //
401 // If this is the Fcb of a deleted dirent or our parent is deleted,
402 // no-op this call with the hope that the caller will do the right thing.
403 // The only caller we really have to worry about is the AdvanceOnly
404 // callback for setting valid data length from Cc, this will happen after
405 // cleanup (and file deletion), just before the SCM is ripped down.
406 //
407
408 if (IsFileDeleted( IrpContext, Fcb ) ||
409 ((NodeType(Fcb) != FAT_NTC_ROOT_DCB) &&
410 IsFileDeleted( IrpContext, Fcb->ParentDcb ))) {
411
412 return;
413 }
414
415 //
416 // If we are not in the process of doing a verify,
417 // first do a quick spot check on the Vcb.
418 //
419
420 if ( Fcb->Vcb->VerifyThread != KeGetCurrentThread() ) {
421
422 FatQuickVerifyVcb( IrpContext, Fcb->Vcb );
423 }
424
425 //
426 // Now based on the condition of the Fcb we'll either return
427 // immediately to the caller, raise a condition, or do some work
428 // to verify the Fcb.
429 //
430
431 switch (Fcb->FcbCondition) {
432
433 case FcbGood:
434
435 DebugTrace(0, Dbg, "The Fcb is good\n", 0);
436 break;
437
438 case FcbBad:
439
440 FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
441 break;
442
443 case FcbNeedsToBeVerified:
444
445 //
446 // We loop here checking our ancestors until we hit an Fcb which
447 // is either good or bad.
448 //
449
450 CurrentFcb = Fcb;
451
452 while (CurrentFcb->FcbCondition == FcbNeedsToBeVerified) {
453
454 FatDetermineAndMarkFcbCondition(IrpContext, CurrentFcb);
455
456 //
457 // If this Fcb didn't make it, or it was the Root Dcb, exit
458 // the loop now, else continue with out parent.
459 //
460
461 if ( (CurrentFcb->FcbCondition != FcbGood) ||
462 (NodeType(CurrentFcb) == FAT_NTC_ROOT_DCB) ) {
463
464 break;
465 }
466
467 CurrentFcb = CurrentFcb->ParentDcb;
468 }
469
470 //
471 // Now we can just look at ourselves to see how we did.
472 //
473
474 if (Fcb->FcbCondition != FcbGood) {
475
476 FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
477 }
478
479 break;
480
481 default:
482
483 DebugDump("Invalid FcbCondition\n", 0, Fcb);
484
485 #ifdef _MSC_VER
486 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
487 #endif
488 FatBugCheck( Fcb->FcbCondition, 0, 0 );
489 }
490
491 DebugTrace(-1, Dbg, "FatVerifyFcb -> VOID\n", 0);
492
493 return;
494 }
495
496
497 VOID
498 NTAPI
FatDeferredCleanVolume(_In_ PVOID Parameter)499 FatDeferredCleanVolume (
500 _In_ PVOID Parameter
501 )
502
503 /*++
504
505 Routine Description:
506
507 This is the routine that performs the actual FatMarkVolumeClean call.
508 It assures that the target volume still exists as there ia a race
509 condition between queueing the ExWorker item and volumes going away.
510
511 Arguments:
512
513 Parameter - Points to a clean volume packet that was allocated from pool
514
515 Return Value:
516
517 None.
518
519 --*/
520
521 {
522 PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
523 PLIST_ENTRY Links;
524 PVCB Vcb;
525 IRP_CONTEXT IrpContext;
526 BOOLEAN VcbExists = FALSE;
527
528 PAGED_CODE();
529
530 DebugTrace(+1, Dbg, "FatDeferredCleanVolume\n", 0);
531
532 Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter;
533
534 Vcb = Packet->Vcb;
535
536 //
537 // Make us appear as a top level FSP request so that we will
538 // receive any errors from the operation.
539 //
540
541 IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
542
543 //
544 // Dummy up and Irp Context so we can call our worker routines
545 //
546
547 RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT));
548
549 SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
550
551 //
552 // Acquire shared access to the global lock and make sure this volume
553 // still exists.
554 //
555
556 #ifdef _MSC_VER
557 #pragma prefast( push )
558 #pragma prefast( disable: 28193, "this will always wait" )
559 #endif
560 FatAcquireSharedGlobal( &IrpContext );
561 #ifdef _MSC_VER
562 #pragma prefast( pop )
563 #endif
564
565 for (Links = FatData.VcbQueue.Flink;
566 Links != &FatData.VcbQueue;
567 Links = Links->Flink) {
568
569 PVCB ExistingVcb;
570
571 ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
572
573 if ( Vcb == ExistingVcb ) {
574
575 VcbExists = TRUE;
576 break;
577 }
578 }
579
580 //
581 // If the vcb is good then mark it clean. Ignore any problems.
582 //
583
584 if ( VcbExists &&
585 (Vcb->VcbCondition == VcbGood) &&
586 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) ) {
587
588 _SEH2_TRY {
589
590 if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
591
592 FatMarkVolume( &IrpContext, Vcb, VolumeClean );
593 }
594
595 //
596 // Check for a pathological race condition, and fix it.
597 //
598
599 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) {
600
601 FatMarkVolume( &IrpContext, Vcb, VolumeDirty );
602
603 } else {
604
605 //
606 // Unlock the volume if it is removable.
607 //
608
609 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
610 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
611
612 FatToggleMediaEjectDisable( &IrpContext, Vcb, FALSE );
613 }
614 }
615
616 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
617 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
618
619 NOTHING;
620 } _SEH2_END;
621 }
622
623 //
624 // Release the global resource, unpin and repinned Bcbs and return.
625 //
626
627 FatReleaseGlobal( &IrpContext );
628
629 _SEH2_TRY {
630
631 FatUnpinRepinnedBcbs( &IrpContext );
632
633 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
634 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
635
636 NOTHING;
637 } _SEH2_END;
638
639 IoSetTopLevelIrp( NULL );
640
641 //
642 // and finally free the packet.
643 //
644
645 ExFreePool( Packet );
646
647 return;
648 }
649
650
651
652 VOID
653 NTAPI
FatCleanVolumeDpc(_In_ PKDPC Dpc,_In_opt_ PVOID DeferredContext,_In_opt_ PVOID SystemArgument1,_In_opt_ PVOID SystemArgument2)654 FatCleanVolumeDpc (
655 _In_ PKDPC Dpc,
656 _In_opt_ PVOID DeferredContext,
657 _In_opt_ PVOID SystemArgument1,
658 _In_opt_ PVOID SystemArgument2
659 )
660
661 /*++
662
663 Routine Description:
664
665 This routine is dispatched 5 seconds after the last disk structure was
666 modified in a specific volume, and exqueues an execuative worker thread
667 to perform the actual task of marking the volume dirty.
668
669 Arguments:
670
671 DefferedContext - Contains the Vcb to process.
672
673 Return Value:
674
675 None.
676
677 --*/
678
679 {
680 PVCB Vcb;
681 PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
682
683 UNREFERENCED_PARAMETER( SystemArgument1 );
684 UNREFERENCED_PARAMETER( SystemArgument2 );
685 UNREFERENCED_PARAMETER( Dpc );
686
687 Vcb = (PVCB)DeferredContext;
688
689
690 //
691 // If there is still dirty data (highly unlikely), set the timer for a
692 // second in the future.
693 //
694
695 if (CcIsThereDirtyData(Vcb->Vpb)) {
696
697 LARGE_INTEGER TwoSecondsFromNow;
698
699 TwoSecondsFromNow.QuadPart = (LONG)-2*1000*1000*10;
700
701 KeSetTimer( &Vcb->CleanVolumeTimer,
702 TwoSecondsFromNow,
703 &Vcb->CleanVolumeDpc );
704
705 return;
706 }
707
708 //
709 // If we couldn't get pool, oh well....
710 //
711
712 Packet = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET), ' taF');
713
714 if ( Packet ) {
715
716 Packet->Vcb = Vcb;
717 Packet->Irp = NULL;
718
719 //
720 // Clear the dirty flag now since we cannot synchronize after this point.
721 //
722
723 ClearFlag( Packet->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
724
725 ExInitializeWorkItem( &Packet->Item, &FatDeferredCleanVolume, Packet );
726
727 #ifdef _MSC_VER
728 #pragma prefast( suppress:28159, "prefast indicates this is an obsolete API, but it is ok for fastfat to keep using it" )
729 #endif
730 ExQueueWorkItem( &Packet->Item, CriticalWorkQueue );
731 }
732
733 return;
734 }
735
736
_Requires_lock_held_(_Global_critical_region_)737 _Requires_lock_held_(_Global_critical_region_)
738 VOID
739 FatMarkVolume (
740 IN PIRP_CONTEXT IrpContext,
741 IN PVCB Vcb,
742 IN FAT_VOLUME_STATE VolumeState
743 )
744
745 /*++
746
747 Routine Description:
748
749 This routine moves the physically marked volume state between the clean
750 and dirty states. For compatibility with Win9x, we manipulate both the
751 historical DOS (on==clean in index 1 of the FAT) and NT (on==dirty in
752 the CurrentHead field of the BPB) dirty bits.
753
754 Arguments:
755
756 Vcb - Supplies the Vcb being modified
757
758 VolumeState - Supplies the state the volume is transitioning to
759
760 Return Value:
761
762 None.
763
764 --*/
765
766 {
767 PCHAR Sector;
768 PBCB Bcb = NULL;
769 KEVENT Event;
770 PIRP Irp = NULL;
771 NTSTATUS Status;
772 BOOLEAN FsInfoUpdate = FALSE;
773 ULONG FsInfoOffset = 0;
774 ULONG ThisPass;
775 LARGE_INTEGER Offset;
776 BOOLEAN abort = FALSE;
777
778 DebugTrace(+1, Dbg, "FatMarkVolume, Vcb = %p\n", Vcb);
779
780 //
781 // We had best not be trying to scribble dirty/clean bits if the
782 // volume is write protected. The responsibility lies with the
783 // callers to make sure that operations that could cause a state
784 // change cannot happen. There are a few, though, that show it
785 // just doesn't make sense to force everyone to do the dinky
786 // check.
787 //
788
789 //
790 // If we were called for FAT12 or readonly media, return immediately.
791 //
792
793 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) ||
794 FatIsFat12( Vcb )) {
795
796 return;
797 }
798
799 //
800 // We have two possible additional tasks to do to mark a volume
801 //
802 // Pass 0) Flip the dirty bit in the Bpb
803 // Pass 1) Rewrite the FsInfo sector for FAT32 if needed
804 //
805 // In most cases we can collapse these two either because the volume
806 // is either not FAT32 or the FsInfo sector is adjacent to the boot sector.
807 //
808
809 for (ThisPass = 0; ThisPass < 2; ThisPass++) {
810
811 //
812 // If this volume is being dirtied, or isn't FAT32, or if it is and
813 // we were able to perform the fast update, or the bpb lied to us
814 // about where the FsInfo went, we're done - no FsInfo to update in
815 // a seperate write.
816 //
817
818 if (ThisPass == 1 && (!FatIsFat32( Vcb ) ||
819 VolumeState != VolumeClean ||
820 FsInfoUpdate ||
821 Vcb->Bpb.FsInfoSector == 0)) {
822
823 break;
824 }
825
826 //
827 // Bail if we get an IO error.
828 //
829
830 _SEH2_TRY {
831
832 ULONG PinLength;
833 ULONG WriteLength;
834
835 //
836 // If the FAT table is 12-bit then our strategy is to pin the entire
837 // thing when any of it is modified. Here we're going to pin the
838 // first page, so in the 12-bit case we also want to pin the rest
839 // of the FAT table.
840 //
841
842 Offset.QuadPart = 0;
843
844 if (Vcb->AllocationSupport.FatIndexBitSize == 12) {
845
846 //
847 // But we only write back the first sector.
848 //
849
850 PinLength = FatReservedBytes(&Vcb->Bpb) + FatBytesPerFat(&Vcb->Bpb);
851 WriteLength = Vcb->Bpb.BytesPerSector;
852
853 } else {
854
855 WriteLength = PinLength = Vcb->Bpb.BytesPerSector;
856
857 //
858 // If this is a FAT32 volume going into the clean state,
859 // see about doing the FsInfo sector.
860 //
861
862 if (FatIsFat32( Vcb ) && VolumeState == VolumeClean) {
863
864 //
865 // If the FsInfo sector immediately follows the boot sector,
866 // we can do this in a single operation by rewriting both
867 // sectors at once.
868 //
869
870 if (Vcb->Bpb.FsInfoSector == 1) {
871
872 NT_ASSERT( ThisPass == 0 );
873
874 FsInfoUpdate = TRUE;
875 FsInfoOffset = Vcb->Bpb.BytesPerSector;
876 WriteLength = PinLength = Vcb->Bpb.BytesPerSector * 2;
877
878 } else if (ThisPass == 1) {
879
880 //
881 // We are doing an explicit write to the FsInfo sector.
882 //
883
884 FsInfoUpdate = TRUE;
885 FsInfoOffset = 0;
886
887 Offset.QuadPart = Vcb->Bpb.BytesPerSector * Vcb->Bpb.FsInfoSector;
888 }
889 }
890 }
891
892 //
893 // Call Cc directly here so that we can avoid overhead and push this
894 // right down to the disk.
895 //
896
897 CcPinRead( Vcb->VirtualVolumeFile,
898 &Offset,
899 PinLength,
900 TRUE,
901 &Bcb,
902 (PVOID *)&Sector );
903
904 DbgDoit( IrpContext->PinCount += 1 )
905
906 //
907 // Set the Bpb on Pass 0 always
908 //
909
910 if (ThisPass == 0) {
911
912 PCHAR CurrentHead;
913
914 //
915 // Before we do anything, doublecheck that this still looks like a
916 // FAT bootsector. If it doesn't, something remarkable happened
917 // and we should avoid touching the volume.
918 //
919 // THIS IS TEMPORARY (but may last a while)
920 //
921
922 if (!FatIsBootSectorFat( (PPACKED_BOOT_SECTOR) Sector )) {
923 abort = TRUE;
924 _SEH2_LEAVE;
925 }
926
927 if (FatIsFat32( Vcb )) {
928
929 CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR_EX) Sector)->CurrentHead;
930
931 } else {
932
933 CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR) Sector)->CurrentHead;
934 }
935
936 if (VolumeState == VolumeClean) {
937
938 ClearFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY );
939
940 } else {
941
942 SetFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY );
943
944 //
945 // In addition, if this request received an error that may indicate
946 // media corruption, have autochk perform a surface test.
947 //
948
949 if ( VolumeState == VolumeDirtyWithSurfaceTest ) {
950
951 SetFlag( *CurrentHead, FAT_BOOT_SECTOR_TEST_SURFACE );
952 }
953 }
954 }
955
956 //
957 // Update the FsInfo as appropriate.
958 //
959
960 if (FsInfoUpdate) {
961
962 PFSINFO_SECTOR FsInfoSector = (PFSINFO_SECTOR) ((PCHAR)Sector + FsInfoOffset);
963
964 //
965 // We just rewrite all of the spec'd fields. Note that we don't
966 // care to synchronize with the allocation package - this will be
967 // quickly taken care of by a re-dirtying of the volume if a change
968 // is racing with us. Remember that this is all a compatibility
969 // deference for Win9x FAT32 - NT will never look at this information.
970 //
971
972 FsInfoSector->SectorBeginSignature = FSINFO_SECTOR_BEGIN_SIGNATURE;
973 FsInfoSector->FsInfoSignature = FSINFO_SIGNATURE;
974 FsInfoSector->FreeClusterCount = Vcb->AllocationSupport.NumberOfFreeClusters;
975 FsInfoSector->NextFreeCluster = Vcb->ClusterHint;
976 FsInfoSector->SectorEndSignature = FSINFO_SECTOR_END_SIGNATURE;
977 }
978
979 //
980 // Initialize the event we're going to use
981 //
982
983 KeInitializeEvent( &Event, NotificationEvent, FALSE );
984
985 //
986 // Build the irp for the operation and also set the override flag.
987 // Note that we may be at APC level, so do this asyncrhonously and
988 // use an event for synchronization as normal request completion
989 // cannot occur at APC level.
990 //
991
992 Irp = IoBuildAsynchronousFsdRequest( IRP_MJ_WRITE,
993 Vcb->TargetDeviceObject,
994 (PVOID)Sector,
995 WriteLength,
996 &Offset,
997 NULL );
998
999 if ( Irp == NULL ) {
1000
1001 try_return(NOTHING);
1002 }
1003
1004 //
1005 // Make this operation write-through. It never hurts to try to be
1006 // safer about this, even though we aren't logged.
1007 //
1008
1009 SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_WRITE_THROUGH );
1010
1011 //
1012 // Set up the completion routine
1013 //
1014
1015 IoSetCompletionRoutine( Irp,
1016 FatMarkVolumeCompletionRoutine,
1017 &Event,
1018 TRUE,
1019 TRUE,
1020 TRUE );
1021
1022 //
1023 // Call the device to do the write and wait for it to finish.
1024 // Igmore any return status.
1025 //
1026
1027 Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
1028
1029 if (Status == STATUS_PENDING) {
1030
1031 (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
1032 }
1033
1034 try_exit: NOTHING;
1035 } _SEH2_FINALLY {
1036
1037 //
1038 // Clean up the Irp and Mdl
1039 //
1040
1041
1042 if (Irp) {
1043
1044 //
1045 // If there is an MDL (or MDLs) associated with this I/O
1046 // request, Free it (them) here. This is accomplished by
1047 // walking the MDL list hanging off of the IRP and deallocating
1048 // each MDL encountered.
1049 //
1050
1051 while (Irp->MdlAddress != NULL) {
1052
1053 PMDL NextMdl;
1054
1055 NextMdl = Irp->MdlAddress->Next;
1056
1057 MmUnlockPages( Irp->MdlAddress );
1058
1059 IoFreeMdl( Irp->MdlAddress );
1060
1061 Irp->MdlAddress = NextMdl;
1062 }
1063
1064 IoFreeIrp( Irp );
1065 }
1066
1067 if (Bcb != NULL) {
1068
1069 FatUnpinBcb( IrpContext, Bcb );
1070 }
1071 } _SEH2_END;
1072 }
1073
1074 if (!abort) {
1075
1076 //
1077 // Flip the dirty bit in the FAT
1078 //
1079
1080 if (VolumeState == VolumeDirty) {
1081
1082 FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_DIRTY_VOLUME);
1083
1084 } else {
1085
1086 FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_CLEAN_VOLUME);
1087 }
1088 }
1089
1090 DebugTrace(-1, Dbg, "FatMarkVolume -> VOID\n", 0);
1091
1092 return;
1093 }
1094
1095
1096 VOID
1097 NTAPI
FatFspMarkVolumeDirtyWithRecover(PVOID Parameter)1098 FatFspMarkVolumeDirtyWithRecover(
1099 PVOID Parameter
1100 )
1101
1102 /*++
1103
1104 Routine Description:
1105
1106 This is the routine that performs the actual FatMarkVolume Dirty call
1107 on a paging file Io that encounters a media error. It is responsible
1108 for completing the PagingIo Irp as soon as this is done.
1109
1110 Note: this routine (and thus FatMarkVolume()) must be resident as
1111 the paging file might be damaged at this point.
1112
1113 Arguments:
1114
1115 Parameter - Points to a dirty volume packet that was allocated from pool
1116
1117 Return Value:
1118
1119 None.
1120
1121 --*/
1122
1123 {
1124 PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
1125 PVCB Vcb;
1126 IRP_CONTEXT IrpContext;
1127 PIRP Irp;
1128
1129 DebugTrace(+1, Dbg, "FatFspMarkVolumeDirtyWithRecover\n", 0);
1130
1131 Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter;
1132
1133 Vcb = Packet->Vcb;
1134 Irp = Packet->Irp;
1135
1136 //
1137 // Dummy up the IrpContext so we can call our worker routines
1138 //
1139
1140 RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT));
1141
1142 SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
1143 IrpContext.OriginatingIrp = Irp;
1144
1145 //
1146 // Make us appear as a top level FSP request so that we will
1147 // receive any errors from the operation.
1148 //
1149
1150 IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
1151
1152 //
1153 // Try to write out the dirty bit. If something goes wrong, we
1154 // tried.
1155 //
1156
1157 _SEH2_TRY {
1158
1159 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
1160
1161 FatMarkVolume( &IrpContext, Vcb, VolumeDirtyWithSurfaceTest );
1162
1163 } _SEH2_EXCEPT(FatExceptionFilter( &IrpContext, _SEH2_GetExceptionInformation() )) {
1164
1165 NOTHING;
1166 } _SEH2_END;
1167
1168 IoSetTopLevelIrp( NULL );
1169
1170 //
1171 // Now complete the originating Irp or set the synchronous event.
1172 //
1173
1174 if (Packet->Event) {
1175 KeSetEvent( Packet->Event, 0, FALSE );
1176 } else {
1177 IoCompleteRequest( Irp, IO_DISK_INCREMENT );
1178 }
1179
1180 DebugTrace(-1, Dbg, "FatFspMarkVolumeDirtyWithRecover -> VOID\n", 0);
1181 }
1182
1183
1184 VOID
FatCheckDirtyBit(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)1185 FatCheckDirtyBit (
1186 IN PIRP_CONTEXT IrpContext,
1187 IN PVCB Vcb
1188 )
1189
1190 /*++
1191
1192 Routine Description:
1193
1194 This routine looks at the volume dirty bit, and depending on the state of
1195 VCB_STATE_FLAG_MOUNTED_DIRTY, the appropriate action is taken.
1196
1197 Arguments:
1198
1199 Vcb - Supplies the Vcb being queried.
1200
1201 Return Value:
1202
1203 None.
1204
1205 --*/
1206
1207 {
1208 BOOLEAN Dirty;
1209
1210 PPACKED_BOOT_SECTOR BootSector;
1211 PBCB BootSectorBcb;
1212
1213 UNICODE_STRING VolumeLabel;
1214
1215 PAGED_CODE();
1216
1217 //
1218 // Look in the boot sector
1219 //
1220
1221 FatReadVolumeFile( IrpContext,
1222 Vcb,
1223 0,
1224 sizeof(PACKED_BOOT_SECTOR),
1225 &BootSectorBcb,
1226 (PVOID *)&BootSector );
1227
1228 _SEH2_TRY {
1229
1230 //
1231 // Check if the magic bit is set
1232 //
1233
1234 if (IsBpbFat32(&BootSector->PackedBpb)) {
1235 Dirty = BooleanFlagOn( ((PPACKED_BOOT_SECTOR_EX)BootSector)->CurrentHead,
1236 FAT_BOOT_SECTOR_DIRTY );
1237 } else {
1238 Dirty = BooleanFlagOn( BootSector->CurrentHead, FAT_BOOT_SECTOR_DIRTY );
1239 }
1240
1241 //
1242 // Setup the VolumeLabel string
1243 //
1244
1245 VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength;
1246 VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
1247 VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0];
1248
1249 if ( Dirty ) {
1250
1251 //
1252 // Do not trigger the mounted dirty bit if this is a verify
1253 // and the volume is a boot or paging device. We know that
1254 // a boot or paging device cannot leave the system, and thus
1255 // that on its mount we will have figured this out correctly.
1256 //
1257 // This logic is a reasonable change. Why?
1258 // 'cause setup cracked a non-exclusive DASD handle near the
1259 // end of setup, wrote some data, closed the handle and we
1260 // set the verify bit ... came back around and saw that other
1261 // arbitrary activity had left the volume in a temporarily dirty
1262 // state.
1263 //
1264 // Of course, the real problem is that we don't have a journal.
1265 //
1266
1267 if (!(IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
1268 IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME &&
1269 FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE))) {
1270
1271 KdPrintEx((DPFLTR_FASTFAT_ID,
1272 DPFLTR_INFO_LEVEL,
1273 "FASTFAT: WARNING! Mounting Dirty Volume %Z\n",
1274 &VolumeLabel));
1275
1276 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
1277 }
1278
1279 } else {
1280
1281 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
1282
1283 KdPrintEx((DPFLTR_FASTFAT_ID,
1284 DPFLTR_INFO_LEVEL,
1285 "FASTFAT: Volume %Z has been cleaned.\n",
1286 &VolumeLabel));
1287
1288 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
1289
1290 } else {
1291
1292 (VOID)FsRtlBalanceReads( Vcb->TargetDeviceObject );
1293 }
1294 }
1295
1296 } _SEH2_FINALLY {
1297
1298 FatUnpinBcb( IrpContext, BootSectorBcb );
1299 } _SEH2_END;
1300 }
1301
1302
1303 VOID
FatVerifyOperationIsLegal(IN PIRP_CONTEXT IrpContext)1304 FatVerifyOperationIsLegal (
1305 IN PIRP_CONTEXT IrpContext
1306 )
1307
1308 /*++
1309
1310 Routine Description:
1311
1312 This routine determines is the requested operation should be allowed to
1313 continue. It either returns to the user if the request is Okay, or
1314 raises an appropriate status.
1315
1316 Arguments:
1317
1318 Irp - Supplies the Irp to check
1319
1320 Return Value:
1321
1322 None.
1323
1324 --*/
1325
1326 {
1327 PIRP Irp;
1328 PFILE_OBJECT FileObject;
1329
1330 PAGED_CODE();
1331
1332 Irp = IrpContext->OriginatingIrp;
1333
1334 //
1335 // If the Irp is not present, then we got here via close.
1336 //
1337 //
1338
1339 if ( Irp == NULL ) {
1340
1341 return;
1342 }
1343
1344 FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
1345
1346 //
1347 // If there is not a file object, we cannot continue.
1348 //
1349
1350 if ( FileObject == NULL ) {
1351
1352 return;
1353 }
1354
1355 //
1356 // If the file object has already been cleaned up, and
1357 //
1358 // A) This request is a paging io read or write, or
1359 // B) This request is a close operation, or
1360 // C) This request is a set or query info call (for Lou)
1361 // D) This is an MDL complete
1362 //
1363 // let it pass, otherwise return STATUS_FILE_CLOSED.
1364 //
1365
1366 if ( FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE) ) {
1367
1368 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
1369
1370 if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) ||
1371 (IrpSp->MajorFunction == IRP_MJ_CLOSE ) ||
1372 (IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION) ||
1373 (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
1374 ( ( (IrpSp->MajorFunction == IRP_MJ_READ) ||
1375 (IrpSp->MajorFunction == IRP_MJ_WRITE) ) &&
1376 FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) {
1377
1378 NOTHING;
1379
1380 } else {
1381
1382 FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED );
1383 }
1384 }
1385
1386 return;
1387 }
1388
1389
1390
1391 //
1392 // Internal support routine
1393 //
1394
1395 VOID
FatResetFcb(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb)1396 FatResetFcb (
1397 IN PIRP_CONTEXT IrpContext,
1398 IN PFCB Fcb
1399 )
1400
1401 /*++
1402
1403 Routine Description:
1404
1405 This routine is called when an Fcb has been marked as needs to be verified.
1406
1407 It does the following tasks:
1408
1409 - Reset Mcb mapping information
1410 - For directories, reset dirent hints
1411 - Set allocation size to unknown
1412
1413 Arguments:
1414
1415 Fcb - Supplies the Fcb to reset
1416
1417 Return Value:
1418
1419 None.
1420
1421 --*/
1422
1423 {
1424 LOGICAL IsRealPagingFile;
1425
1426 PAGED_CODE();
1427 UNREFERENCED_PARAMETER( IrpContext );
1428
1429 //
1430 // Don't do the two following operations for the Root Dcb
1431 // of a non FAT32 volume or paging files. Paging files!?
1432 // Yes, if someone diddles a volume we try to reverify all
1433 // of the Fcbs just in case; however, there is no safe way
1434 // to chuck and retrieve the mapping pair information for
1435 // a real paging file. Lose it and die.
1436 //
1437 // An exception is made for ReadyBoost cache files, which
1438 // are created as paging files on removable devices and
1439 // require validation after a power transition.
1440 //
1441
1442 if (!FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
1443 FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
1444
1445 IsRealPagingFile = TRUE;
1446
1447 } else {
1448
1449 IsRealPagingFile = FALSE;
1450 }
1451
1452 if ( (NodeType(Fcb) != FAT_NTC_ROOT_DCB ||
1453 FatIsFat32( Fcb->Vcb )) &&
1454 !IsRealPagingFile ) {
1455
1456 //
1457 // Reset the mcb mapping.
1458 //
1459
1460 FsRtlRemoveLargeMcbEntry( &Fcb->Mcb, 0, 0xFFFFFFFF );
1461
1462 //
1463 // Reset the allocation size to 0 or unknown
1464 //
1465
1466 if ( Fcb->FirstClusterOfFile == 0 ) {
1467
1468 Fcb->Header.AllocationSize.QuadPart = 0;
1469
1470 } else {
1471
1472 Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
1473 }
1474 }
1475
1476 //
1477 // If this is a directory, reset the hints.
1478 //
1479
1480 if ( (NodeType(Fcb) == FAT_NTC_DCB) ||
1481 (NodeType(Fcb) == FAT_NTC_ROOT_DCB) ) {
1482
1483 //
1484 // Force a rescan of the directory
1485 //
1486
1487 Fcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff;
1488 Fcb->Specific.Dcb.DeletedDirentHint = 0xffffffff;
1489 }
1490 }
1491
1492
1493
1494 BOOLEAN
FatMatchFileSize(__in PIRP_CONTEXT IrpContext,__in PDIRENT Dirent,__in PFCB Fcb)1495 FatMatchFileSize (
1496 __in PIRP_CONTEXT IrpContext,
1497 __in PDIRENT Dirent,
1498 __in PFCB Fcb
1499 )
1500 {
1501
1502 UNREFERENCED_PARAMETER(IrpContext);
1503
1504 if (NodeType(Fcb) != FAT_NTC_FCB) {
1505 return TRUE;
1506 }
1507
1508
1509 if (Fcb->Header.FileSize.LowPart != Dirent->FileSize) {
1510 return FALSE;
1511 }
1512
1513
1514 return TRUE;
1515 }
1516
1517 //
1518 // Internal support routine
1519 //
1520
_Requires_lock_held_(_Global_critical_region_)1521 _Requires_lock_held_(_Global_critical_region_)
1522 VOID
1523 FatDetermineAndMarkFcbCondition (
1524 IN PIRP_CONTEXT IrpContext,
1525 IN PFCB Fcb
1526 )
1527
1528 /*++
1529
1530 Routine Description:
1531
1532 This routine checks a specific Fcb to see if it is different from what's
1533 on the disk. The following things are checked:
1534
1535 - File Name
1536 - File Size (if not directory)
1537 - First Cluster Of File
1538 - Dirent Attributes
1539
1540 Arguments:
1541
1542 Fcb - Supplies the Fcb to examine
1543
1544 Return Value:
1545
1546 None.
1547
1548 --*/
1549
1550 {
1551 PDIRENT Dirent;
1552 PBCB DirentBcb;
1553 ULONG FirstClusterOfFile;
1554
1555 OEM_STRING Name;
1556 CHAR Buffer[16];
1557
1558 PAGED_CODE();
1559
1560 //
1561 // If this is the Root Dcb, special case it. That is, we know
1562 // by definition that it is good since it is fixed in the volume
1563 // structure.
1564 //
1565
1566 if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {
1567
1568 FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE );
1569
1570 return;
1571 }
1572
1573 // The first thing we need to do to verify ourselves is
1574 // locate the dirent on the disk.
1575 //
1576
1577 FatGetDirentFromFcbOrDcb( IrpContext,
1578 Fcb,
1579 TRUE,
1580 &Dirent,
1581 &DirentBcb );
1582 //
1583 // If we couldn't get the dirent, this fcb must be bad (case of
1584 // enclosing directory shrinking during the time it was ejected).
1585 //
1586
1587 if (DirentBcb == NULL) {
1588
1589 FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE );
1590
1591 return;
1592 }
1593
1594 //
1595 // We located the dirent for ourselves now make sure it
1596 // is really ours by comparing the Name and FatFlags.
1597 // Then for a file we also check the file size.
1598 //
1599 // Note that we have to unpin the Bcb before calling FatResetFcb
1600 // in order to avoid a deadlock in CcUninitializeCacheMap.
1601 //
1602
1603 _SEH2_TRY {
1604
1605 Name.MaximumLength = 16;
1606 Name.Buffer = &Buffer[0];
1607
1608 Fat8dot3ToString( IrpContext, Dirent, FALSE, &Name );
1609
1610 //
1611 // We need to calculate the first cluster 'cause FAT32 splits
1612 // this field across the dirent.
1613 //
1614
1615 FirstClusterOfFile = Dirent->FirstClusterOfFile;
1616
1617 if (FatIsFat32( Fcb->Vcb )) {
1618
1619 FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16;
1620 }
1621
1622 if (!RtlEqualString( &Name, &Fcb->ShortName.Name.Oem, TRUE )
1623
1624 ||
1625
1626 !FatMatchFileSize(IrpContext, Dirent, Fcb )
1627
1628 ||
1629
1630 (FirstClusterOfFile != Fcb->FirstClusterOfFile)
1631
1632 ||
1633
1634 (Dirent->Attributes != Fcb->DirentFatFlags) ) {
1635
1636 FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE );
1637
1638 } else {
1639
1640 //
1641 // We passed. Get the Fcb ready to use again.
1642 //
1643
1644 FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE );
1645 }
1646
1647 } _SEH2_FINALLY {
1648
1649 FatUnpinBcb( IrpContext, DirentBcb );
1650 } _SEH2_END;
1651
1652 return;
1653 }
1654
1655
1656
1657 //
1658 // Internal support routine
1659 //
1660
1661 VOID
FatQuickVerifyVcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)1662 FatQuickVerifyVcb (
1663 IN PIRP_CONTEXT IrpContext,
1664 IN PVCB Vcb
1665 )
1666
1667 /*++
1668
1669 Routine Description:
1670
1671 This routines just checks the verify bit in the real device and the
1672 Vcb condition and raises an appropriate exception if so warented.
1673 It is called when verifying both Fcbs and Vcbs.
1674
1675 Arguments:
1676
1677 Vcb - Supplies the Vcb to check the condition of.
1678
1679 Return Value:
1680
1681 None.
1682
1683 --*/
1684
1685 {
1686 PAGED_CODE();
1687
1688 //
1689 // If the real device needs to be verified we'll set the
1690 // DeviceToVerify to be our real device and raise VerifyRequired.
1691 //
1692
1693 if (FlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
1694
1695 DebugTrace(0, Dbg, "The Vcb needs to be verified\n", 0);
1696
1697 IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
1698 Vcb->Vpb->RealDevice );
1699
1700 FatRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
1701 }
1702
1703 //
1704 // Based on the condition of the Vcb we'll either return to our
1705 // caller or raise an error condition
1706 //
1707
1708 switch (Vcb->VcbCondition) {
1709
1710 case VcbGood:
1711
1712 DebugTrace(0, Dbg, "The Vcb is good\n", 0);
1713
1714 //
1715 // Do a check here of an operation that would try to modify a
1716 // write protected media.
1717 //
1718
1719 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) &&
1720 ((IrpContext->MajorFunction == IRP_MJ_WRITE) ||
1721 (IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) ||
1722 (IrpContext->MajorFunction == IRP_MJ_SET_EA) ||
1723 (IrpContext->MajorFunction == IRP_MJ_FLUSH_BUFFERS) ||
1724 (IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION) ||
1725 (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
1726 IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST &&
1727 IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.FileSystemControl.FsControlCode ==
1728 FSCTL_MARK_VOLUME_DIRTY))) {
1729
1730 //
1731 // Set the real device for the pop-up info, and set the verify
1732 // bit in the device object, so that we will force a verify
1733 // in case the user put the correct media back in.
1734 //
1735
1736
1737 IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
1738 Vcb->Vpb->RealDevice );
1739
1740 FatMarkDevForVerifyIfVcbMounted(Vcb);
1741
1742 FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
1743 }
1744
1745 break;
1746
1747 case VcbNotMounted:
1748
1749 DebugTrace(0, Dbg, "The Vcb is not mounted\n", 0);
1750
1751 //
1752 // Set the real device for the pop-up info, and set the verify
1753 // bit in the device object, so that we will force a verify
1754 // in case the user put the correct media back in.
1755 //
1756
1757 IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
1758 Vcb->Vpb->RealDevice );
1759
1760 FatRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
1761
1762 break;
1763
1764 case VcbBad:
1765
1766 DebugTrace(0, Dbg, "The Vcb is bad\n", 0);
1767
1768 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
1769
1770 FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED );
1771
1772 } else {
1773
1774 FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
1775 }
1776 break;
1777
1778 default:
1779
1780 DebugDump("Invalid VcbCondition\n", 0, Vcb);
1781 #ifdef _MSC_VER
1782 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1783 #endif
1784 FatBugCheck( Vcb->VcbCondition, 0, 0 );
1785 }
1786 }
1787
_Requires_lock_held_(_Global_critical_region_)1788 _Requires_lock_held_(_Global_critical_region_)
1789 NTSTATUS
1790 FatPerformVerify (
1791 _In_ PIRP_CONTEXT IrpContext,
1792 _In_ PIRP Irp,
1793 _In_ PDEVICE_OBJECT Device
1794 )
1795
1796 /*++
1797
1798 Routine Description:
1799
1800 This routines performs an IoVerifyVolume operation and takes the
1801 appropriate action. After the Verify is complete the originating
1802 Irp is sent off to an Ex Worker Thread. This routine is called
1803 from the exception handler.
1804
1805 Arguments:
1806
1807 Irp - The irp to send off after all is well and done.
1808
1809 Device - The real device needing verification.
1810
1811 Return Value:
1812
1813 None.
1814
1815 --*/
1816
1817 {
1818 PVCB Vcb;
1819 NTSTATUS Status = STATUS_SUCCESS;
1820 PIO_STACK_LOCATION IrpSp;
1821 PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
1822 BOOLEAN AllowRawMount = FALSE;
1823 BOOLEAN VcbDeleted = FALSE;
1824
1825 PAGED_CODE();
1826
1827 //
1828 // Check if this Irp has a status of Verify required and if it does
1829 // then call the I/O system to do a verify.
1830 //
1831 // Skip the IoVerifyVolume if this is a mount or verify request
1832 // itself. Trying a recursive mount will cause a deadlock with
1833 // the DeviceObject->DeviceLock.
1834 //
1835
1836 if ( (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
1837 ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) ||
1838 (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME)) ) {
1839
1840 return FatFsdPostRequest( IrpContext, Irp );
1841 }
1842
1843 DebugTrace(0, Dbg, "Verify Required, DeviceObject = %p\n", Device);
1844
1845 //
1846 // Extract a pointer to the Vcb from the VolumeDeviceObject.
1847 // Note that since we have specifically excluded mount,
1848 // requests, we know that IrpSp->DeviceObject is indeed a
1849 // volume device object.
1850 //
1851
1852 IrpSp = IoGetCurrentIrpStackLocation(Irp);
1853
1854 Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject,
1855 VOLUME_DEVICE_OBJECT,
1856 DeviceObject )->Vcb;
1857
1858 //
1859 // Check if the volume still thinks it needs to be verified,
1860 // if it doesn't then we can skip doing a verify because someone
1861 // else beat us to it.
1862 //
1863
1864 _SEH2_TRY {
1865
1866 //
1867 // We will allow Raw to mount this volume if we were doing a
1868 // a DASD open.
1869 //
1870
1871 if ( (IrpContext->MajorFunction == IRP_MJ_CREATE) &&
1872 (IrpSp->FileObject->FileName.Length == 0) &&
1873 (IrpSp->FileObject->RelatedFileObject == NULL) ) {
1874
1875 AllowRawMount = TRUE;
1876 }
1877
1878 //
1879 // Send down the verify. This could be going to a different
1880 // filesystem.
1881 //
1882
1883 Status = IoVerifyVolume( Device, AllowRawMount );
1884
1885 //
1886 // If the verify operation completed it will return
1887 // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly.
1888 //
1889 // If FatVerifyVolume encountered an error during
1890 // processing, it will return that error. If we got
1891 // STATUS_WRONG_VOLUME from the verfy, and our volume
1892 // is now mounted, commute the status to STATUS_SUCCESS.
1893 //
1894 // Acquire the Vcb so we're working with a stable Vcb condition.
1895 //
1896
1897 FatAcquireSharedVcb(IrpContext, Vcb);
1898
1899 if ( (Status == STATUS_WRONG_VOLUME) &&
1900 (Vcb->VcbCondition == VcbGood) ) {
1901
1902 Status = STATUS_SUCCESS;
1903 }
1904 else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbGood)) {
1905
1906 Status = STATUS_WRONG_VOLUME;
1907 }
1908
1909 //
1910 // Do a quick unprotected check here. The routine will do
1911 // a safe check. After here we can release the resource.
1912 // Note that if the volume really went away, we will be taking
1913 // the Reparse path.
1914 //
1915
1916 if ((VcbGood != Vcb->VcbCondition) &&
1917 (0 == Vcb->OpenFileCount) ) {
1918
1919 FatReleaseVcb( IrpContext, Vcb);
1920
1921 #ifdef _MSC_VER
1922 #pragma prefast( push )
1923 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1924 #pragma prefast( disable: 28193 )
1925 #endif
1926 FatAcquireExclusiveGlobal( IrpContext );
1927 #ifdef _MSC_VER
1928 #pragma prefast( pop )
1929 #endif
1930
1931 FatAcquireExclusiveVcb( IrpContext,
1932 Vcb );
1933
1934 VcbDeleted = FatCheckForDismount( IrpContext,
1935 Vcb,
1936 FALSE );
1937
1938 if (!VcbDeleted) {
1939
1940 FatReleaseVcb( IrpContext,
1941 Vcb );
1942 }
1943
1944 FatReleaseGlobal( IrpContext );
1945 }
1946 else {
1947
1948 FatReleaseVcb( IrpContext, Vcb);
1949 }
1950
1951 //
1952 // If the IopMount in IoVerifyVolume did something, and
1953 // this is an absolute open, force a reparse.
1954 //
1955
1956 if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
1957 (FileObject->RelatedFileObject == NULL) &&
1958 ((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) {
1959
1960 Irp->IoStatus.Information = IO_REMOUNT;
1961
1962 FatCompleteRequest( IrpContext, Irp, STATUS_REPARSE );
1963 Status = STATUS_REPARSE;
1964 Irp = NULL;
1965 }
1966
1967 if ( (Irp != NULL) && !NT_SUCCESS(Status) ) {
1968
1969 //
1970 // Fill in the device object if required.
1971 //
1972
1973 if ( IoIsErrorUserInduced( Status ) ) {
1974
1975 IoSetHardErrorOrVerifyDevice( Irp, Device );
1976 }
1977
1978 NT_ASSERT( STATUS_VERIFY_REQUIRED != Status);
1979
1980 FatNormalizeAndRaiseStatus( IrpContext, Status );
1981 }
1982
1983 //
1984 // If there is still an Irp, send it off to an Ex Worker thread.
1985 //
1986
1987 if ( Irp != NULL ) {
1988
1989 Status = FatFsdPostRequest( IrpContext, Irp );
1990 }
1991
1992 }
1993 _SEH2_EXCEPT (FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
1994
1995 //
1996 // We had some trouble trying to perform the verify or raised
1997 // an error ourselves. So we'll abort the I/O request with
1998 // the error status that we get back from the execption code.
1999 //
2000
2001 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
2002 } _SEH2_END;
2003
2004 return Status;
2005 }
2006
2007 //
2008 // Local support routine
2009 //
2010
2011 NTSTATUS
2012 NTAPI
2013 FatMarkVolumeCompletionRoutine(
2014 _In_ PDEVICE_OBJECT DeviceObject,
2015 _In_ PIRP Irp,
2016 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
2017 )
2018
2019 {
2020 //
2021 // Set the event so that our call will wake up.
2022 //
2023
2024 KeSetEvent( (PKEVENT)Contxt, 0, FALSE );
2025
2026 UNREFERENCED_PARAMETER( DeviceObject );
2027 UNREFERENCED_PARAMETER( Irp );
2028
2029 return STATUS_MORE_PROCESSING_REQUIRED;
2030 }
2031
2032
2033