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