mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
2032 lines
50 KiB
C
2032 lines
50 KiB
C
/*++
|
|
|
|
Copyright (c) 1989-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
VerfySup.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Fat Verify volume and fcb/dcb support
|
|
routines
|
|
|
|
|
|
--*/
|
|
|
|
#include "fatprocs.h"
|
|
|
|
//
|
|
// The Bug check file id for this module
|
|
//
|
|
|
|
#define BugCheckFileId (FAT_BUG_CHECK_VERFYSUP)
|
|
|
|
//
|
|
// The Debug trace level for this module
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_VERFYSUP)
|
|
|
|
//
|
|
// Local procedure prototypes
|
|
//
|
|
|
|
VOID
|
|
FatResetFcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB Fcb
|
|
);
|
|
|
|
BOOLEAN
|
|
FatMatchFileSize (
|
|
__in PIRP_CONTEXT IrpContext,
|
|
__in PDIRENT Dirent,
|
|
__in PFCB Fcb
|
|
);
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
FatDetermineAndMarkFcbCondition (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB Fcb
|
|
);
|
|
|
|
WORKER_THREAD_ROUTINE FatDeferredCleanVolume;
|
|
|
|
VOID
|
|
NTAPI
|
|
FatDeferredCleanVolume (
|
|
_In_ PVOID Parameter
|
|
);
|
|
|
|
IO_COMPLETION_ROUTINE FatMarkVolumeCompletionRoutine;
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
FatMarkVolumeCompletionRoutine(
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_In_ PIRP Irp,
|
|
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FatCheckDirtyBit)
|
|
#pragma alloc_text(PAGE, FatVerifyOperationIsLegal)
|
|
#pragma alloc_text(PAGE, FatDeferredCleanVolume)
|
|
#pragma alloc_text(PAGE, FatMatchFileSize)
|
|
#pragma alloc_text(PAGE, FatDetermineAndMarkFcbCondition)
|
|
#pragma alloc_text(PAGE, FatQuickVerifyVcb)
|
|
#pragma alloc_text(PAGE, FatPerformVerify)
|
|
#pragma alloc_text(PAGE, FatMarkFcbCondition)
|
|
#pragma alloc_text(PAGE, FatResetFcb)
|
|
#pragma alloc_text(PAGE, FatVerifyVcb)
|
|
#pragma alloc_text(PAGE, FatVerifyFcb)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
FatMarkFcbCondition (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB Fcb,
|
|
IN FCB_CONDITION FcbCondition,
|
|
IN BOOLEAN Recursive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines marks the entire Fcb/Dcb structure from Fcb down with
|
|
FcbCondition.
|
|
|
|
Arguments:
|
|
|
|
Fcb - Supplies the Fcb/Dcb being marked
|
|
|
|
FcbCondition - Supplies the setting to use for the Fcb Condition
|
|
|
|
Recursive - Specifies whether this condition should be applied to
|
|
all children (see the case where we are invalidating a live volume
|
|
for a case where this is now desireable).
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "FatMarkFcbCondition, Fcb = %p\n", Fcb );
|
|
|
|
//
|
|
// If we are marking this Fcb something other than Good, we will need
|
|
// to have the Vcb exclusive.
|
|
//
|
|
|
|
NT_ASSERT( FcbCondition != FcbNeedsToBeVerified ? TRUE :
|
|
FatVcbAcquiredExclusive(IrpContext, Fcb->Vcb) );
|
|
|
|
//
|
|
// If this is a PagingFile it has to be good unless media underneath is
|
|
// removable. The "removable" check was added specifically for ReadyBoost,
|
|
// which opens its cache file on a removable device as a paging file and
|
|
// relies on the file system to validate its mapping information after a
|
|
// power transition.
|
|
//
|
|
|
|
if (!FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
|
|
FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
|
|
|
|
Fcb->FcbCondition = FcbGood;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update the condition of the Fcb.
|
|
//
|
|
|
|
Fcb->FcbCondition = FcbCondition;
|
|
|
|
DebugTrace(0, Dbg, "MarkFcb: %wZ\n", &Fcb->FullFileName);
|
|
|
|
//
|
|
// This FastIo flag is based on FcbCondition, so update it now. This only
|
|
// applies to regular FCBs, of course.
|
|
//
|
|
|
|
if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) {
|
|
|
|
Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
|
|
}
|
|
|
|
if (FcbCondition == FcbNeedsToBeVerified) {
|
|
FatResetFcb( IrpContext, Fcb );
|
|
}
|
|
|
|
//
|
|
// Now if we marked NeedsVerify or Bad a directory then we also need to
|
|
// go and mark all of our children with the same condition.
|
|
//
|
|
|
|
if ( ((FcbCondition == FcbNeedsToBeVerified) ||
|
|
(FcbCondition == FcbBad)) &&
|
|
Recursive &&
|
|
((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) ||
|
|
(Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) ) {
|
|
|
|
PFCB OriginalFcb = Fcb;
|
|
|
|
while ( (Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, OriginalFcb)) != NULL ) {
|
|
|
|
DebugTrace(0, Dbg, "MarkFcb: %wZ\n", &Fcb->FullFileName);
|
|
|
|
Fcb->FcbCondition = FcbCondition;
|
|
|
|
//
|
|
// We already know that FastIo is not possible since we are propagating
|
|
// a parent's bad/verify flag down the tree - IO to the children must
|
|
// take the long route for now.
|
|
//
|
|
|
|
Fcb->Header.IsFastIoPossible = FastIoIsNotPossible;
|
|
|
|
//
|
|
// Leave all the Fcbs in a condition to be verified.
|
|
//
|
|
|
|
if (FcbCondition == FcbNeedsToBeVerified) {
|
|
FatResetFcb( IrpContext, Fcb );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "FatMarkFcbCondition -> VOID\n", 0);
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
FatMarkDevForVerifyIfVcbMounted(
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the specified Vcb is currently mounted on
|
|
the device or not. If it is, it sets the verify flag on the device, if
|
|
not then the state is noted in the Vcb.
|
|
|
|
Arguments:
|
|
|
|
Vcb - This is the volume to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the device has been marked for verify here, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN Marked = FALSE;
|
|
KIRQL SavedIrql;
|
|
|
|
IoAcquireVpbSpinLock( &SavedIrql );
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( push )
|
|
#pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
|
|
#endif
|
|
|
|
if (Vcb->Vpb->RealDevice->Vpb == Vcb->Vpb) {
|
|
|
|
SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
|
|
Marked = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Flag this to avoid the VPB spinlock in future passes.
|
|
//
|
|
|
|
SetFlag( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE);
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( pop )
|
|
#endif
|
|
|
|
IoReleaseVpbSpinLock( SavedIrql );
|
|
|
|
return Marked;
|
|
}
|
|
|
|
|
|
VOID
|
|
FatVerifyVcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines verifies that the Vcb still denotes a valid Volume
|
|
If the Vcb is bad it raises an error condition.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb being verified
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN DevMarkedForVerify;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "FatVerifyVcb, Vcb = %p\n", Vcb );
|
|
|
|
//
|
|
// If the verify volume flag in the device object is set
|
|
// this means the media has potentially changed.
|
|
//
|
|
// Note that we only force this ping for create operations.
|
|
// For others we take a sporting chance. If in the end we
|
|
// have to physically access the disk, the right thing will happen.
|
|
//
|
|
|
|
DevMarkedForVerify = BooleanFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
|
|
|
|
//
|
|
// We ALWAYS force CREATE requests on unmounted volumes through the
|
|
// verify path. These requests could have been in limbo between
|
|
// IoCheckMountedVpb and us, when a verify/mount took place and caused
|
|
// a completely different fs/volume to be mounted. In this case the
|
|
// checks above may not have caught the condition, since we may already
|
|
// have verified (wrong volume) and decided that we have nothing to do.
|
|
// We want the requests to be re routed to the currently mounted volume,
|
|
// since they were directed at the 'drive', not our volume. So we take
|
|
// the verify path for synchronisation, and the request will eventually
|
|
// be bounced back to IO with STATUS_REPARSE by our verify handler.
|
|
//
|
|
|
|
if (!DevMarkedForVerify &&
|
|
(IrpContext->MajorFunction == IRP_MJ_CREATE) &&
|
|
(IrpContext->OriginatingIrp != NULL)) {
|
|
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp);
|
|
|
|
if ((IrpSp->FileObject->RelatedFileObject == NULL) &&
|
|
(Vcb->VcbCondition == VcbNotMounted)) {
|
|
|
|
DevMarkedForVerify = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Raise any error condition otherwise.
|
|
//
|
|
|
|
if (DevMarkedForVerify) {
|
|
|
|
DebugTrace(0, Dbg, "The Vcb needs to be verified\n", 0);
|
|
|
|
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
|
|
Vcb->Vpb->RealDevice );
|
|
|
|
FatNormalizeAndRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
|
|
}
|
|
|
|
//
|
|
// Check the operation is legal for current Vcb state.
|
|
//
|
|
|
|
FatQuickVerifyVcb( IrpContext, Vcb );
|
|
|
|
DebugTrace(-1, Dbg, "FatVerifyVcb -> VOID\n", 0);
|
|
}
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
FatVerifyFcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB Fcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines verifies that the Fcb still denotes the same file.
|
|
If the Fcb is bad it raises a error condition.
|
|
|
|
Arguments:
|
|
|
|
Fcb - Supplies the Fcb being verified
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB CurrentFcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "FatVerifyFcb, Vcb = %p\n", Fcb );
|
|
|
|
//
|
|
// Always refuse operations on dismounted volumes.
|
|
//
|
|
|
|
if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED );
|
|
}
|
|
|
|
//
|
|
// If this is the Fcb of a deleted dirent or our parent is deleted,
|
|
// no-op this call with the hope that the caller will do the right thing.
|
|
// The only caller we really have to worry about is the AdvanceOnly
|
|
// callback for setting valid data length from Cc, this will happen after
|
|
// cleanup (and file deletion), just before the SCM is ripped down.
|
|
//
|
|
|
|
if (IsFileDeleted( IrpContext, Fcb ) ||
|
|
((NodeType(Fcb) != FAT_NTC_ROOT_DCB) &&
|
|
IsFileDeleted( IrpContext, Fcb->ParentDcb ))) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we are not in the process of doing a verify,
|
|
// first do a quick spot check on the Vcb.
|
|
//
|
|
|
|
if ( Fcb->Vcb->VerifyThread != KeGetCurrentThread() ) {
|
|
|
|
FatQuickVerifyVcb( IrpContext, Fcb->Vcb );
|
|
}
|
|
|
|
//
|
|
// Now based on the condition of the Fcb we'll either return
|
|
// immediately to the caller, raise a condition, or do some work
|
|
// to verify the Fcb.
|
|
//
|
|
|
|
switch (Fcb->FcbCondition) {
|
|
|
|
case FcbGood:
|
|
|
|
DebugTrace(0, Dbg, "The Fcb is good\n", 0);
|
|
break;
|
|
|
|
case FcbBad:
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
|
break;
|
|
|
|
case FcbNeedsToBeVerified:
|
|
|
|
//
|
|
// We loop here checking our ancestors until we hit an Fcb which
|
|
// is either good or bad.
|
|
//
|
|
|
|
CurrentFcb = Fcb;
|
|
|
|
while (CurrentFcb->FcbCondition == FcbNeedsToBeVerified) {
|
|
|
|
FatDetermineAndMarkFcbCondition(IrpContext, CurrentFcb);
|
|
|
|
//
|
|
// If this Fcb didn't make it, or it was the Root Dcb, exit
|
|
// the loop now, else continue with out parent.
|
|
//
|
|
|
|
if ( (CurrentFcb->FcbCondition != FcbGood) ||
|
|
(NodeType(CurrentFcb) == FAT_NTC_ROOT_DCB) ) {
|
|
|
|
break;
|
|
}
|
|
|
|
CurrentFcb = CurrentFcb->ParentDcb;
|
|
}
|
|
|
|
//
|
|
// Now we can just look at ourselves to see how we did.
|
|
//
|
|
|
|
if (Fcb->FcbCondition != FcbGood) {
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DebugDump("Invalid FcbCondition\n", 0, Fcb);
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
|
|
#endif
|
|
FatBugCheck( Fcb->FcbCondition, 0, 0 );
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "FatVerifyFcb -> VOID\n", 0);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
FatDeferredCleanVolume (
|
|
_In_ PVOID Parameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine that performs the actual FatMarkVolumeClean call.
|
|
It assures that the target volume still exists as there ia a race
|
|
condition between queueing the ExWorker item and volumes going away.
|
|
|
|
Arguments:
|
|
|
|
Parameter - Points to a clean volume packet that was allocated from pool
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
|
|
PLIST_ENTRY Links;
|
|
PVCB Vcb;
|
|
IRP_CONTEXT IrpContext;
|
|
BOOLEAN VcbExists = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "FatDeferredCleanVolume\n", 0);
|
|
|
|
Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter;
|
|
|
|
Vcb = Packet->Vcb;
|
|
|
|
//
|
|
// Make us appear as a top level FSP request so that we will
|
|
// receive any errors from the operation.
|
|
//
|
|
|
|
IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
|
|
|
|
//
|
|
// Dummy up and Irp Context so we can call our worker routines
|
|
//
|
|
|
|
RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT));
|
|
|
|
SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
|
|
//
|
|
// Acquire shared access to the global lock and make sure this volume
|
|
// still exists.
|
|
//
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( push )
|
|
#pragma prefast( disable: 28193, "this will always wait" )
|
|
#endif
|
|
FatAcquireSharedGlobal( &IrpContext );
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( pop )
|
|
#endif
|
|
|
|
for (Links = FatData.VcbQueue.Flink;
|
|
Links != &FatData.VcbQueue;
|
|
Links = Links->Flink) {
|
|
|
|
PVCB ExistingVcb;
|
|
|
|
ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
|
|
|
|
if ( Vcb == ExistingVcb ) {
|
|
|
|
VcbExists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the vcb is good then mark it clean. Ignore any problems.
|
|
//
|
|
|
|
if ( VcbExists &&
|
|
(Vcb->VcbCondition == VcbGood) &&
|
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) ) {
|
|
|
|
_SEH2_TRY {
|
|
|
|
if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
|
|
|
|
FatMarkVolume( &IrpContext, Vcb, VolumeClean );
|
|
}
|
|
|
|
//
|
|
// Check for a pathological race condition, and fix it.
|
|
//
|
|
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) {
|
|
|
|
FatMarkVolume( &IrpContext, Vcb, VolumeDirty );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Unlock the volume if it is removable.
|
|
//
|
|
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
|
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
|
|
|
|
FatToggleMediaEjectDisable( &IrpContext, Vcb, FALSE );
|
|
}
|
|
}
|
|
|
|
} _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|
|
|
NOTHING;
|
|
} _SEH2_END;
|
|
}
|
|
|
|
//
|
|
// Release the global resource, unpin and repinned Bcbs and return.
|
|
//
|
|
|
|
FatReleaseGlobal( &IrpContext );
|
|
|
|
_SEH2_TRY {
|
|
|
|
FatUnpinRepinnedBcbs( &IrpContext );
|
|
|
|
} _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|
|
|
NOTHING;
|
|
} _SEH2_END;
|
|
|
|
IoSetTopLevelIrp( NULL );
|
|
|
|
//
|
|
// and finally free the packet.
|
|
//
|
|
|
|
ExFreePool( Packet );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
FatCleanVolumeDpc (
|
|
_In_ PKDPC Dpc,
|
|
_In_opt_ PVOID DeferredContext,
|
|
_In_opt_ PVOID SystemArgument1,
|
|
_In_opt_ PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is dispatched 5 seconds after the last disk structure was
|
|
modified in a specific volume, and exqueues an execuative worker thread
|
|
to perform the actual task of marking the volume dirty.
|
|
|
|
Arguments:
|
|
|
|
DefferedContext - Contains the Vcb to process.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVCB Vcb;
|
|
PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
|
|
|
|
UNREFERENCED_PARAMETER( SystemArgument1 );
|
|
UNREFERENCED_PARAMETER( SystemArgument2 );
|
|
UNREFERENCED_PARAMETER( Dpc );
|
|
|
|
Vcb = (PVCB)DeferredContext;
|
|
|
|
|
|
//
|
|
// If there is still dirty data (highly unlikely), set the timer for a
|
|
// second in the future.
|
|
//
|
|
|
|
if (CcIsThereDirtyData(Vcb->Vpb)) {
|
|
|
|
LARGE_INTEGER TwoSecondsFromNow;
|
|
|
|
TwoSecondsFromNow.QuadPart = (LONG)-2*1000*1000*10;
|
|
|
|
KeSetTimer( &Vcb->CleanVolumeTimer,
|
|
TwoSecondsFromNow,
|
|
&Vcb->CleanVolumeDpc );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we couldn't get pool, oh well....
|
|
//
|
|
|
|
Packet = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET), ' taF');
|
|
|
|
if ( Packet ) {
|
|
|
|
Packet->Vcb = Vcb;
|
|
Packet->Irp = NULL;
|
|
|
|
//
|
|
// Clear the dirty flag now since we cannot synchronize after this point.
|
|
//
|
|
|
|
ClearFlag( Packet->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
|
|
|
|
ExInitializeWorkItem( &Packet->Item, &FatDeferredCleanVolume, Packet );
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress:28159, "prefast indicates this is an obsolete API, but it is ok for fastfat to keep using it" )
|
|
#endif
|
|
ExQueueWorkItem( &Packet->Item, CriticalWorkQueue );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
FatMarkVolume (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN FAT_VOLUME_STATE VolumeState
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine moves the physically marked volume state between the clean
|
|
and dirty states. For compatibility with Win9x, we manipulate both the
|
|
historical DOS (on==clean in index 1 of the FAT) and NT (on==dirty in
|
|
the CurrentHead field of the BPB) dirty bits.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb being modified
|
|
|
|
VolumeState - Supplies the state the volume is transitioning to
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHAR Sector;
|
|
PBCB Bcb = NULL;
|
|
KEVENT Event;
|
|
PIRP Irp = NULL;
|
|
NTSTATUS Status;
|
|
BOOLEAN FsInfoUpdate = FALSE;
|
|
ULONG FsInfoOffset = 0;
|
|
ULONG ThisPass;
|
|
LARGE_INTEGER Offset;
|
|
BOOLEAN abort = FALSE;
|
|
|
|
DebugTrace(+1, Dbg, "FatMarkVolume, Vcb = %p\n", Vcb);
|
|
|
|
//
|
|
// We had best not be trying to scribble dirty/clean bits if the
|
|
// volume is write protected. The responsibility lies with the
|
|
// callers to make sure that operations that could cause a state
|
|
// change cannot happen. There are a few, though, that show it
|
|
// just doesn't make sense to force everyone to do the dinky
|
|
// check.
|
|
//
|
|
|
|
//
|
|
// If we were called for FAT12 or readonly media, return immediately.
|
|
//
|
|
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) ||
|
|
FatIsFat12( Vcb )) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We have two possible additional tasks to do to mark a volume
|
|
//
|
|
// Pass 0) Flip the dirty bit in the Bpb
|
|
// Pass 1) Rewrite the FsInfo sector for FAT32 if needed
|
|
//
|
|
// In most cases we can collapse these two either because the volume
|
|
// is either not FAT32 or the FsInfo sector is adjacent to the boot sector.
|
|
//
|
|
|
|
for (ThisPass = 0; ThisPass < 2; ThisPass++) {
|
|
|
|
//
|
|
// If this volume is being dirtied, or isn't FAT32, or if it is and
|
|
// we were able to perform the fast update, or the bpb lied to us
|
|
// about where the FsInfo went, we're done - no FsInfo to update in
|
|
// a seperate write.
|
|
//
|
|
|
|
if (ThisPass == 1 && (!FatIsFat32( Vcb ) ||
|
|
VolumeState != VolumeClean ||
|
|
FsInfoUpdate ||
|
|
Vcb->Bpb.FsInfoSector == 0)) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Bail if we get an IO error.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
ULONG PinLength;
|
|
ULONG WriteLength;
|
|
|
|
//
|
|
// If the FAT table is 12-bit then our strategy is to pin the entire
|
|
// thing when any of it is modified. Here we're going to pin the
|
|
// first page, so in the 12-bit case we also want to pin the rest
|
|
// of the FAT table.
|
|
//
|
|
|
|
Offset.QuadPart = 0;
|
|
|
|
if (Vcb->AllocationSupport.FatIndexBitSize == 12) {
|
|
|
|
//
|
|
// But we only write back the first sector.
|
|
//
|
|
|
|
PinLength = FatReservedBytes(&Vcb->Bpb) + FatBytesPerFat(&Vcb->Bpb);
|
|
WriteLength = Vcb->Bpb.BytesPerSector;
|
|
|
|
} else {
|
|
|
|
WriteLength = PinLength = Vcb->Bpb.BytesPerSector;
|
|
|
|
//
|
|
// If this is a FAT32 volume going into the clean state,
|
|
// see about doing the FsInfo sector.
|
|
//
|
|
|
|
if (FatIsFat32( Vcb ) && VolumeState == VolumeClean) {
|
|
|
|
//
|
|
// If the FsInfo sector immediately follows the boot sector,
|
|
// we can do this in a single operation by rewriting both
|
|
// sectors at once.
|
|
//
|
|
|
|
if (Vcb->Bpb.FsInfoSector == 1) {
|
|
|
|
NT_ASSERT( ThisPass == 0 );
|
|
|
|
FsInfoUpdate = TRUE;
|
|
FsInfoOffset = Vcb->Bpb.BytesPerSector;
|
|
WriteLength = PinLength = Vcb->Bpb.BytesPerSector * 2;
|
|
|
|
} else if (ThisPass == 1) {
|
|
|
|
//
|
|
// We are doing an explicit write to the FsInfo sector.
|
|
//
|
|
|
|
FsInfoUpdate = TRUE;
|
|
FsInfoOffset = 0;
|
|
|
|
Offset.QuadPart = Vcb->Bpb.BytesPerSector * Vcb->Bpb.FsInfoSector;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call Cc directly here so that we can avoid overhead and push this
|
|
// right down to the disk.
|
|
//
|
|
|
|
CcPinRead( Vcb->VirtualVolumeFile,
|
|
&Offset,
|
|
PinLength,
|
|
TRUE,
|
|
&Bcb,
|
|
(PVOID *)&Sector );
|
|
|
|
DbgDoit( IrpContext->PinCount += 1 )
|
|
|
|
//
|
|
// Set the Bpb on Pass 0 always
|
|
//
|
|
|
|
if (ThisPass == 0) {
|
|
|
|
PCHAR CurrentHead;
|
|
|
|
//
|
|
// Before we do anything, doublecheck that this still looks like a
|
|
// FAT bootsector. If it doesn't, something remarkable happened
|
|
// and we should avoid touching the volume.
|
|
//
|
|
// THIS IS TEMPORARY (but may last a while)
|
|
//
|
|
|
|
if (!FatIsBootSectorFat( (PPACKED_BOOT_SECTOR) Sector )) {
|
|
abort = TRUE;
|
|
_SEH2_LEAVE;
|
|
}
|
|
|
|
if (FatIsFat32( Vcb )) {
|
|
|
|
CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR_EX) Sector)->CurrentHead;
|
|
|
|
} else {
|
|
|
|
CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR) Sector)->CurrentHead;
|
|
}
|
|
|
|
if (VolumeState == VolumeClean) {
|
|
|
|
ClearFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY );
|
|
|
|
} else {
|
|
|
|
SetFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY );
|
|
|
|
//
|
|
// In addition, if this request received an error that may indicate
|
|
// media corruption, have autochk perform a surface test.
|
|
//
|
|
|
|
if ( VolumeState == VolumeDirtyWithSurfaceTest ) {
|
|
|
|
SetFlag( *CurrentHead, FAT_BOOT_SECTOR_TEST_SURFACE );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the FsInfo as appropriate.
|
|
//
|
|
|
|
if (FsInfoUpdate) {
|
|
|
|
PFSINFO_SECTOR FsInfoSector = (PFSINFO_SECTOR) ((PCHAR)Sector + FsInfoOffset);
|
|
|
|
//
|
|
// We just rewrite all of the spec'd fields. Note that we don't
|
|
// care to synchronize with the allocation package - this will be
|
|
// quickly taken care of by a re-dirtying of the volume if a change
|
|
// is racing with us. Remember that this is all a compatibility
|
|
// deference for Win9x FAT32 - NT will never look at this information.
|
|
//
|
|
|
|
FsInfoSector->SectorBeginSignature = FSINFO_SECTOR_BEGIN_SIGNATURE;
|
|
FsInfoSector->FsInfoSignature = FSINFO_SIGNATURE;
|
|
FsInfoSector->FreeClusterCount = Vcb->AllocationSupport.NumberOfFreeClusters;
|
|
FsInfoSector->NextFreeCluster = Vcb->ClusterHint;
|
|
FsInfoSector->SectorEndSignature = FSINFO_SECTOR_END_SIGNATURE;
|
|
}
|
|
|
|
//
|
|
// Initialize the event we're going to use
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// Build the irp for the operation and also set the override flag.
|
|
// Note that we may be at APC level, so do this asyncrhonously and
|
|
// use an event for synchronization as normal request completion
|
|
// cannot occur at APC level.
|
|
//
|
|
|
|
Irp = IoBuildAsynchronousFsdRequest( IRP_MJ_WRITE,
|
|
Vcb->TargetDeviceObject,
|
|
(PVOID)Sector,
|
|
WriteLength,
|
|
&Offset,
|
|
NULL );
|
|
|
|
if ( Irp == NULL ) {
|
|
|
|
try_return(NOTHING);
|
|
}
|
|
|
|
//
|
|
// Make this operation write-through. It never hurts to try to be
|
|
// safer about this, even though we aren't logged.
|
|
//
|
|
|
|
SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_WRITE_THROUGH );
|
|
|
|
//
|
|
// Set up the completion routine
|
|
//
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
FatMarkVolumeCompletionRoutine,
|
|
&Event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
//
|
|
// Call the device to do the write and wait for it to finish.
|
|
// Igmore any return status.
|
|
//
|
|
|
|
Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
(VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
} _SEH2_FINALLY {
|
|
|
|
//
|
|
// Clean up the Irp and Mdl
|
|
//
|
|
|
|
|
|
if (Irp) {
|
|
|
|
//
|
|
// If there is an MDL (or MDLs) associated with this I/O
|
|
// request, Free it (them) here. This is accomplished by
|
|
// walking the MDL list hanging off of the IRP and deallocating
|
|
// each MDL encountered.
|
|
//
|
|
|
|
while (Irp->MdlAddress != NULL) {
|
|
|
|
PMDL NextMdl;
|
|
|
|
NextMdl = Irp->MdlAddress->Next;
|
|
|
|
MmUnlockPages( Irp->MdlAddress );
|
|
|
|
IoFreeMdl( Irp->MdlAddress );
|
|
|
|
Irp->MdlAddress = NextMdl;
|
|
}
|
|
|
|
IoFreeIrp( Irp );
|
|
}
|
|
|
|
if (Bcb != NULL) {
|
|
|
|
FatUnpinBcb( IrpContext, Bcb );
|
|
}
|
|
} _SEH2_END;
|
|
}
|
|
|
|
if (!abort) {
|
|
|
|
//
|
|
// Flip the dirty bit in the FAT
|
|
//
|
|
|
|
if (VolumeState == VolumeDirty) {
|
|
|
|
FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_DIRTY_VOLUME);
|
|
|
|
} else {
|
|
|
|
FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_CLEAN_VOLUME);
|
|
}
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "FatMarkVolume -> VOID\n", 0);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
FatFspMarkVolumeDirtyWithRecover(
|
|
PVOID Parameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine that performs the actual FatMarkVolume Dirty call
|
|
on a paging file Io that encounters a media error. It is responsible
|
|
for completing the PagingIo Irp as soon as this is done.
|
|
|
|
Note: this routine (and thus FatMarkVolume()) must be resident as
|
|
the paging file might be damaged at this point.
|
|
|
|
Arguments:
|
|
|
|
Parameter - Points to a dirty volume packet that was allocated from pool
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
|
|
PVCB Vcb;
|
|
IRP_CONTEXT IrpContext;
|
|
PIRP Irp;
|
|
|
|
DebugTrace(+1, Dbg, "FatFspMarkVolumeDirtyWithRecover\n", 0);
|
|
|
|
Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter;
|
|
|
|
Vcb = Packet->Vcb;
|
|
Irp = Packet->Irp;
|
|
|
|
//
|
|
// Dummy up the IrpContext so we can call our worker routines
|
|
//
|
|
|
|
RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT));
|
|
|
|
SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
IrpContext.OriginatingIrp = Irp;
|
|
|
|
//
|
|
// Make us appear as a top level FSP request so that we will
|
|
// receive any errors from the operation.
|
|
//
|
|
|
|
IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
|
|
|
|
//
|
|
// Try to write out the dirty bit. If something goes wrong, we
|
|
// tried.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
|
|
|
|
FatMarkVolume( &IrpContext, Vcb, VolumeDirtyWithSurfaceTest );
|
|
|
|
} _SEH2_EXCEPT(FatExceptionFilter( &IrpContext, _SEH2_GetExceptionInformation() )) {
|
|
|
|
NOTHING;
|
|
} _SEH2_END;
|
|
|
|
IoSetTopLevelIrp( NULL );
|
|
|
|
//
|
|
// Now complete the originating Irp or set the synchronous event.
|
|
//
|
|
|
|
if (Packet->Event) {
|
|
KeSetEvent( Packet->Event, 0, FALSE );
|
|
} else {
|
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "FatFspMarkVolumeDirtyWithRecover -> VOID\n", 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
FatCheckDirtyBit (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks at the volume dirty bit, and depending on the state of
|
|
VCB_STATE_FLAG_MOUNTED_DIRTY, the appropriate action is taken.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb being queried.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Dirty;
|
|
|
|
PPACKED_BOOT_SECTOR BootSector;
|
|
PBCB BootSectorBcb;
|
|
|
|
UNICODE_STRING VolumeLabel;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Look in the boot sector
|
|
//
|
|
|
|
FatReadVolumeFile( IrpContext,
|
|
Vcb,
|
|
0,
|
|
sizeof(PACKED_BOOT_SECTOR),
|
|
&BootSectorBcb,
|
|
(PVOID *)&BootSector );
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// Check if the magic bit is set
|
|
//
|
|
|
|
if (IsBpbFat32(&BootSector->PackedBpb)) {
|
|
Dirty = BooleanFlagOn( ((PPACKED_BOOT_SECTOR_EX)BootSector)->CurrentHead,
|
|
FAT_BOOT_SECTOR_DIRTY );
|
|
} else {
|
|
Dirty = BooleanFlagOn( BootSector->CurrentHead, FAT_BOOT_SECTOR_DIRTY );
|
|
}
|
|
|
|
//
|
|
// Setup the VolumeLabel string
|
|
//
|
|
|
|
VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength;
|
|
VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
|
|
VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0];
|
|
|
|
if ( Dirty ) {
|
|
|
|
//
|
|
// Do not trigger the mounted dirty bit if this is a verify
|
|
// and the volume is a boot or paging device. We know that
|
|
// a boot or paging device cannot leave the system, and thus
|
|
// that on its mount we will have figured this out correctly.
|
|
//
|
|
// This logic is a reasonable change. Why?
|
|
// 'cause setup cracked a non-exclusive DASD handle near the
|
|
// end of setup, wrote some data, closed the handle and we
|
|
// set the verify bit ... came back around and saw that other
|
|
// arbitrary activity had left the volume in a temporarily dirty
|
|
// state.
|
|
//
|
|
// Of course, the real problem is that we don't have a journal.
|
|
//
|
|
|
|
if (!(IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
|
|
IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME &&
|
|
FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE))) {
|
|
|
|
KdPrintEx((DPFLTR_FASTFAT_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"FASTFAT: WARNING! Mounting Dirty Volume %Z\n",
|
|
&VolumeLabel));
|
|
|
|
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
|
|
}
|
|
|
|
} else {
|
|
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
|
|
|
|
KdPrintEx((DPFLTR_FASTFAT_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"FASTFAT: Volume %Z has been cleaned.\n",
|
|
&VolumeLabel));
|
|
|
|
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
|
|
|
|
} else {
|
|
|
|
(VOID)FsRtlBalanceReads( Vcb->TargetDeviceObject );
|
|
}
|
|
}
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
FatUnpinBcb( IrpContext, BootSectorBcb );
|
|
} _SEH2_END;
|
|
}
|
|
|
|
|
|
VOID
|
|
FatVerifyOperationIsLegal (
|
|
IN PIRP_CONTEXT IrpContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines is the requested operation should be allowed to
|
|
continue. It either returns to the user if the request is Okay, or
|
|
raises an appropriate status.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to check
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP Irp;
|
|
PFILE_OBJECT FileObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
Irp = IrpContext->OriginatingIrp;
|
|
|
|
//
|
|
// If the Irp is not present, then we got here via close.
|
|
//
|
|
//
|
|
|
|
if ( Irp == NULL ) {
|
|
|
|
return;
|
|
}
|
|
|
|
FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
|
|
|
|
//
|
|
// If there is not a file object, we cannot continue.
|
|
//
|
|
|
|
if ( FileObject == NULL ) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the file object has already been cleaned up, and
|
|
//
|
|
// A) This request is a paging io read or write, or
|
|
// B) This request is a close operation, or
|
|
// C) This request is a set or query info call (for Lou)
|
|
// D) This is an MDL complete
|
|
//
|
|
// let it pass, otherwise return STATUS_FILE_CLOSED.
|
|
//
|
|
|
|
if ( FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE) ) {
|
|
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_CLOSE ) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
|
|
( ( (IrpSp->MajorFunction == IRP_MJ_READ) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_WRITE) ) &&
|
|
FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) {
|
|
|
|
NOTHING;
|
|
|
|
} else {
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
VOID
|
|
FatResetFcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB Fcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when an Fcb has been marked as needs to be verified.
|
|
|
|
It does the following tasks:
|
|
|
|
- Reset Mcb mapping information
|
|
- For directories, reset dirent hints
|
|
- Set allocation size to unknown
|
|
|
|
Arguments:
|
|
|
|
Fcb - Supplies the Fcb to reset
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LOGICAL IsRealPagingFile;
|
|
|
|
PAGED_CODE();
|
|
UNREFERENCED_PARAMETER( IrpContext );
|
|
|
|
//
|
|
// Don't do the two following operations for the Root Dcb
|
|
// of a non FAT32 volume or paging files. Paging files!?
|
|
// Yes, if someone diddles a volume we try to reverify all
|
|
// of the Fcbs just in case; however, there is no safe way
|
|
// to chuck and retrieve the mapping pair information for
|
|
// a real paging file. Lose it and die.
|
|
//
|
|
// An exception is made for ReadyBoost cache files, which
|
|
// are created as paging files on removable devices and
|
|
// require validation after a power transition.
|
|
//
|
|
|
|
if (!FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
|
|
FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
|
|
|
|
IsRealPagingFile = TRUE;
|
|
|
|
} else {
|
|
|
|
IsRealPagingFile = FALSE;
|
|
}
|
|
|
|
if ( (NodeType(Fcb) != FAT_NTC_ROOT_DCB ||
|
|
FatIsFat32( Fcb->Vcb )) &&
|
|
!IsRealPagingFile ) {
|
|
|
|
//
|
|
// Reset the mcb mapping.
|
|
//
|
|
|
|
FsRtlRemoveLargeMcbEntry( &Fcb->Mcb, 0, 0xFFFFFFFF );
|
|
|
|
//
|
|
// Reset the allocation size to 0 or unknown
|
|
//
|
|
|
|
if ( Fcb->FirstClusterOfFile == 0 ) {
|
|
|
|
Fcb->Header.AllocationSize.QuadPart = 0;
|
|
|
|
} else {
|
|
|
|
Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a directory, reset the hints.
|
|
//
|
|
|
|
if ( (NodeType(Fcb) == FAT_NTC_DCB) ||
|
|
(NodeType(Fcb) == FAT_NTC_ROOT_DCB) ) {
|
|
|
|
//
|
|
// Force a rescan of the directory
|
|
//
|
|
|
|
Fcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff;
|
|
Fcb->Specific.Dcb.DeletedDirentHint = 0xffffffff;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FatMatchFileSize (
|
|
__in PIRP_CONTEXT IrpContext,
|
|
__in PDIRENT Dirent,
|
|
__in PFCB Fcb
|
|
)
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(IrpContext);
|
|
|
|
if (NodeType(Fcb) != FAT_NTC_FCB) {
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
if (Fcb->Header.FileSize.LowPart != Dirent->FileSize) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
FatDetermineAndMarkFcbCondition (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB Fcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks a specific Fcb to see if it is different from what's
|
|
on the disk. The following things are checked:
|
|
|
|
- File Name
|
|
- File Size (if not directory)
|
|
- First Cluster Of File
|
|
- Dirent Attributes
|
|
|
|
Arguments:
|
|
|
|
Fcb - Supplies the Fcb to examine
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDIRENT Dirent;
|
|
PBCB DirentBcb;
|
|
ULONG FirstClusterOfFile;
|
|
|
|
OEM_STRING Name;
|
|
CHAR Buffer[16];
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If this is the Root Dcb, special case it. That is, we know
|
|
// by definition that it is good since it is fixed in the volume
|
|
// structure.
|
|
//
|
|
|
|
if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {
|
|
|
|
FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE );
|
|
|
|
return;
|
|
}
|
|
|
|
// The first thing we need to do to verify ourselves is
|
|
// locate the dirent on the disk.
|
|
//
|
|
|
|
FatGetDirentFromFcbOrDcb( IrpContext,
|
|
Fcb,
|
|
TRUE,
|
|
&Dirent,
|
|
&DirentBcb );
|
|
//
|
|
// If we couldn't get the dirent, this fcb must be bad (case of
|
|
// enclosing directory shrinking during the time it was ejected).
|
|
//
|
|
|
|
if (DirentBcb == NULL) {
|
|
|
|
FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We located the dirent for ourselves now make sure it
|
|
// is really ours by comparing the Name and FatFlags.
|
|
// Then for a file we also check the file size.
|
|
//
|
|
// Note that we have to unpin the Bcb before calling FatResetFcb
|
|
// in order to avoid a deadlock in CcUninitializeCacheMap.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
Name.MaximumLength = 16;
|
|
Name.Buffer = &Buffer[0];
|
|
|
|
Fat8dot3ToString( IrpContext, Dirent, FALSE, &Name );
|
|
|
|
//
|
|
// We need to calculate the first cluster 'cause FAT32 splits
|
|
// this field across the dirent.
|
|
//
|
|
|
|
FirstClusterOfFile = Dirent->FirstClusterOfFile;
|
|
|
|
if (FatIsFat32( Fcb->Vcb )) {
|
|
|
|
FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16;
|
|
}
|
|
|
|
if (!RtlEqualString( &Name, &Fcb->ShortName.Name.Oem, TRUE )
|
|
|
|
||
|
|
|
|
!FatMatchFileSize(IrpContext, Dirent, Fcb )
|
|
|
|
||
|
|
|
|
(FirstClusterOfFile != Fcb->FirstClusterOfFile)
|
|
|
|
||
|
|
|
|
(Dirent->Attributes != Fcb->DirentFatFlags) ) {
|
|
|
|
FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// We passed. Get the Fcb ready to use again.
|
|
//
|
|
|
|
FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE );
|
|
}
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
FatUnpinBcb( IrpContext, DirentBcb );
|
|
} _SEH2_END;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
VOID
|
|
FatQuickVerifyVcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines just checks the verify bit in the real device and the
|
|
Vcb condition and raises an appropriate exception if so warented.
|
|
It is called when verifying both Fcbs and Vcbs.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb to check the condition of.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the real device needs to be verified we'll set the
|
|
// DeviceToVerify to be our real device and raise VerifyRequired.
|
|
//
|
|
|
|
if (FlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
|
|
|
|
DebugTrace(0, Dbg, "The Vcb needs to be verified\n", 0);
|
|
|
|
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
|
|
Vcb->Vpb->RealDevice );
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
|
|
}
|
|
|
|
//
|
|
// Based on the condition of the Vcb we'll either return to our
|
|
// caller or raise an error condition
|
|
//
|
|
|
|
switch (Vcb->VcbCondition) {
|
|
|
|
case VcbGood:
|
|
|
|
DebugTrace(0, Dbg, "The Vcb is good\n", 0);
|
|
|
|
//
|
|
// Do a check here of an operation that would try to modify a
|
|
// write protected media.
|
|
//
|
|
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) &&
|
|
((IrpContext->MajorFunction == IRP_MJ_WRITE) ||
|
|
(IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) ||
|
|
(IrpContext->MajorFunction == IRP_MJ_SET_EA) ||
|
|
(IrpContext->MajorFunction == IRP_MJ_FLUSH_BUFFERS) ||
|
|
(IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION) ||
|
|
(IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
|
|
IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST &&
|
|
IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.FileSystemControl.FsControlCode ==
|
|
FSCTL_MARK_VOLUME_DIRTY))) {
|
|
|
|
//
|
|
// Set the real device for the pop-up info, and set the verify
|
|
// bit in the device object, so that we will force a verify
|
|
// in case the user put the correct media back in.
|
|
//
|
|
|
|
|
|
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
|
|
Vcb->Vpb->RealDevice );
|
|
|
|
FatMarkDevForVerifyIfVcbMounted(Vcb);
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
|
|
}
|
|
|
|
break;
|
|
|
|
case VcbNotMounted:
|
|
|
|
DebugTrace(0, Dbg, "The Vcb is not mounted\n", 0);
|
|
|
|
//
|
|
// Set the real device for the pop-up info, and set the verify
|
|
// bit in the device object, so that we will force a verify
|
|
// in case the user put the correct media back in.
|
|
//
|
|
|
|
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
|
|
Vcb->Vpb->RealDevice );
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
|
|
|
|
break;
|
|
|
|
case VcbBad:
|
|
|
|
DebugTrace(0, Dbg, "The Vcb is bad\n", 0);
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED );
|
|
|
|
} else {
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
DebugDump("Invalid VcbCondition\n", 0, Vcb);
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
|
|
#endif
|
|
FatBugCheck( Vcb->VcbCondition, 0, 0 );
|
|
}
|
|
}
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
FatPerformVerify (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIRP Irp,
|
|
_In_ PDEVICE_OBJECT Device
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines performs an IoVerifyVolume operation and takes the
|
|
appropriate action. After the Verify is complete the originating
|
|
Irp is sent off to an Ex Worker Thread. This routine is called
|
|
from the exception handler.
|
|
|
|
Arguments:
|
|
|
|
Irp - The irp to send off after all is well and done.
|
|
|
|
Device - The real device needing verification.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVCB Vcb;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
|
|
BOOLEAN AllowRawMount = FALSE;
|
|
BOOLEAN VcbDeleted = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check if this Irp has a status of Verify required and if it does
|
|
// then call the I/O system to do a verify.
|
|
//
|
|
// Skip the IoVerifyVolume if this is a mount or verify request
|
|
// itself. Trying a recursive mount will cause a deadlock with
|
|
// the DeviceObject->DeviceLock.
|
|
//
|
|
|
|
if ( (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
|
|
((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) ||
|
|
(IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME)) ) {
|
|
|
|
return FatFsdPostRequest( IrpContext, Irp );
|
|
}
|
|
|
|
DebugTrace(0, Dbg, "Verify Required, DeviceObject = %p\n", Device);
|
|
|
|
//
|
|
// Extract a pointer to the Vcb from the VolumeDeviceObject.
|
|
// Note that since we have specifically excluded mount,
|
|
// requests, we know that IrpSp->DeviceObject is indeed a
|
|
// volume device object.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject,
|
|
VOLUME_DEVICE_OBJECT,
|
|
DeviceObject )->Vcb;
|
|
|
|
//
|
|
// Check if the volume still thinks it needs to be verified,
|
|
// if it doesn't then we can skip doing a verify because someone
|
|
// else beat us to it.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// We will allow Raw to mount this volume if we were doing a
|
|
// a DASD open.
|
|
//
|
|
|
|
if ( (IrpContext->MajorFunction == IRP_MJ_CREATE) &&
|
|
(IrpSp->FileObject->FileName.Length == 0) &&
|
|
(IrpSp->FileObject->RelatedFileObject == NULL) ) {
|
|
|
|
AllowRawMount = TRUE;
|
|
}
|
|
|
|
//
|
|
// Send down the verify. This could be going to a different
|
|
// filesystem.
|
|
//
|
|
|
|
Status = IoVerifyVolume( Device, AllowRawMount );
|
|
|
|
//
|
|
// If the verify operation completed it will return
|
|
// either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly.
|
|
//
|
|
// If FatVerifyVolume encountered an error during
|
|
// processing, it will return that error. If we got
|
|
// STATUS_WRONG_VOLUME from the verfy, and our volume
|
|
// is now mounted, commute the status to STATUS_SUCCESS.
|
|
//
|
|
// Acquire the Vcb so we're working with a stable Vcb condition.
|
|
//
|
|
|
|
FatAcquireSharedVcb(IrpContext, Vcb);
|
|
|
|
if ( (Status == STATUS_WRONG_VOLUME) &&
|
|
(Vcb->VcbCondition == VcbGood) ) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbGood)) {
|
|
|
|
Status = STATUS_WRONG_VOLUME;
|
|
}
|
|
|
|
//
|
|
// Do a quick unprotected check here. The routine will do
|
|
// a safe check. After here we can release the resource.
|
|
// Note that if the volume really went away, we will be taking
|
|
// the Reparse path.
|
|
//
|
|
|
|
if ((VcbGood != Vcb->VcbCondition) &&
|
|
(0 == Vcb->OpenFileCount) ) {
|
|
|
|
FatReleaseVcb( IrpContext, Vcb);
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( push )
|
|
#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
|
|
#pragma prefast( disable: 28193 )
|
|
#endif
|
|
FatAcquireExclusiveGlobal( IrpContext );
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( pop )
|
|
#endif
|
|
|
|
FatAcquireExclusiveVcb( IrpContext,
|
|
Vcb );
|
|
|
|
VcbDeleted = FatCheckForDismount( IrpContext,
|
|
Vcb,
|
|
FALSE );
|
|
|
|
if (!VcbDeleted) {
|
|
|
|
FatReleaseVcb( IrpContext,
|
|
Vcb );
|
|
}
|
|
|
|
FatReleaseGlobal( IrpContext );
|
|
}
|
|
else {
|
|
|
|
FatReleaseVcb( IrpContext, Vcb);
|
|
}
|
|
|
|
//
|
|
// If the IopMount in IoVerifyVolume did something, and
|
|
// this is an absolute open, force a reparse.
|
|
//
|
|
|
|
if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
|
|
(FileObject->RelatedFileObject == NULL) &&
|
|
((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) {
|
|
|
|
Irp->IoStatus.Information = IO_REMOUNT;
|
|
|
|
FatCompleteRequest( IrpContext, Irp, STATUS_REPARSE );
|
|
Status = STATUS_REPARSE;
|
|
Irp = NULL;
|
|
}
|
|
|
|
if ( (Irp != NULL) && !NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// Fill in the device object if required.
|
|
//
|
|
|
|
if ( IoIsErrorUserInduced( Status ) ) {
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp, Device );
|
|
}
|
|
|
|
NT_ASSERT( STATUS_VERIFY_REQUIRED != Status);
|
|
|
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|
}
|
|
|
|
//
|
|
// If there is still an Irp, send it off to an Ex Worker thread.
|
|
//
|
|
|
|
if ( Irp != NULL ) {
|
|
|
|
Status = FatFsdPostRequest( IrpContext, Irp );
|
|
}
|
|
|
|
}
|
|
_SEH2_EXCEPT (FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
|
|
|
//
|
|
// We had some trouble trying to perform the verify or raised
|
|
// an error ourselves. So we'll abort the I/O request with
|
|
// the error status that we get back from the execption code.
|
|
//
|
|
|
|
Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
|
|
} _SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
FatMarkVolumeCompletionRoutine(
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_In_ PIRP Irp,
|
|
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
|
|
)
|
|
|
|
{
|
|
//
|
|
// Set the event so that our call will wake up.
|
|
//
|
|
|
|
KeSetEvent( (PKEVENT)Contxt, 0, FALSE );
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|