reactos/drivers/filesystems/fastfat/deviosup.c

3838 lines
103 KiB
C

/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
DevIoSup.c
Abstract:
This module implements the low lever disk read/write support for Fat.
--*/
#include "fatprocs.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (FAT_BUG_CHECK_DEVIOSUP)
//
// Local debug trace level
//
#define Dbg (DEBUG_TRACE_DEVIOSUP)
#define CollectDiskIoStats(VCB,FUNCTION,IS_USER_IO,COUNT) { \
PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common; \
if (IS_USER_IO) { \
if ((FUNCTION) == IRP_MJ_WRITE) { \
Stats->UserDiskWrites += (COUNT); \
} else { \
Stats->UserDiskReads += (COUNT); \
} \
} else { \
if ((FUNCTION) == IRP_MJ_WRITE) { \
Stats->MetaDataDiskWrites += (COUNT); \
} else { \
Stats->MetaDataDiskReads += (COUNT); \
} \
} \
}
typedef struct _FAT_SYNC_CONTEXT {
//
// Io status block for the request
//
IO_STATUS_BLOCK Iosb;
//
// Event to be signaled when the request completes
//
KEVENT Event;
} FAT_SYNC_CONTEXT, *PFAT_SYNC_CONTEXT;
//
// Completion Routine declarations
//
IO_COMPLETION_ROUTINE FatMultiSyncCompletionRoutine;
NTSTATUS
NTAPI
FatMultiSyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
);
IO_COMPLETION_ROUTINE FatMultiAsyncCompletionRoutine;
NTSTATUS
NTAPI
FatMultiAsyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
);
IO_COMPLETION_ROUTINE FatSpecialSyncCompletionRoutine;
NTSTATUS
NTAPI
FatSpecialSyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
);
IO_COMPLETION_ROUTINE FatSingleSyncCompletionRoutine;
NTSTATUS
NTAPI
FatSingleSyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
);
IO_COMPLETION_ROUTINE FatSingleAsyncCompletionRoutine;
NTSTATUS
NTAPI
FatSingleAsyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
);
IO_COMPLETION_ROUTINE FatPagingFileCompletionRoutine;
NTSTATUS
NTAPI
FatPagingFileCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID MasterIrp
);
IO_COMPLETION_ROUTINE FatPagingFileCompletionRoutineCatch;
NTSTATUS
NTAPI
FatPagingFileCompletionRoutineCatch (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
);
VOID
FatSingleNonAlignedSync (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN PUCHAR Buffer,
IN LBO Lbo,
IN ULONG ByteCount,
IN PIRP Irp
);
//
// The following macro decides whether to send a request directly to
// the device driver, or to other routines. It was meant to
// replace IoCallDriver as transparently as possible. It must only be
// called with a read or write Irp.
//
// NTSTATUS
// FatLowLevelReadWrite (
// PIRP_CONTEXT IrpContext,
// PDEVICE_OBJECT DeviceObject,
// PIRP Irp,
// PVCB Vcb
// );
//
#define FatLowLevelReadWrite(IRPCONTEXT,DO,IRP,VCB) ( \
IoCallDriver((DO),(IRP)) \
)
//
// The following macro handles completion-time zeroing of buffers.
//
#define FatDoCompletionZero( I, C ) \
if ((C)->ZeroMdl) { \
NT_ASSERT( (C)->ZeroMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | \
MDL_SOURCE_IS_NONPAGED_POOL));\
if (NT_SUCCESS((I)->IoStatus.Status)) { \
RtlZeroMemory( (C)->ZeroMdl->MappedSystemVa, \
(C)->ZeroMdl->ByteCount ); \
} \
IoFreeMdl((C)->ZeroMdl); \
(C)->ZeroMdl = NULL; \
}
#if (NTDDI_VERSION >= NTDDI_WIN8)
#define FatUpdateIOCountersPCW(IsAWrite,Count) \
FsRtlUpdateDiskCounters( ((IsAWrite) ? 0 : (Count) ), \
((IsAWrite) ? (Count) : 0) )
#else
#define FatUpdateIOCountersPCW(IsAWrite,Count)
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatMultipleAsync)
#pragma alloc_text(PAGE, FatSingleAsync)
#pragma alloc_text(PAGE, FatSingleNonAlignedSync)
#pragma alloc_text(PAGE, FatWaitSync)
#pragma alloc_text(PAGE, FatLockUserBuffer)
#pragma alloc_text(PAGE, FatBufferUserBuffer)
#pragma alloc_text(PAGE, FatMapUserBuffer)
#pragma alloc_text(PAGE, FatNonCachedIo)
#pragma alloc_text(PAGE, FatNonCachedNonAlignedRead)
#pragma alloc_text(PAGE, FatPerformDevIoCtrl)
#endif
typedef struct FAT_PAGING_FILE_CONTEXT {
KEVENT Event;
PMDL RestoreMdl;
} FAT_PAGING_FILE_CONTEXT, *PFAT_PAGING_FILE_CONTEXT;
VOID
FatPagingFileIo (
IN PIRP Irp,
IN PFCB Fcb
)
/*++
Routine Description:
This routine performs the non-cached disk io described in its parameters.
This routine nevers blocks, and should only be used with the paging
file since no completion processing is performed.
Arguments:
Irp - Supplies the requesting Irp.
Fcb - Supplies the file to act on.
Return Value:
None.
--*/
{
//
// Declare some local variables for enumeration through the
// runs of the file.
//
VBO Vbo;
ULONG ByteCount;
PMDL Mdl;
LBO NextLbo;
VBO NextVbo = 0;
ULONG NextByteCount;
ULONG RemainingByteCount;
BOOLEAN MustSucceed;
ULONG FirstIndex;
ULONG CurrentIndex;
ULONG LastIndex;
LBO LastLbo;
ULONG LastByteCount;
BOOLEAN MdlIsReserve = FALSE;
BOOLEAN IrpIsMaster = FALSE;
FAT_PAGING_FILE_CONTEXT Context;
LONG IrpCount;
PIRP AssocIrp;
PIO_STACK_LOCATION IrpSp;
PIO_STACK_LOCATION NextIrpSp;
ULONG BufferOffset;
PDEVICE_OBJECT DeviceObject;
#ifndef __REACTOS__
BOOLEAN IsAWrite = FALSE;
#endif
DebugTrace(+1, Dbg, "FatPagingFileIo\n", 0);
DebugTrace( 0, Dbg, "Irp = %p\n", Irp );
DebugTrace( 0, Dbg, "Fcb = %p\n", Fcb );
NT_ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ));
//
// Initialize some locals.
//
BufferOffset = 0;
DeviceObject = Fcb->Vcb->TargetDeviceObject;
IrpSp = IoGetCurrentIrpStackLocation( Irp );
Vbo = IrpSp->Parameters.Read.ByteOffset.LowPart;
ByteCount = IrpSp->Parameters.Read.Length;
#ifndef __REACTOS__
IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
#endif
MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb,
Vbo,
&NextLbo,
&NextByteCount,
&FirstIndex);
//
// If this run isn't present, something is very wrong.
//
if (!MustSucceed) {
#ifdef _MSC_VER
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
#endif
FatBugCheck( Vbo, ByteCount, 0 );
}
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Charge the IO to paging file to current thread
//
if (FatDiskAccountingEnabled) {
PETHREAD ThreadIssuingIo = PsGetCurrentThread();
BOOLEAN IsWriteOperation = FALSE;
if (IrpSp->MajorFunction == IRP_MJ_WRITE) {
IsWriteOperation = TRUE;
}
PsUpdateDiskCounters( PsGetThreadProcess( ThreadIssuingIo ),
(IsWriteOperation ? 0 : ByteCount ), // bytes to read
(IsWriteOperation ? ByteCount : 0), // bytes to write
(IsWriteOperation ? 0 : 1), // # of reads
(IsWriteOperation ? 1 : 0), // # of writes
0 );
}
#endif
// See if the write covers a single valid run, and if so pass
// it on.
//
if ( NextByteCount >= ByteCount ) {
DebugTrace( 0, Dbg, "Passing Irp on to Disk Driver\n", 0 );
//
// Setup the next IRP stack location for the disk driver beneath us.
//
NextIrpSp = IoGetNextIrpStackLocation( Irp );
NextIrpSp->MajorFunction = IrpSp->MajorFunction;
NextIrpSp->Parameters.Read.Length = ByteCount;
NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo;
//
// Since this is Paging file IO, we'll just ignore the verify bit.
//
SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
//
// Set up the completion routine address in our stack frame.
// This is only invoked on error or cancel, and just copies
// the error Status into master irp's iosb.
//
// If the error implies a media problem, it also enqueues a
// worker item to write out the dirty bit so that the next
// time we run we will do a autochk /r
//
IoSetCompletionRoutine( Irp,
&FatPagingFileCompletionRoutine,
Irp,
FALSE,
TRUE,
TRUE );
//
// Issue the read/write request
//
// If IoCallDriver returns an error, it has completed the Irp
// and the error will be dealt with as a normal IO error.
//
(VOID)IoCallDriver( DeviceObject, Irp );
//
// We just issued an IO to the storage stack, update the counters indicating so.
//
if (FatDiskAccountingEnabled) {
FatUpdateIOCountersPCW( IsAWrite, ByteCount );
}
DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0);
return;
}
//
// Find out how may runs there are.
//
MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb,
Vbo + ByteCount - 1,
&LastLbo,
&LastByteCount,
&LastIndex);
//
// If this run isn't present, something is very wrong.
//
if (!MustSucceed) {
#ifdef _MSC_VER
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
#endif
FatBugCheck( Vbo + ByteCount - 1, 1, 0 );
}
CurrentIndex = FirstIndex;
//
// Now set up the Irp->IoStatus. It will be modified by the
// multi-completion routine in case of error or verify required.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = ByteCount;
//
// Loop while there are still byte writes to satisfy. The way we'll work this
// is to hope for the best - one associated IRP per run, which will let us be
// completely async after launching all the IO.
//
// IrpCount will indicate the remaining number of associated Irps to launch.
//
// All we have to do is make sure IrpCount doesn't hit zero before we're building
// the very last Irp. If it is positive when we're done, it means we have to
// wait for the rest of the associated Irps to come back before we complete the
// master by hand.
//
// This will keep the master from completing early.
//
Irp->AssociatedIrp.IrpCount = IrpCount = LastIndex - FirstIndex + 1;
while (CurrentIndex <= LastIndex) {
//
// Reset this for unwinding purposes
//
AssocIrp = NULL;
//
// If next run is larger than we need, "ya get what ya need".
//
if (NextByteCount > ByteCount) {
NextByteCount = ByteCount;
}
RemainingByteCount = 0;
//
// Allocate and build a partial Mdl for the request.
//
Mdl = IoAllocateMdl( (PCHAR)Irp->UserBuffer + BufferOffset,
NextByteCount,
FALSE,
FALSE,
AssocIrp );
if (Mdl == NULL) {
//
// Pick up the reserve MDL
//
KeWaitForSingleObject( &FatReserveEvent, Executive, KernelMode, FALSE, NULL );
Mdl = FatReserveMdl;
MdlIsReserve = TRUE;
//
// Trim to fit the size of the reserve MDL.
//
if (NextByteCount > FAT_RESERVE_MDL_SIZE * PAGE_SIZE) {
RemainingByteCount = NextByteCount - FAT_RESERVE_MDL_SIZE * PAGE_SIZE;
NextByteCount = FAT_RESERVE_MDL_SIZE * PAGE_SIZE;
}
}
IoBuildPartialMdl( Irp->MdlAddress,
Mdl,
(PCHAR)Irp->UserBuffer + BufferOffset,
NextByteCount );
//
// Now that we have properly bounded this piece of the transfer, it is
// time to read/write it. We can simplify life slightly by always
// re-using the master IRP for cases where we use the reserve MDL,
// since we'll always be synchronous for those and can use a single
// completion context on our local stack.
//
// We also must prevent ourselves from issuing an associated IRP that would
// complete the master UNLESS this is the very last IRP we'll issue.
//
// This logic looks a bit complicated, but is hopefully understandable.
//
if (!MdlIsReserve &&
(IrpCount != 1 ||
(CurrentIndex == LastIndex &&
RemainingByteCount == 0))) {
AssocIrp = IoMakeAssociatedIrp( Irp, (CCHAR)(DeviceObject->StackSize + 1) );
}
if (AssocIrp == NULL) {
AssocIrp = Irp;
IrpIsMaster = TRUE;
//
// We need to drain the associated Irps so we can reliably figure out if
// the master Irp is showing a failed status, in which case we bail out
// immediately - as opposed to putting the value in the status field in
// jeopardy due to our re-use of the master Irp.
//
while (Irp->AssociatedIrp.IrpCount != IrpCount) {
KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds);
}
//
// Note that since we failed to launch this associated Irp, that the completion
// code at the bottom will take care of completing the master Irp.
//
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
NT_ASSERT( IrpCount );
break;
}
} else {
//
// Indicate we used an associated Irp.
//
IrpCount -= 1;
}
//
// With an associated IRP, we must take over the first stack location so
// we can have one to put the completion routine on. When re-using the
// master IRP, its already there.
//
if (!IrpIsMaster) {
//
// Get the first IRP stack location in the associated Irp
//
IoSetNextIrpStackLocation( AssocIrp );
NextIrpSp = IoGetCurrentIrpStackLocation( AssocIrp );
//
// Setup the Stack location to describe our read.
//
NextIrpSp->MajorFunction = IrpSp->MajorFunction;
NextIrpSp->Parameters.Read.Length = NextByteCount;
NextIrpSp->Parameters.Read.ByteOffset.QuadPart = Vbo;
//
// We also need the VolumeDeviceObject in the Irp stack in case
// we take the failure path.
//
NextIrpSp->DeviceObject = IrpSp->DeviceObject;
} else {
//
// Save the MDL in the IRP and prepare the stack
// context for the completion routine.
//
KeInitializeEvent( &Context.Event, SynchronizationEvent, FALSE );
Context.RestoreMdl = Irp->MdlAddress;
}
//
// And drop our Mdl into the Irp.
//
AssocIrp->MdlAddress = Mdl;
//
// Set up the completion routine address in our stack frame.
// For true associated IRPs, this is only invoked on error or
// cancel, and just copies the error Status into master irp's
// iosb.
//
// If the error implies a media problem, it also enqueues a
// worker item to write out the dirty bit so that the next
// time we run we will do a autochk /r
//
if (IrpIsMaster) {
IoSetCompletionRoutine( AssocIrp,
FatPagingFileCompletionRoutineCatch,
&Context,
TRUE,
TRUE,
TRUE );
} else {
IoSetCompletionRoutine( AssocIrp,
FatPagingFileCompletionRoutine,
Irp,
FALSE,
TRUE,
TRUE );
}
//
// Setup the next IRP stack location for the disk driver beneath us.
//
NextIrpSp = IoGetNextIrpStackLocation( AssocIrp );
//
// Since this is paging file IO, we'll just ignore the verify bit.
//
SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
//
// Setup the Stack location to do a read from the disk driver.
//
NextIrpSp->MajorFunction = IrpSp->MajorFunction;
NextIrpSp->Parameters.Read.Length = NextByteCount;
NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo;
(VOID)IoCallDriver( DeviceObject, AssocIrp );
//
// We just issued an IO to the storage stack, update the counters indicating so.
//
if (FatDiskAccountingEnabled) {
FatUpdateIOCountersPCW( IsAWrite, (ULONG64)NextByteCount );
}
//
// Wait for the Irp in the catch case and drop the flags.
//
if (IrpIsMaster) {
KeWaitForSingleObject( &Context.Event, Executive, KernelMode, FALSE, NULL );
IrpIsMaster = MdlIsReserve = FALSE;
//
// If the Irp is showing a failed status, there is no point in continuing.
// In doing so, we get to avoid squirreling away the failed status in case
// we were to re-use the master irp again.
//
// Note that since we re-used the master, we must not have issued the "last"
// associated Irp, and thus the completion code at the bottom will take care
// of that for us.
//
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
NT_ASSERT( IrpCount );
break;
}
}
//
// Now adjust everything for the next pass through the loop.
//
Vbo += NextByteCount;
BufferOffset += NextByteCount;
ByteCount -= NextByteCount;
//
// Try to lookup the next run, if we are not done and we got
// all the way through the current run.
//
if (RemainingByteCount) {
//
// Advance the Lbo/Vbo if we have more to do in the current run.
//
NextLbo += NextByteCount;
NextVbo += NextByteCount;
NextByteCount = RemainingByteCount;
} else {
CurrentIndex += 1;
if ( CurrentIndex <= LastIndex ) {
NT_ASSERT( ByteCount != 0 );
FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb,
CurrentIndex,
&NextVbo,
&NextLbo,
&NextByteCount );
NT_ASSERT( NextVbo == Vbo );
}
}
} // while ( CurrentIndex <= LastIndex )
//
// If we didn't get enough associated Irps going to make this asynchronous, we
// twiddle our thumbs and wait for those we did launch to complete.
//
if (IrpCount) {
while (Irp->AssociatedIrp.IrpCount != IrpCount) {
KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds);
}
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
}
DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0);
return;
}
#if (NTDDI_VERSION >= NTDDI_WIN8)
VOID
FatUpdateDiskStats (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN ULONG ByteCount
)
/*++
Routine Description:
Charge appropriate process for the IO this IRP will cause.
Arguments:
IrpContext- The Irp Context
Irp - Supplies the requesting Irp.
ByteCount - The lengh of the operation.
Return Value:
None.
--*/
{
PETHREAD OriginatingThread = NULL;
ULONG NumReads = 0;
ULONG NumWrites = 0;
ULONGLONG BytesToRead = 0;
ULONGLONG BytesToWrite = 0;
//
// Here we attempt to charge the IO back to the originating process.
// - These checks are intended to cover following cases:
// o Buffered sync reads
// o Unbuffered sync read
// o Inline metadata reads
// o memory mapped reads (in-line faulting of data)
//
if (IrpContext->MajorFunction == IRP_MJ_READ) {
NumReads++;
BytesToRead = ByteCount;
if ((Irp->Tail.Overlay.Thread != NULL) &&
!IoIsSystemThread( Irp->Tail.Overlay.Thread )) {
OriginatingThread = Irp->Tail.Overlay.Thread;
} else if (!IoIsSystemThread( PsGetCurrentThread() )) {
OriginatingThread = PsGetCurrentThread();
//
// We couldn't find a non-system entity, so this should be charged to system.
// Do so only if we are top level.
// If we are not top-level then the read was initiated by someone like Cc (read ahead)
// who should have already accounted for this IO.
//
} else if (IoIsSystemThread( PsGetCurrentThread() ) &&
(IoGetTopLevelIrp() == Irp)) {
OriginatingThread = PsGetCurrentThread();
}
//
// Charge the write to Originating process.
// Intended to cover the following writes:
// - Unbuffered sync write
// - unbuffered async write
//
// If we re not top-level, then it should already have been accounted for
// somewhere else (Cc).
//
} else if (IrpContext->MajorFunction == IRP_MJ_WRITE) {
NumWrites++;
BytesToWrite = ByteCount;
if (IoGetTopLevelIrp() == Irp) {
if ((Irp->Tail.Overlay.Thread != NULL) &&
!IoIsSystemThread( Irp->Tail.Overlay.Thread )) {
OriginatingThread = Irp->Tail.Overlay.Thread;
} else {
OriginatingThread = PsGetCurrentThread();
}
//
// For mapped page writes
//
} else if (IoGetTopLevelIrp() == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP) {
OriginatingThread = PsGetCurrentThread();
}
}
if (OriginatingThread != NULL) {
PsUpdateDiskCounters( PsGetThreadProcess( OriginatingThread ),
BytesToRead,
BytesToWrite,
NumReads,
NumWrites,
0 );
}
}
#endif
_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatNonCachedIo (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PFCB FcbOrDcb,
IN ULONG StartingVbo,
IN ULONG ByteCount,
IN ULONG UserByteCount,
IN ULONG StreamFlags
)
/*++
Routine Description:
This routine performs the non-cached disk io described in its parameters.
The choice of a single run is made if possible, otherwise multiple runs
are executed.
Arguments:
IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
Irp - Supplies the requesting Irp.
FcbOrDcb - Supplies the file to act on.
StartingVbo - The starting point for the operation.
ByteCount - The lengh of the operation.
UserByteCount - The last byte the user can see, rest to be zeroed.
StreamFlags - flag to indicate special attributes for a NonCachedIo.
Return Value:
None.
--*/
{
//
// Declare some local variables for enumeration through the
// runs of the file, and an array to store parameters for
// parallel I/Os
//
BOOLEAN Wait;
LBO NextLbo;
VBO NextVbo;
ULONG NextByteCount;
BOOLEAN NextIsAllocated;
LBO LastLbo;
ULONG LastByteCount;
BOOLEAN LastIsAllocated;
BOOLEAN EndOnMax;
ULONG FirstIndex;
ULONG CurrentIndex;
ULONG LastIndex;
ULONG NextRun;
ULONG BufferOffset;
ULONG OriginalByteCount;
IO_RUN StackIoRuns[FAT_MAX_IO_RUNS_ON_STACK];
PIO_RUN IoRuns;
PAGED_CODE();
UNREFERENCED_PARAMETER( StreamFlags );
DebugTrace(+1, Dbg, "FatNonCachedIo\n", 0);
DebugTrace( 0, Dbg, "Irp = %p\n", Irp );
DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
DebugTrace( 0, Dbg, "FcbOrDcb = %p\n", FcbOrDcb );
DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo );
DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount );
if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) {
PFILE_SYSTEM_STATISTICS Stats =
&FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors];
if (IrpContext->MajorFunction == IRP_MJ_READ) {
Stats->Fat.NonCachedReads += 1;
Stats->Fat.NonCachedReadBytes += ByteCount;
} else {
Stats->Fat.NonCachedWrites += 1;
Stats->Fat.NonCachedWriteBytes += ByteCount;
}
}
//
// Initialize some locals.
//
NextRun = 0;
BufferOffset = 0;
OriginalByteCount = ByteCount;
Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
#if (NTDDI_VERSION >= NTDDI_WIN8)
//
// Disk IO accounting
//
if (FatDiskAccountingEnabled) {
FatUpdateDiskStats( IrpContext,
Irp,
ByteCount );
}
#endif
//
// For nonbuffered I/O, we need the buffer locked in all
// cases.
//
// This call may raise. If this call succeeds and a subsequent
// condition is raised, the buffers are unlocked automatically
// by the I/O system when the request is completed, via the
// Irp->MdlAddress field.
//
FatLockUserBuffer( IrpContext,
Irp,
(IrpContext->MajorFunction == IRP_MJ_READ) ?
IoWriteAccess : IoReadAccess,
ByteCount );
//
// No zeroing for trailing sectors if requested.
// Otherwise setup the required zeroing for read requests.
//
if (UserByteCount != ByteCount) {
PMDL Mdl;
NT_ASSERT( ByteCount > UserByteCount );
_Analysis_assume_(ByteCount > UserByteCount);
Mdl = IoAllocateMdl( (PUCHAR) Irp->UserBuffer + UserByteCount,
ByteCount - UserByteCount,
FALSE,
FALSE,
NULL );
if (Mdl == NULL) {
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
}
IoBuildPartialMdl( Irp->MdlAddress,
Mdl,
(PUCHAR) Irp->UserBuffer + UserByteCount,
ByteCount - UserByteCount );
IrpContext->FatIoContext->ZeroMdl = Mdl;
//
// Map the MDL now so we can't fail at IO completion time. Note
// that this will be only a single page.
//
if (MmGetSystemAddressForMdlSafe( Mdl, NormalPagePriority | MdlMappingNoExecute ) == NULL) {
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
}
}
//
// Try to lookup the first run. If there is just a single run,
// we may just be able to pass it on.
//
FatLookupFileAllocation( IrpContext,
FcbOrDcb,
StartingVbo,
&NextLbo,
&NextByteCount,
&NextIsAllocated,
&EndOnMax,
&FirstIndex );
//
// We just added the allocation, thus there must be at least
// one entry in the mcb corresponding to our write, ie.
// NextIsAllocated must be true. If not, the pre-existing file
// must have an allocation error.
//
if ( !NextIsAllocated ) {
FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
}
NT_ASSERT( NextByteCount != 0 );
//
// If the request was not aligned correctly, read in the first
// part first.
//
//
// See if the write covers a single valid run, and if so pass
// it on. We must bias this by the byte that is lost at the
// end of the maximal file.
//
if ( NextByteCount >= ByteCount - (EndOnMax ? 1 : 0)) {
if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction,
FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), 1);
} else {
PFILE_SYSTEM_STATISTICS Stats =
&FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors];
if (IrpContext->MajorFunction == IRP_MJ_READ) {
Stats->Fat.NonCachedDiskReads += 1;
} else {
Stats->Fat.NonCachedDiskWrites += 1;
}
}
DebugTrace( 0, Dbg, "Passing 1 Irp on to Disk Driver\n", 0 );
FatSingleAsync( IrpContext,
FcbOrDcb->Vcb,
NextLbo,
ByteCount,
Irp );
} else {
//
// If there we can't wait, and there are more runs than we can handle,
// we will have to post this request.
//
FatLookupFileAllocation( IrpContext,
FcbOrDcb,
StartingVbo + ByteCount - 1,
&LastLbo,
&LastByteCount,
&LastIsAllocated,
&EndOnMax,
&LastIndex );
//
// Since we already added the allocation for the whole
// write, assert that we find runs until ByteCount == 0
// Otherwise this file is corrupt.
//
if ( !LastIsAllocated ) {
FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
}
if (LastIndex - FirstIndex + 1 > FAT_MAX_IO_RUNS_ON_STACK) {
IoRuns = FsRtlAllocatePoolWithTag( PagedPool,
(LastIndex - FirstIndex + 1) * sizeof(IO_RUN),
TAG_IO_RUNS );
} else {
IoRuns = StackIoRuns;
}
NT_ASSERT( LastIndex != FirstIndex );
CurrentIndex = FirstIndex;
//
// Loop while there are still byte writes to satisfy.
//
while (CurrentIndex <= LastIndex) {
NT_ASSERT( NextByteCount != 0);
NT_ASSERT( ByteCount != 0);
//
// If next run is larger than we need, "ya get what you need".
//
if (NextByteCount > ByteCount) {
NextByteCount = ByteCount;
}
//
// Now that we have properly bounded this piece of the
// transfer, it is time to write it.
//
// We remember each piece of a parallel run by saving the
// essential information in the IoRuns array. The tranfers
// are started up in parallel below.
//
IoRuns[NextRun].Vbo = StartingVbo;
IoRuns[NextRun].Lbo = NextLbo;
IoRuns[NextRun].Offset = BufferOffset;
IoRuns[NextRun].ByteCount = NextByteCount;
NextRun += 1;
//
// Now adjust everything for the next pass through the loop.
//
StartingVbo += NextByteCount;
BufferOffset += NextByteCount;
ByteCount -= NextByteCount;
//
// Try to lookup the next run (if we are not done).
//
CurrentIndex += 1;
if ( CurrentIndex <= LastIndex ) {
NT_ASSERT( ByteCount != 0 );
FatGetNextMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb,
CurrentIndex,
&NextVbo,
&NextLbo,
&NextByteCount );
NT_ASSERT(NextVbo == StartingVbo);
}
} // while ( CurrentIndex <= LastIndex )
//
// Now set up the Irp->IoStatus. It will be modified by the
// multi-completion routine in case of error or verify required.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = OriginalByteCount;
if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction,
FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), NextRun);
}
//
// OK, now do the I/O.
//
_SEH2_TRY {
DebugTrace( 0, Dbg, "Passing Multiple Irps on to Disk Driver\n", 0 );
FatMultipleAsync( IrpContext,
FcbOrDcb->Vcb,
Irp,
NextRun,
IoRuns );
} _SEH2_FINALLY {
if (IoRuns != StackIoRuns) {
ExFreePool( IoRuns );
}
} _SEH2_END;
}
if (!Wait) {
DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0);
return STATUS_PENDING;
}
FatWaitSync( IrpContext );
DebugTrace(-1, Dbg, "FatNonCachedIo -> 0x%08lx\n", Irp->IoStatus.Status);
return Irp->IoStatus.Status;
}
_Requires_lock_held_(_Global_critical_region_)
VOID
FatNonCachedNonAlignedRead (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PFCB FcbOrDcb,
IN ULONG StartingVbo,
IN ULONG ByteCount
)
/*++
Routine Description:
This routine performs the non-cached disk io described in its parameters.
This routine differs from the above in that the range does not have to be
sector aligned. This accomplished with the use of intermediate buffers.
Arguments:
IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
Irp - Supplies the requesting Irp.
FcbOrDcb - Supplies the file to act on.
StartingVbo - The starting point for the operation.
ByteCount - The lengh of the operation.
Return Value:
None.
--*/
{
//
// Declare some local variables for enumeration through the
// runs of the file, and an array to store parameters for
// parallel I/Os
//
LBO NextLbo;
ULONG NextByteCount;
BOOLEAN NextIsAllocated;
ULONG SectorSize;
ULONG BytesToCopy;
ULONG OriginalByteCount;
ULONG OriginalStartingVbo;
BOOLEAN EndOnMax;
PUCHAR UserBuffer;
PUCHAR DiskBuffer = NULL;
PMDL Mdl;
PMDL SavedMdl;
PVOID SavedUserBuffer;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatNonCachedNonAlignedRead\n", 0);
DebugTrace( 0, Dbg, "Irp = %p\n", Irp );
DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
DebugTrace( 0, Dbg, "FcbOrDcb = %p\n", FcbOrDcb );
DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo );
DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount );
//
// Initialize some locals.
//
OriginalByteCount = ByteCount;
OriginalStartingVbo = StartingVbo;
SectorSize = FcbOrDcb->Vcb->Bpb.BytesPerSector;
NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
//
// For nonbuffered I/O, we need the buffer locked in all
// cases.
//
// This call may raise. If this call succeeds and a subsequent
// condition is raised, the buffers are unlocked automatically
// by the I/O system when the request is completed, via the
// Irp->MdlAddress field.
//
FatLockUserBuffer( IrpContext,
Irp,
IoWriteAccess,
ByteCount );
UserBuffer = FatMapUserBuffer( IrpContext, Irp );
//
// Allocate the local buffer
//
DiskBuffer = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned,
(ULONG) ROUND_TO_PAGES( SectorSize ),
TAG_IO_BUFFER );
//
// We use a try block here to ensure the buffer is freed, and to
// fill in the correct byte count in the Iosb.Information field.
//
_SEH2_TRY {
//
// If the beginning of the request was not aligned correctly, read in
// the first part first.
//
if ( StartingVbo & (SectorSize - 1) ) {
VBO Hole;
//
// Try to lookup the first run.
//
FatLookupFileAllocation( IrpContext,
FcbOrDcb,
StartingVbo,
&NextLbo,
&NextByteCount,
&NextIsAllocated,
&EndOnMax,
NULL );
//
// We just added the allocation, thus there must be at least
// one entry in the mcb corresponding to our write, ie.
// NextIsAllocated must be true. If not, the pre-existing file
// must have an allocation error.
//
if ( !NextIsAllocated ) {
FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
}
FatSingleNonAlignedSync( IrpContext,
FcbOrDcb->Vcb,
DiskBuffer,
NextLbo & ~((LONG)SectorSize - 1),
SectorSize,
Irp );
if (!NT_SUCCESS( Irp->IoStatus.Status )) {
try_return( NOTHING );
}
//
// Now copy the part of the first sector that we want to the user
// buffer.
//
Hole = StartingVbo & (SectorSize - 1);
BytesToCopy = ByteCount >= SectorSize - Hole ?
SectorSize - Hole : ByteCount;
RtlCopyMemory( UserBuffer, DiskBuffer + Hole, BytesToCopy );
StartingVbo += BytesToCopy;
ByteCount -= BytesToCopy;
if ( ByteCount == 0 ) {
try_return( NOTHING );
}
}
NT_ASSERT( (StartingVbo & (SectorSize - 1)) == 0 );
//
// If there is a tail part that is not sector aligned, read it.
//
if ( ByteCount & (SectorSize - 1) ) {
VBO LastSectorVbo;
LastSectorVbo = StartingVbo + (ByteCount & ~(SectorSize - 1));
//
// Try to lookup the last part of the requested range.
//
FatLookupFileAllocation( IrpContext,
FcbOrDcb,
LastSectorVbo,
&NextLbo,
&NextByteCount,
&NextIsAllocated,
&EndOnMax,
NULL );
//
// We just added the allocation, thus there must be at least
// one entry in the mcb corresponding to our write, ie.
// NextIsAllocated must be true. If not, the pre-existing file
// must have an allocation error.
//
if ( !NextIsAllocated ) {
FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
}
FatSingleNonAlignedSync( IrpContext,
FcbOrDcb->Vcb,
DiskBuffer,
NextLbo,
SectorSize,
Irp );
if (!NT_SUCCESS( Irp->IoStatus.Status )) {
try_return( NOTHING );
}
//
// Now copy over the part of this last sector that we need.
//
BytesToCopy = ByteCount & (SectorSize - 1);
UserBuffer += LastSectorVbo - OriginalStartingVbo;
RtlCopyMemory( UserBuffer, DiskBuffer, BytesToCopy );
ByteCount -= BytesToCopy;
if ( ByteCount == 0 ) {
try_return( NOTHING );
}
}
NT_ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 );
//
// Now build a Mdl describing the sector aligned balance of the transfer,
// and put it in the Irp, and read that part.
//
SavedMdl = Irp->MdlAddress;
Irp->MdlAddress = NULL;
SavedUserBuffer = Irp->UserBuffer;
Irp->UserBuffer = (PUCHAR)MmGetMdlVirtualAddress( SavedMdl ) +
(StartingVbo - OriginalStartingVbo);
Mdl = IoAllocateMdl( Irp->UserBuffer,
ByteCount,
FALSE,
FALSE,
Irp );
if (Mdl == NULL) {
Irp->MdlAddress = SavedMdl;
Irp->UserBuffer = SavedUserBuffer;
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
}
IoBuildPartialMdl( SavedMdl,
Mdl,
Irp->UserBuffer,
ByteCount );
//
// Try to read in the pages.
//
_SEH2_TRY {
FatNonCachedIo( IrpContext,
Irp,
FcbOrDcb,
StartingVbo,
ByteCount,
ByteCount,
0 );
} _SEH2_FINALLY {
IoFreeMdl( Irp->MdlAddress );
Irp->MdlAddress = SavedMdl;
Irp->UserBuffer = SavedUserBuffer;
} _SEH2_END;
try_exit: NOTHING;
} _SEH2_FINALLY {
ExFreePool( DiskBuffer );
if ( !_SEH2_AbnormalTermination() && NT_SUCCESS(Irp->IoStatus.Status) ) {
Irp->IoStatus.Information = OriginalByteCount;
//
// We now flush the user's buffer to memory.
//
KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE );
}
} _SEH2_END;
DebugTrace(-1, Dbg, "FatNonCachedNonAlignedRead -> VOID\n", 0);
return;
}
VOID
FatMultipleAsync (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN PIRP MasterIrp,
IN ULONG MultipleIrpCount,
IN PIO_RUN IoRuns
)
/*++
Routine Description:
This routine first does the initial setup required of a Master IRP that is
going to be completed using associated IRPs. This routine should not
be used if only one async request is needed, instead the single read/write
async routines should be called.
A context parameter is initialized, to serve as a communications area
between here and the common completion routine. This initialization
includes allocation of a spinlock. The spinlock is deallocated in the
FatWaitSync routine, so it is essential that the caller insure that
this routine is always called under all circumstances following a call
to this routine.
Next this routine reads or writes one or more contiguous sectors from
a device asynchronously, and is used if there are multiple reads for a
master IRP. A completion routine is used to synchronize with the
completion of all of the I/O requests started by calls to this routine.
Also, prior to calling this routine the caller must initialize the
IoStatus field in the Context, with the correct success status and byte
count which are expected if all of the parallel transfers complete
successfully. After return this status will be unchanged if all requests
were, in fact, successful. However, if one or more errors occur, the
IoStatus will be modified to reflect the error status and byte count
from the first run (by Vbo) which encountered an error. I/O status
from all subsequent runs will not be indicated.
Arguments:
IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
Vcb - Supplies the device to be read
MasterIrp - Supplies the master Irp.
MulitpleIrpCount - Supplies the number of multiple async requests
that will be issued against the master irp.
IoRuns - Supplies an array containing the Vbo, Lbo, BufferOffset, and
ByteCount for all the runs to executed in parallel.
Return Value:
None.
--*/
{
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
PMDL Mdl;
BOOLEAN Wait;
PFAT_IO_CONTEXT Context;
#ifndef __REACTOS__
BOOLEAN IsAWrite = FALSE;
ULONG Length = 0;
#endif
ULONG UnwindRunCount = 0;
BOOLEAN ExceptionExpected = TRUE;
PAGED_CODE();
DebugTrace(+1, Dbg, "FatMultipleAsync\n", 0);
DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb );
DebugTrace( 0, Dbg, "MasterIrp = %p\n", MasterIrp );
DebugTrace( 0, Dbg, "MultipleIrpCount = %08lx\n", MultipleIrpCount );
DebugTrace( 0, Dbg, "IoRuns = %08lx\n", IoRuns );
//
// If this I/O originating during FatVerifyVolume, bypass the
// verify logic.
//
if (Vcb->VerifyThread == KeGetCurrentThread()) {
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY );
}
//
// Set up things according to whether this is truely async.
//
Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
Context = IrpContext->FatIoContext;
//
// Finish initializing Context, for use in Read/Write Multiple Asynch.
//
Context->MasterIrp = MasterIrp;
IrpSp = IoGetCurrentIrpStackLocation( MasterIrp );
#ifndef __REACTOS__
IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
Length = IrpSp->Parameters.Read.Length;
#endif
_SEH2_TRY {
//
// Itterate through the runs, doing everything that can fail
//
for ( UnwindRunCount = 0;
UnwindRunCount < MultipleIrpCount;
UnwindRunCount++ ) {
//
// Create an associated IRP, making sure there is one stack entry for
// us, as well.
//
IoRuns[UnwindRunCount].SavedIrp = 0;
Irp = IoMakeAssociatedIrp( MasterIrp,
(CCHAR)(Vcb->TargetDeviceObject->StackSize + 1) );
if (Irp == NULL) {
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
}
IoRuns[UnwindRunCount].SavedIrp = Irp;
//
// Allocate and build a partial Mdl for the request.
//
Mdl = IoAllocateMdl( (PCHAR)MasterIrp->UserBuffer +
IoRuns[UnwindRunCount].Offset,
IoRuns[UnwindRunCount].ByteCount,
FALSE,
FALSE,
Irp );
if (Mdl == NULL) {
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
}
//
// Sanity Check
//
NT_ASSERT( Mdl == Irp->MdlAddress );
IoBuildPartialMdl( MasterIrp->MdlAddress,
Mdl,
(PCHAR)MasterIrp->UserBuffer +
IoRuns[UnwindRunCount].Offset,
IoRuns[UnwindRunCount].ByteCount );
//
// Get the first IRP stack location in the associated Irp
//
IoSetNextIrpStackLocation( Irp );
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Setup the Stack location to describe our read.
//
IrpSp->MajorFunction = IrpContext->MajorFunction;
IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount;
IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Vbo;
//
// Set up the completion routine address in our stack frame.
//
IoSetCompletionRoutine( Irp,
Wait ?
&FatMultiSyncCompletionRoutine :
&FatMultiAsyncCompletionRoutine,
Context,
TRUE,
TRUE,
TRUE );
//
// Setup the next IRP stack location in the associated Irp for the disk
// driver beneath us.
//
IrpSp = IoGetNextIrpStackLocation( Irp );
//
// Setup the Stack location to do a read from the disk driver.
//
IrpSp->MajorFunction = IrpContext->MajorFunction;
IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount;
IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Lbo;
//
// If this Irp is the result of a WriteThough operation,
// tell the device to write it through.
//
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH )) {
SetFlag( IrpSp->Flags, SL_WRITE_THROUGH );
}
//
// If this I/O requires override verify, bypass the verify logic.
//
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) {
SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
}
}
//
// Now we no longer expect an exception. If the driver raises, we
// must bugcheck, because we do not know how to recover from that
// case.
//
ExceptionExpected = FALSE;
//
// We only need to set the associated IRP count in the master irp to
// make it a master IRP. But we set the count to one more than our
// caller requested, because we do not want the I/O system to complete
// the I/O. We also set our own count.
//
Context->IrpCount = MultipleIrpCount;
MasterIrp->AssociatedIrp.IrpCount = MultipleIrpCount;
if (Wait) {
MasterIrp->AssociatedIrp.IrpCount += 1;
}
else if (FlagOn( Context->Wait.Async.ResourceThreadId, 3 )) {
//
// For async requests if we acquired locks, transition the lock owners to an
// object, since when we return this thread could go away before request
// completion, and the resource package may try to boost priority.
//
if (Context->Wait.Async.Resource != NULL) {
ExSetResourceOwnerPointer( Context->Wait.Async.Resource,
(PVOID)Context->Wait.Async.ResourceThreadId );
}
if (Context->Wait.Async.Resource2 != NULL) {
ExSetResourceOwnerPointer( Context->Wait.Async.Resource2,
(PVOID)Context->Wait.Async.ResourceThreadId );
}
}
//
// Back up a copy of the IrpContext flags for later use in async completion.
//
Context->IrpContextFlags = IrpContext->Flags;
//
// Now that all the dangerous work is done, issue the read requests
//
for (UnwindRunCount = 0;
UnwindRunCount < MultipleIrpCount;
UnwindRunCount++) {
Irp = IoRuns[UnwindRunCount].SavedIrp;
DebugDoit( FatIoCallDriverCount += 1);
//
// If IoCallDriver returns an error, it has completed the Irp
// and the error will be caught by our completion routines
// and dealt with as a normal IO error.
//
(VOID)FatLowLevelReadWrite( IrpContext,
Vcb->TargetDeviceObject,
Irp,
Vcb );
}
//
// We just issued an IO to the storage stack, update the counters indicating so.
//
if (FatDiskAccountingEnabled) {
FatUpdateIOCountersPCW( IsAWrite, Length );
}
} _SEH2_FINALLY {
ULONG i;
DebugUnwind( FatMultipleAsync );
//
// Only allocating the spinlock, making the associated Irps
// and allocating the Mdls can fail.
//
if ( _SEH2_AbnormalTermination() ) {
//
// If the driver raised, we are hosed. He is not supposed to raise,
// and it is impossible for us to figure out how to clean up.
//
if (!ExceptionExpected) {
NT_ASSERT( ExceptionExpected );
#ifdef _MSC_VER
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
#endif
FatBugCheck( 0, 0, 0 );
}
//
// Unwind
//
for (i = 0; i <= UnwindRunCount; i++) {
if ( (Irp = IoRuns[i].SavedIrp) != NULL ) {
if ( Irp->MdlAddress != NULL ) {
IoFreeMdl( Irp->MdlAddress );
}
IoFreeIrp( Irp );
}
}
}
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatMultipleAsync -> VOID\n", 0);
} _SEH2_END;
return;
}
VOID
FatSingleAsync (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN LBO Lbo,
IN ULONG ByteCount,
IN PIRP Irp
)
/*++
Routine Description:
This routine reads or writes one or more contiguous sectors from a device
asynchronously, and is used if there is only one read necessary to
complete the IRP. It implements the read by simply filling
in the next stack frame in the Irp, and passing it on. The transfer
occurs to the single buffer originally specified in the user request.
Arguments:
IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
Vcb - Supplies the device to read
Lbo - Supplies the starting Logical Byte Offset to begin reading from
ByteCount - Supplies the number of bytes to read from the device
Irp - Supplies the master Irp to associated with the async
request.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpSp;
PFAT_IO_CONTEXT Context;
#ifndef __REACTOS__
BOOLEAN IsAWrite = FALSE;
#endif
PAGED_CODE();
DebugTrace(+1, Dbg, "FatSingleAsync\n", 0);
DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb );
DebugTrace( 0, Dbg, "Lbo = %08lx\n", Lbo);
DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount);
DebugTrace( 0, Dbg, "Irp = %p\n", Irp );
//
// If this I/O originating during FatVerifyVolume, bypass the
// verify logic.
//
if (Vcb->VerifyThread == KeGetCurrentThread()) {
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY );
}
//
// Set up the completion routine address in our stack frame.
//
IoSetCompletionRoutine( Irp,
FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ?
&FatSingleSyncCompletionRoutine :
&FatSingleAsyncCompletionRoutine,
IrpContext->FatIoContext,
TRUE,
TRUE,
TRUE );
//
// Setup the next IRP stack location in the associated Irp for the disk
// driver beneath us.
//
IrpSp = IoGetNextIrpStackLocation( Irp );
//
// Setup the Stack location to do a read from the disk driver.
//
IrpSp->MajorFunction = IrpContext->MajorFunction;
IrpSp->Parameters.Read.Length = ByteCount;
IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo;
#ifndef __REACTOS__
IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
#endif
//
// If this Irp is the result of a WriteThough operation,
// tell the device to write it through.
//
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH )) {
SetFlag( IrpSp->Flags, SL_WRITE_THROUGH );
}
//
// If this I/O requires override verify, bypass the verify logic.
//
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) {
SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
}
//
// For async requests if we acquired locks, transition the lock owners to an
// object, since when we return this thread could go away before request
// completion, and the resource package may try to boost priority.
//
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ) &&
FlagOn( IrpContext->FatIoContext->Wait.Async.ResourceThreadId, 3 )) {
Context = IrpContext->FatIoContext;
if (Context->Wait.Async.Resource != NULL) {
ExSetResourceOwnerPointer( Context->Wait.Async.Resource,
(PVOID)Context->Wait.Async.ResourceThreadId );
}
if (Context->Wait.Async.Resource2 != NULL) {
ExSetResourceOwnerPointer( Context->Wait.Async.Resource2,
(PVOID)Context->Wait.Async.ResourceThreadId );
}
}
//
// Back up a copy of the IrpContext flags for later use in async completion.
//
IrpContext->FatIoContext->IrpContextFlags = IrpContext->Flags;
//
// Issue the read request
//
DebugDoit( FatIoCallDriverCount += 1);
//
// If IoCallDriver returns an error, it has completed the Irp
// and the error will be caught by our completion routines
// and dealt with as a normal IO error.
//
(VOID)FatLowLevelReadWrite( IrpContext,
Vcb->TargetDeviceObject,
Irp,
Vcb );
//
// We just issued an IO to the storage stack, update the counters indicating so.
//
if (FatDiskAccountingEnabled) {
FatUpdateIOCountersPCW( IsAWrite, ByteCount );
}
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatSingleAsync -> VOID\n", 0);
return;
}
VOID
FatSingleNonAlignedSync (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN PUCHAR Buffer,
IN LBO Lbo,
IN ULONG ByteCount,
IN PIRP Irp
)
/*++
Routine Description:
This routine reads or writes one or more contiguous sectors from a device
Synchronously, and does so to a buffer that must come from non paged
pool. It saves a pointer to the Irp's original Mdl, and creates a new
one describing the given buffer. It implements the read by simply filling
in the next stack frame in the Irp, and passing it on. The transfer
occurs to the single buffer originally specified in the user request.
Arguments:
IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
Vcb - Supplies the device to read
Buffer - Supplies a buffer from non-paged pool.
Lbo - Supplies the starting Logical Byte Offset to begin reading from
ByteCount - Supplies the number of bytes to read from the device
Irp - Supplies the master Irp to associated with the async
request.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpSp;
PMDL Mdl;
PMDL SavedMdl;
#ifndef __REACTOS__
BOOLEAN IsAWrite = FALSE;
#endif
PAGED_CODE();
DebugTrace(+1, Dbg, "FatSingleNonAlignedAsync\n", 0);
DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb );
DebugTrace( 0, Dbg, "Buffer = %p\n", Buffer );
DebugTrace( 0, Dbg, "Lbo = %08lx\n", Lbo);
DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount);
DebugTrace( 0, Dbg, "Irp = %p\n", Irp );
//
// Create a new Mdl describing the buffer, saving the current one in the
// Irp
//
SavedMdl = Irp->MdlAddress;
Irp->MdlAddress = 0;
Mdl = IoAllocateMdl( Buffer,
ByteCount,
FALSE,
FALSE,
Irp );
if (Mdl == NULL) {
Irp->MdlAddress = SavedMdl;
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
}
//
// Lock the new Mdl in memory.
//
_SEH2_TRY {
MmProbeAndLockPages( Mdl, KernelMode, IoWriteAccess );
} _SEH2_FINALLY {
if ( _SEH2_AbnormalTermination() ) {
IoFreeMdl( Mdl );
Irp->MdlAddress = SavedMdl;
}
} _SEH2_END;
//
// Set up the completion routine address in our stack frame.
//
IoSetCompletionRoutine( Irp,
&FatSingleSyncCompletionRoutine,
IrpContext->FatIoContext,
TRUE,
TRUE,
TRUE );
//
// Setup the next IRP stack location in the associated Irp for the disk
// driver beneath us.
//
IrpSp = IoGetNextIrpStackLocation( Irp );
//
// Setup the Stack location to do a read from the disk driver.
//
IrpSp->MajorFunction = IrpContext->MajorFunction;
IrpSp->Parameters.Read.Length = ByteCount;
IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo;
#ifndef __REACTOS__
IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
#endif
//
// If this I/O originating during FatVerifyVolume, bypass the
// verify logic.
//
if (Vcb->VerifyThread == KeGetCurrentThread()) {
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY );
}
//
// If this I/O requires override verify, bypass the verify logic.
//
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) {
SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
}
//
// Issue the read request
//
DebugDoit( FatIoCallDriverCount += 1);
//
// If IoCallDriver returns an error, it has completed the Irp
// and the error will be caught by our completion routines
// and dealt with as a normal IO error.
//
_SEH2_TRY {
(VOID)FatLowLevelReadWrite( IrpContext,
Vcb->TargetDeviceObject,
Irp,
Vcb );
FatWaitSync( IrpContext );
} _SEH2_FINALLY {
MmUnlockPages( Mdl );
IoFreeMdl( Mdl );
Irp->MdlAddress = SavedMdl;
} _SEH2_END;
//
// We just issued an IO to the storage stack, update the counters indicating so.
//
if (FatDiskAccountingEnabled) {
FatUpdateIOCountersPCW( IsAWrite, ByteCount );
}
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatSingleNonAlignedSync -> VOID\n", 0);
return;
}
VOID
FatWaitSync (
IN PIRP_CONTEXT IrpContext
)
/*++
Routine Description:
This routine waits for one or more previously started I/O requests
from the above routines, by simply waiting on the event.
Arguments:
Return Value:
None
--*/
{
PAGED_CODE();
DebugTrace(+1, Dbg, "FatWaitSync, Context = %p\n", IrpContext->FatIoContext );
KeWaitForSingleObject( &IrpContext->FatIoContext->Wait.SyncEvent,
Executive, KernelMode, FALSE, NULL );
KeClearEvent( &IrpContext->FatIoContext->Wait.SyncEvent );
DebugTrace(-1, Dbg, "FatWaitSync -> VOID\n", 0 );
}
//
// Internal Support Routine
//
NTSTATUS
NTAPI
FatMultiSyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
)
/*++
Routine Description:
This is the completion routine for all reads and writes started via
FatRead/WriteMultipleAsynch. It must synchronize its operation for
multiprocessor environments with itself on all other processors, via
a spin lock found via the Context parameter.
The completion routine has the following responsibilities:
If the individual request was completed with an error, then
this completion routine must see if this is the first error
(essentially by Vbo), and if so it must correctly reduce the
byte count and remember the error status in the Context.
If the IrpCount goes to 1, then it sets the event in the Context
parameter to signal the caller that all of the asynch requests
are done.
Arguments:
DeviceObject - Pointer to the file system device object.
Irp - Pointer to the associated Irp which is being completed. (This
Irp will no longer be accessible after this routine returns.)
Contxt - The context parameter which was specified for all of
the multiple asynch I/O requests for this MasterIrp.
Return Value:
The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can
immediately complete the Master Irp without being in a race condition
with the IoCompleteRequest thread trying to decrement the IrpCount in
the Master Irp.
--*/
{
PFAT_IO_CONTEXT Context = Contxt;
PIRP MasterIrp = Context->MasterIrp;
DebugTrace(+1, Dbg, "FatMultiSyncCompletionRoutine, Context = %p\n", Context );
//
// If we got an error (or verify required), remember it in the Irp
//
if (!NT_SUCCESS( Irp->IoStatus.Status )) {
#if DBG
if(!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
DbgBreakPoint();
}
#endif
#ifdef SYSCACHE_COMPILE
DbgPrint( "FAT SYSCACHE: MultiSync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus );
#endif
MasterIrp->IoStatus = Irp->IoStatus;
}
NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 ));
//
// We must do this here since IoCompleteRequest won't get a chance
// on this associated Irp.
//
IoFreeMdl( Irp->MdlAddress );
IoFreeIrp( Irp );
if (InterlockedDecrement(&Context->IrpCount) == 0) {
FatDoCompletionZero( MasterIrp, Context );
KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE );
}
DebugTrace(-1, Dbg, "FatMultiSyncCompletionRoutine -> SUCCESS\n", 0 );
UNREFERENCED_PARAMETER( DeviceObject );
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Internal Support Routine
//
NTSTATUS
NTAPI
FatMultiAsyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
)
/*++
Routine Description:
This is the completion routine for all reads and writes started via
FatRead/WriteMultipleAsynch. It must synchronize its operation for
multiprocessor environments with itself on all other processors, via
a spin lock found via the Context parameter.
The completion routine has has the following responsibilities:
If the individual request was completed with an error, then
this completion routine must see if this is the first error
(essentially by Vbo), and if so it must correctly reduce the
byte count and remember the error status in the Context.
If the IrpCount goes to 1, then it sets the event in the Context
parameter to signal the caller that all of the asynch requests
are done.
Arguments:
DeviceObject - Pointer to the file system device object.
Irp - Pointer to the associated Irp which is being completed. (This
Irp will no longer be accessible after this routine returns.)
Contxt - The context parameter which was specified for all of
the multiple asynch I/O requests for this MasterIrp.
Return Value:
The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can
immediately complete the Master Irp without being in a race condition
with the IoCompleteRequest thread trying to decrement the IrpCount in
the Master Irp.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PFAT_IO_CONTEXT Context = Contxt;
PIRP MasterIrp = Context->MasterIrp;
BOOLEAN PostRequest = FALSE;
DebugTrace(+1, Dbg, "FatMultiAsyncCompletionRoutine, Context = %p\n", Context );
//
// If we got an error (or verify required), remember it in the Irp
//
if (!NT_SUCCESS( Irp->IoStatus.Status )) {
#if DBG
if (!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
DbgBreakPoint();
}
#endif
#ifdef SYSCACHE_COMPILE
DbgPrint( "FAT SYSCACHE: MultiAsync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus );
#endif
MasterIrp->IoStatus = Irp->IoStatus;
}
NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 ));
if (InterlockedDecrement(&Context->IrpCount) == 0) {
FatDoCompletionZero( MasterIrp, Context );
if (NT_SUCCESS(MasterIrp->IoStatus.Status)) {
MasterIrp->IoStatus.Information =
Context->Wait.Async.RequestedByteCount;
NT_ASSERT(MasterIrp->IoStatus.Information != 0);
//
// Now if this wasn't PagingIo, set either the read or write bit.
//
if (!FlagOn(MasterIrp->Flags, IRP_PAGING_IO)) {
SetFlag( Context->Wait.Async.FileObject->Flags,
IoGetCurrentIrpStackLocation(MasterIrp)->MajorFunction == IRP_MJ_READ ?
FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED );
}
} else {
//
// Post STATUS_VERIFY_REQUIRED failures. Only post top level IRPs, because recursive I/Os
// cannot process volume verification.
//
if (!FlagOn(Context->IrpContextFlags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) &&
(MasterIrp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
PostRequest = TRUE;
}
}
//
// If this was a special async write, decrement the count. Set the
// event if this was the final outstanding I/O for the file. We will
// also want to queue an APC to deal with any error conditionions.
//
_Analysis_assume_(!(Context->Wait.Async.NonPagedFcb) &&
(ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
0xffffffff,
&FatData.GeneralSpinLock ) != 1));
if ((Context->Wait.Async.NonPagedFcb) &&
(ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
0xffffffff,
&FatData.GeneralSpinLock ) == 1)) {
KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE );
}
//
// Now release the resources.
//
if (Context->Wait.Async.Resource != NULL) {
ExReleaseResourceForThreadLite( Context->Wait.Async.Resource,
Context->Wait.Async.ResourceThreadId );
}
if (Context->Wait.Async.Resource2 != NULL) {
ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2,
Context->Wait.Async.ResourceThreadId );
}
//
// Mark the master Irp pending
//
IoMarkIrpPending( MasterIrp );
//
// and finally, free the context record.
//
ExFreePool( Context );
if (PostRequest) {
PIRP_CONTEXT IrpContext = NULL;
_SEH2_TRY {
IrpContext = FatCreateIrpContext(Irp, TRUE );
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL);
FatFsdPostRequest( IrpContext, Irp );
Status = STATUS_MORE_PROCESSING_REQUIRED;
} _SEH2_EXCEPT( FatExceptionFilter(NULL, _SEH2_GetExceptionInformation()) ) {
//
// If we failed to post the IRP, we just have to return the failure
// to the user. :(
//
NOTHING;
} _SEH2_END;
}
}
DebugTrace(-1, Dbg, "FatMultiAsyncCompletionRoutine -> SUCCESS\n", 0 );
UNREFERENCED_PARAMETER( DeviceObject );
return Status;
}
NTSTATUS
FatPagingFileErrorHandler (
IN PIRP Irp,
IN PKEVENT Event OPTIONAL
)
/*++
Routine Description:
This routine attempts to guarantee that the media is marked dirty
with the surface test bit if a paging file IO fails.
The work done here has several basic problems
1) when paging file writes start failing, this is a good sign
that the rest of the system is about to fall down around us
2) it has no forward progress guarantee
With Whistler, it is actually quite intentional that we're rejiggering
the paging file write path to make forward progress at all times. This
means that the cases where it *does* fail, we're truly seeing media errors
and this is probably going to mean the paging file is going to stop working
very soon.
It'd be nice to make this guarantee progress. It would need
1) a guaranteed worker thread which can only be used by items which
will make forward progress (i.e., not block out this one)
2) the virtual volume file's pages containing the boot sector and
1st FAT entry would have to be pinned resident and have a guaranteed
mapping address
3) mark volume would have to have a stashed irp/mdl and roll the write
irp, or use a generalized mechanism to guarantee issue of the irp
4) the lower stack would have to guarantee progress
Of these, 1 and 4 may actually exist shortly.
Arguments:
Irp - Pointer to the associated Irp which is being failed.
Event - Pointer to optional event to be signalled instead of completing
the IRP
Return Value:
Returns STATUS_MORE_PROCESSING_REQUIRED if we managed to queue off the workitem,
STATUS_SUCCESS otherwise.
--*/
{
NTSTATUS Status;
//
// If this was a media error, we want to chkdsk /r the next time we boot.
//
if (FsRtlIsTotalDeviceFailure(Irp->IoStatus.Status)) {
Status = STATUS_SUCCESS;
} else {
PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
//
// We are going to try to mark the volume needing recover.
// If we can't get pool, oh well....
//
Packet = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET), ' taF');
if ( Packet ) {
Packet->Vcb = &((PVOLUME_DEVICE_OBJECT)IoGetCurrentIrpStackLocation(Irp)->DeviceObject)->Vcb;
Packet->Irp = Irp;
Packet->Event = Event;
ExInitializeWorkItem( &Packet->Item,
&FatFspMarkVolumeDirtyWithRecover,
Packet );
#ifdef _MSC_VER
#pragma prefast( suppress:28159, "prefast indicates this is obsolete, but it is ok for fastfat to use it" )
#endif
ExQueueWorkItem( &Packet->Item, CriticalWorkQueue );
Status = STATUS_MORE_PROCESSING_REQUIRED;
} else {
Status = STATUS_SUCCESS;
}
}
return Status;
}
//
// Internal Support Routine
//
NTSTATUS
NTAPI
FatPagingFileCompletionRoutineCatch (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
)
/*++
Routine Description:
This is the completion routine for all reads and writes started via
FatPagingFileIo that reuse the master irp (that we have to catch
on the way back). It is always invoked.
The completion routine has has the following responsibility:
If the error implies a media problem, it enqueues a
worker item to write out the dirty bit so that the next
time we run we will do a autochk /r. This is not forward
progress guaranteed at the moment.
Clean up the Mdl used for this partial request.
Note that if the Irp is failing, the error code is already where
we want it.
Arguments:
DeviceObject - Pointer to the file system device object.
Irp - Pointer to the associated Irp which is being completed. (This
Irp will no longer be accessible after this routine returns.)
MasterIrp - Pointer to the master Irp.
Return Value:
Always returns STATUS_MORE_PROCESSING_REQUIRED.
--*/
{
PFAT_PAGING_FILE_CONTEXT Context = (PFAT_PAGING_FILE_CONTEXT) Contxt;
UNREFERENCED_PARAMETER( DeviceObject );
DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutineCatch, Context = %p\n", Context );
//
// Cleanup the existing Mdl, perhaps by returning the reserve.
//
if (Irp->MdlAddress == FatReserveMdl) {
MmPrepareMdlForReuse( Irp->MdlAddress );
KeSetEvent( &FatReserveEvent, 0, FALSE );
} else {
IoFreeMdl( Irp->MdlAddress );
}
//
// Restore the original Mdl.
//
Irp->MdlAddress = Context->RestoreMdl;
DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 );
//
// If the IRP is succeeding or the failure handler did not post off the
// completion, we're done and should set the event to let the master
// know the IRP is his again.
//
if (NT_SUCCESS( Irp->IoStatus.Status ) ||
FatPagingFileErrorHandler( Irp, &Context->Event ) == STATUS_SUCCESS) {
KeSetEvent( &Context->Event, 0, FALSE );
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Internal Support Routine
//
NTSTATUS
NTAPI
FatPagingFileCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID MasterIrp
)
/*++
Routine Description:
This is the completion routine for all reads and writes started via
FatPagingFileIo. It should only be invoked on error or cancel.
The completion routine has has the following responsibility:
Since the individual request was completed with an error,
this completion routine must stuff it into the master irp.
If the error implies a media problem, it also enqueues a
worker item to write out the dirty bit so that the next
time we run we will do a autochk /r
Arguments:
DeviceObject - Pointer to the file system device object.
Irp - Pointer to the associated Irp which is being completed. (This
Irp will no longer be accessible after this routine returns.)
MasterIrp - Pointer to the master Irp.
Return Value:
Always returns STATUS_SUCCESS.
--*/
{
DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutine, MasterIrp = %p\n", MasterIrp );
//
// If we got an error (or verify required), remember it in the Irp
//
NT_ASSERT( !NT_SUCCESS( Irp->IoStatus.Status ));
//
// If we were invoked with an assoicated Irp, copy the error over.
//
if (Irp != MasterIrp) {
((PIRP)MasterIrp)->IoStatus = Irp->IoStatus;
}
DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 );
UNREFERENCED_PARAMETER( DeviceObject );
return FatPagingFileErrorHandler( Irp, NULL );
}
//
// Internal Support Routine
//
NTSTATUS
NTAPI
FatSpecialSyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
)
/*++
Routine Description:
This is the completion routine for a special set of sub irps
that have to work at APC level.
The completion routine has has the following responsibilities:
It sets the event passed as the context to signal that the
request is done.
By doing this, the caller will be released before final APC
completion with knowledge that the IRP is finished. Final
completion will occur at an indeterminate time after this
occurs, and by using this completion routine the caller expects
to not have any output or status returned. A junk user Iosb
should be used to capture the status without forcing Io to take
an exception on NULL.
Arguments:
DeviceObject - Pointer to the file system device object.
Irp - Pointer to the Irp for this request. (This Irp will no longer
be accessible after this routine returns.)
Contxt - The context parameter which was specified in the call to
FatRead/WriteSingleAsynch.
Return Value:
Currently always returns STATUS_SUCCESS.
--*/
{
PFAT_SYNC_CONTEXT SyncContext = (PFAT_SYNC_CONTEXT)Contxt;
UNREFERENCED_PARAMETER( Irp );
DebugTrace(+1, Dbg, "FatSpecialSyncCompletionRoutine, Context = %p\n", Contxt );
SyncContext->Iosb = Irp->IoStatus;
KeSetEvent( &SyncContext->Event, 0, FALSE );
DebugTrace(-1, Dbg, "FatSpecialSyncCompletionRoutine -> STATUS_SUCCESS\n", 0 );
UNREFERENCED_PARAMETER( DeviceObject );
return STATUS_SUCCESS;
}
//
// Internal Support Routine
//
NTSTATUS
NTAPI
FatSingleSyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
)
/*++
Routine Description:
This is the completion routine for all reads and writes started via
FatRead/WriteSingleAsynch.
The completion routine has has the following responsibilities:
Copy the I/O status from the Irp to the Context, since the Irp
will no longer be accessible.
It sets the event in the Context parameter to signal the caller
that all of the asynch requests are done.
Arguments:
DeviceObject - Pointer to the file system device object.
Irp - Pointer to the Irp for this request. (This Irp will no longer
be accessible after this routine returns.)
Contxt - The context parameter which was specified in the call to
FatRead/WriteSingleAsynch.
Return Value:
Currently always returns STATUS_SUCCESS.
--*/
{
PFAT_IO_CONTEXT Context = Contxt;
DebugTrace(+1, Dbg, "FatSingleSyncCompletionRoutine, Context = %p\n", Context );
FatDoCompletionZero( Irp, Context );
if (!NT_SUCCESS( Irp->IoStatus.Status )) {
#if DBG
if(!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
DbgBreakPoint();
}
#endif
}
NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 ));
KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE );
DebugTrace(-1, Dbg, "FatSingleSyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 );
UNREFERENCED_PARAMETER( DeviceObject );
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Internal Support Routine
//
NTSTATUS
NTAPI
FatSingleAsyncCompletionRoutine (
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
)
/*++
Routine Description:
This is the completion routine for all reads and writes started via
FatRead/WriteSingleAsynch.
The completion routine has has the following responsibilities:
Copy the I/O status from the Irp to the Context, since the Irp
will no longer be accessible.
It sets the event in the Context parameter to signal the caller
that all of the asynch requests are done.
Arguments:
DeviceObject - Pointer to the file system device object.
Irp - Pointer to the Irp for this request. (This Irp will no longer
be accessible after this routine returns.)
Contxt - The context parameter which was specified in the call to
FatRead/WriteSingleAsynch.
Return Value:
Currently always returns STATUS_SUCCESS.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PFAT_IO_CONTEXT Context = Contxt;
BOOLEAN PostRequest = FALSE;
DebugTrace(+1, Dbg, "FatSingleAsyncCompletionRoutine, Context = %p\n", Context );
//
// Fill in the information field correctedly if this worked.
//
FatDoCompletionZero( Irp, Context );
if (NT_SUCCESS(Irp->IoStatus.Status)) {
NT_ASSERT( Irp->IoStatus.Information != 0 );
Irp->IoStatus.Information = Context->Wait.Async.RequestedByteCount;
NT_ASSERT( Irp->IoStatus.Information != 0 );
//
// Now if this wasn't PagingIo, set either the read or write bit.
//
if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) {
SetFlag( Context->Wait.Async.FileObject->Flags,
IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_READ ?
FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED );
}
} else {
#if DBG
if(!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
DbgBreakPoint();
}
#endif
#ifdef SYSCACHE_COMPILE
DbgPrint( "FAT SYSCACHE: SingleAsync (IRP %08x) -> %08x\n", Irp, Irp->IoStatus );
#endif
//
// Post STATUS_VERIFY_REQUIRED failures. Only post top level IRPs, because recursive I/Os
// cannot process volume verification.
//
if (!FlagOn(Context->IrpContextFlags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) &&
(Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
PostRequest = TRUE;
}
}
//
// If this was a special async write, decrement the count. Set the
// event if this was the final outstanding I/O for the file. We will
// also want to queue an APC to deal with any error conditionions.
//
_Analysis_assume_(!(Context->Wait.Async.NonPagedFcb) &&
(ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
0xffffffff,
&FatData.GeneralSpinLock ) != 1));
if ((Context->Wait.Async.NonPagedFcb) &&
(ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
0xffffffff,
&FatData.GeneralSpinLock ) == 1)) {
KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE );
}
//
// Now release the resources
//
if (Context->Wait.Async.Resource != NULL) {
ExReleaseResourceForThreadLite( Context->Wait.Async.Resource,
Context->Wait.Async.ResourceThreadId );
}
if (Context->Wait.Async.Resource2 != NULL) {
ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2,
Context->Wait.Async.ResourceThreadId );
}
//
// Mark the Irp pending
//
IoMarkIrpPending( Irp );
//
// and finally, free the context record.
//
ExFreePool( Context );
if (PostRequest) {
PIRP_CONTEXT IrpContext = NULL;
_SEH2_TRY {
IrpContext = FatCreateIrpContext(Irp, TRUE );
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL);
FatFsdPostRequest( IrpContext, Irp );
Status = STATUS_MORE_PROCESSING_REQUIRED;
} _SEH2_EXCEPT( FatExceptionFilter(NULL, _SEH2_GetExceptionInformation()) ) {
//
// If we failed to post the IRP, we just have to return the failure
// to the user. :(
//
NOTHING;
} _SEH2_END;
}
DebugTrace(-1, Dbg, "FatSingleAsyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 );
UNREFERENCED_PARAMETER( DeviceObject );
return Status;
}
VOID
FatLockUserBuffer (
IN PIRP_CONTEXT IrpContext,
IN OUT PIRP Irp,
IN LOCK_OPERATION Operation,
IN ULONG BufferLength
)
/*++
Routine Description:
This routine locks the specified buffer for the specified type of
access. The file system requires this routine since it does not
ask the I/O system to lock its buffers for direct I/O. This routine
may only be called from the Fsd while still in the user context.
Note that this is the *input/output* buffer.
Arguments:
Irp - Pointer to the Irp for which the buffer is to be locked.
Operation - IoWriteAccess for read operations, or IoReadAccess for
write operations.
BufferLength - Length of user buffer.
Return Value:
None
--*/
{
PMDL Mdl = NULL;
PAGED_CODE();
if (Irp->MdlAddress == NULL) {
//
// Allocate the Mdl, and Raise if we fail.
//
Mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp );
if (Mdl == NULL) {
FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
}
//
// Now probe the buffer described by the Irp. If we get an exception,
// deallocate the Mdl and return the appropriate "expected" status.
//
_SEH2_TRY {
MmProbeAndLockPages( Mdl,
Irp->RequestorMode,
Operation );
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
NTSTATUS Status;
Status = _SEH2_GetExceptionCode();
IoFreeMdl( Mdl );
Irp->MdlAddress = NULL;
FatRaiseStatus( IrpContext,
FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER );
} _SEH2_END;
}
UNREFERENCED_PARAMETER( IrpContext );
}
PVOID
FatMapUserBuffer (
IN PIRP_CONTEXT IrpContext,
IN OUT PIRP Irp
)
/*++
Routine Description:
This routine conditionally maps the user buffer for the current I/O
request in the specified mode. If the buffer is already mapped, it
just returns its address.
Note that this is the *input/output* buffer.
Arguments:
Irp - Pointer to the Irp for the request.
Return Value:
Mapped address
--*/
{
UNREFERENCED_PARAMETER( IrpContext );
PAGED_CODE();
//
// If there is no Mdl, then we must be in the Fsd, and we can simply
// return the UserBuffer field from the Irp.
//
if (Irp->MdlAddress == NULL) {
return Irp->UserBuffer;
} else {
PVOID Address = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority | MdlMappingNoExecute );
if (Address == NULL) {
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
}
return Address;
}
}
PVOID
FatBufferUserBuffer (
IN PIRP_CONTEXT IrpContext,
IN OUT PIRP Irp,
IN ULONG BufferLength
)
/*++
Routine Description:
This routine conditionally buffers the user buffer for the current I/O
request. If the buffer is already buffered, it just returns its address.
Note that this is the *input* buffer.
Arguments:
Irp - Pointer to the Irp for the request.
BufferLength - Length of user buffer.
Return Value:
Buffered address.
--*/
{
PUCHAR UserBuffer;
UNREFERENCED_PARAMETER( IrpContext );
PAGED_CODE();
//
// Handle the no buffer case.
//
if (BufferLength == 0) {
return NULL;
}
//
// If there is no system buffer we must have been supplied an Mdl
// describing the users input buffer, which we will now snapshot.
//
if (Irp->AssociatedIrp.SystemBuffer == NULL) {
UserBuffer = FatMapUserBuffer( IrpContext, Irp );
Irp->AssociatedIrp.SystemBuffer = FsRtlAllocatePoolWithQuotaTag( NonPagedPoolNx,
BufferLength,
TAG_IO_USER_BUFFER );
//
// Set the flags so that the completion code knows to deallocate the
// buffer.
//
Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
_SEH2_TRY {
RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer,
UserBuffer,
BufferLength );
} _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
NTSTATUS Status;
Status = _SEH2_GetExceptionCode();
FatRaiseStatus( IrpContext,
FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER );
} _SEH2_END;
}
return Irp->AssociatedIrp.SystemBuffer;
}
NTSTATUS
FatToggleMediaEjectDisable (
IN PIRP_CONTEXT IrpContext,
IN PVCB Vcb,
IN BOOLEAN PreventRemoval
)
/*++
Routine Description:
The routine either enables or disables the eject button on removable
media.
Arguments:
Vcb - Descibes the volume to operate on
PreventRemoval - TRUE if we should disable the media eject button. FALSE
if we want to enable it.
Return Value:
Status of the operation.
--*/
{
PIRP Irp;
KIRQL SavedIrql;
NTSTATUS Status;
FAT_SYNC_CONTEXT SyncContext;
PREVENT_MEDIA_REMOVAL Prevent;
UNREFERENCED_PARAMETER( IrpContext );
//
// If PreventRemoval is the same as VCB_STATE_FLAG_REMOVAL_PREVENTED,
// no-op this call, otherwise toggle the state of the flag.
//
KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql );
if ((PreventRemoval ^
BooleanFlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVAL_PREVENTED)) == 0) {
KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql );
return STATUS_SUCCESS;
} else {
Vcb->VcbState ^= VCB_STATE_FLAG_REMOVAL_PREVENTED;
KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql );
}
Prevent.PreventMediaRemoval = PreventRemoval;
KeInitializeEvent( &SyncContext.Event, NotificationEvent, FALSE );
//
// We build this IRP using a junk Iosb that will receive the final
// completion status since we won't be around for it.
//
// We fill in the UserIosb manually below,
// So passing NULL for the final parameter is ok in this special case.
//
#ifdef _MSC_VER
#pragma warning(suppress: 6387)
#endif
Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_MEDIA_REMOVAL,
Vcb->TargetDeviceObject,
&Prevent,
sizeof(PREVENT_MEDIA_REMOVAL),
NULL,
0,
FALSE,
NULL,
NULL );
if ( Irp != NULL ) {
//
// Use our special completion routine which will remove the requirement that
// the caller must be below APC level. All it tells us is that the Irp got
// back, but will not tell us if it was succesful or not. We don't care,
// and there is of course no fallback if the attempt to prevent removal
// doesn't work for some mysterious reason.
//
// Normally, all IO is done at passive level. However, MM needs to be able
// to issue IO with fast mutexes locked down, which raises us to APC. The
// overlying IRP is set up to complete in yet another magical fashion even
// though APCs are disabled, and any IRPage we do in these cases has to do
// the same. Marking media dirty (and toggling eject state) is one.
//
Irp->UserIosb = &Irp->IoStatus;
IoSetCompletionRoutine( Irp,
FatSpecialSyncCompletionRoutine,
&SyncContext,
TRUE,
TRUE,
TRUE );
Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
if (Status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &SyncContext.Event,
Executive,
KernelMode,
FALSE,
NULL );
Status = SyncContext.Iosb.Status;
}
return Status;
}
return STATUS_INSUFFICIENT_RESOURCES;
}
NTSTATUS
FatPerformDevIoCtrl (
IN PIRP_CONTEXT IrpContext,
IN ULONG IoControlCode,
IN PDEVICE_OBJECT Device,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength,
IN BOOLEAN InternalDeviceIoControl,
IN BOOLEAN OverrideVerify,
OUT PIO_STATUS_BLOCK Iosb OPTIONAL
)
/*++
Routine Description:
This routine is called to perform DevIoCtrl functions internally within
the filesystem. We take the status from the driver and return it to our
caller.
Arguments:
IoControlCode - Code to send to driver.
Device - This is the device to send the request to.
OutPutBuffer - Pointer to output buffer.
OutputBufferLength - Length of output buffer above.
InternalDeviceIoControl - Indicates if this is an internal or external
Io control code.
OverrideVerify - Indicates if we should tell the driver not to return
STATUS_VERIFY_REQUIRED for mount and verify.
Iosb - If specified, we return the results of the operation here.
Return Value:
NTSTATUS - Status returned by next lower driver.
--*/
{
NTSTATUS Status;
PIRP Irp;
KEVENT Event;
IO_STATUS_BLOCK LocalIosb;
PIO_STATUS_BLOCK IosbToUse = &LocalIosb;
PAGED_CODE();
UNREFERENCED_PARAMETER( IrpContext );
//
// Check if the user gave us an Iosb.
//
if (ARGUMENT_PRESENT( Iosb )) {
IosbToUse = Iosb;
}
IosbToUse->Status = 0;
IosbToUse->Information = 0;
KeInitializeEvent( &Event, NotificationEvent, FALSE );
Irp = IoBuildDeviceIoControlRequest( IoControlCode,
Device,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
InternalDeviceIoControl,
&Event,
IosbToUse );
if (Irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
if (OverrideVerify) {
SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
}
Status = IoCallDriver( Device, Irp );
//
// We check for device not ready by first checking Status
// and then if status pending was returned, the Iosb status
// value.
//
if (Status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &Event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL );
Status = IosbToUse->Status;
}
return Status;
}
PMDL
FatBuildZeroMdl (
__in PIRP_CONTEXT IrpContext,
__in ULONG Length
)
/*++
Routine Description:
Create an efficient mdl that describe a given length of zeros. We'll only
use a one page buffer and make a mdl that maps all the pages back to the single
physical page. We'll default to a smaller size buffer down to 1 PAGE if memory
is tight. The caller should check the Mdl->ByteCount to see the true size
Arguments:
Length - The desired length of the zero buffer. We may return less than this
Return Value:
a MDL if successful / NULL if not
--*/
{
PMDL ZeroMdl;
ULONG SavedByteCount;
PPFN_NUMBER Page;
ULONG i;
UNREFERENCED_PARAMETER( IrpContext );
//
// Spin down trying to get an MDL which can describe our operation.
//
while (TRUE) {
ZeroMdl = IoAllocateMdl( FatData.ZeroPage, Length, FALSE, FALSE, NULL );
//
// Throttle ourselves to what we've physically allocated. Note that
// we could have started with an odd multiple of this number. If we
// tried for exactly that size and failed, we're toast.
//
if (ZeroMdl || (Length <= PAGE_SIZE)) {
break;
}
//
// Fallback by half and round down to a page multiple.
//
ASSERT( IrpContext->Vcb->Bpb.BytesPerSector <= PAGE_SIZE );
Length = BlockAlignTruncate( Length / 2, PAGE_SIZE );
if (Length < PAGE_SIZE) {
Length = PAGE_SIZE;
}
}
if (ZeroMdl == NULL) {
return NULL;
}
//
// If we have throttled all the way down, stop and just build a
// simple MDL describing our previous allocation.
//
if (Length == PAGE_SIZE) {
MmBuildMdlForNonPagedPool( ZeroMdl );
return ZeroMdl;
}
//
// Now we will temporarily lock the allocated pages
// only, and then replicate the page frame numbers through
// the entire Mdl to keep writing the same pages of zeros.
//
// It would be nice if Mm exported a way for us to not have
// to pull the Mdl apart and rebuild it ourselves, but this
// is so bizarre a purpose as to be tolerable.
//
SavedByteCount = ZeroMdl->ByteCount;
ZeroMdl->ByteCount = PAGE_SIZE;
MmBuildMdlForNonPagedPool( ZeroMdl );
ZeroMdl->MdlFlags &= ~MDL_SOURCE_IS_NONPAGED_POOL;
ZeroMdl->MdlFlags |= MDL_PAGES_LOCKED;
ZeroMdl->MappedSystemVa = NULL;
ZeroMdl->StartVa = NULL;
ZeroMdl->ByteCount = SavedByteCount;
Page = MmGetMdlPfnArray( ZeroMdl );
for (i = 1; i < (ADDRESS_AND_SIZE_TO_SPAN_PAGES( 0, SavedByteCount )); i++) {
*(Page + i) = *(Page);
}
return ZeroMdl;
}