mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
1747 lines
48 KiB
C
1747 lines
48 KiB
C
/*++
|
|
|
|
Copyright (c) 1989-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Read.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the File Read routine for Read called by the
|
|
dispatch driver.
|
|
|
|
|
|
--*/
|
|
|
|
#include "fatprocs.h"
|
|
|
|
//
|
|
// The Bug check file id for this module
|
|
//
|
|
|
|
#define BugCheckFileId (FAT_BUG_CHECK_READ)
|
|
|
|
//
|
|
// The local debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_READ)
|
|
|
|
//
|
|
// Define stack overflow read threshhold. For the x86 we'll use a smaller
|
|
// threshold than for a risc platform.
|
|
//
|
|
// Empirically, the limit is a result of the (large) amount of stack
|
|
// neccesary to throw an exception.
|
|
//
|
|
|
|
#if defined(_M_IX86)
|
|
#define OVERFLOW_READ_THRESHHOLD (0xE00)
|
|
#else
|
|
#define OVERFLOW_READ_THRESHHOLD (0x1000)
|
|
#endif // defined(_M_IX86)
|
|
|
|
|
|
//
|
|
// The following procedures handles read stack overflow operations.
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
NTAPI
|
|
FatStackOverflowRead (
|
|
IN PVOID Context,
|
|
IN PKEVENT Event
|
|
);
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
FatPostStackOverflowRead (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PFCB Fcb
|
|
);
|
|
|
|
VOID
|
|
NTAPI
|
|
FatOverflowPagingFileRead (
|
|
IN PVOID Context,
|
|
IN PKEVENT Event
|
|
);
|
|
|
|
//
|
|
// VOID
|
|
// SafeZeroMemory (
|
|
// IN PUCHAR At,
|
|
// IN ULONG ByteCount
|
|
// );
|
|
//
|
|
|
|
//
|
|
// This macro just puts a nice little try-except around RtlZeroMemory
|
|
//
|
|
|
|
#define SafeZeroMemory(AT,BYTE_COUNT) { \
|
|
_SEH2_TRY { \
|
|
RtlZeroMemory((AT), (BYTE_COUNT)); \
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
|
|
FatRaiseStatus( IrpContext, STATUS_INVALID_USER_BUFFER ); \
|
|
} _SEH2_END \
|
|
}
|
|
|
|
//
|
|
// Macro to increment appropriate performance counters.
|
|
//
|
|
|
|
#define CollectReadStats(VCB,OPEN_TYPE,BYTE_COUNT) { \
|
|
PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common; \
|
|
if (((OPEN_TYPE) == UserFileOpen)) { \
|
|
Stats->UserFileReads += 1; \
|
|
Stats->UserFileReadBytes += (ULONG)(BYTE_COUNT); \
|
|
} else if (((OPEN_TYPE) == VirtualVolumeFile || ((OPEN_TYPE) == DirectoryFile))) { \
|
|
Stats->MetaDataReads += 1; \
|
|
Stats->MetaDataReadBytes += (ULONG)(BYTE_COUNT); \
|
|
} \
|
|
}
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FatStackOverflowRead)
|
|
#pragma alloc_text(PAGE, FatPostStackOverflowRead)
|
|
#pragma alloc_text(PAGE, FatCommonRead)
|
|
#endif
|
|
|
|
|
|
_Function_class_(IRP_MJ_READ)
|
|
_Function_class_(DRIVER_DISPATCH)
|
|
NTSTATUS
|
|
NTAPI
|
|
FatFsdRead (
|
|
_In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
|
_Inout_ PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the driver entry to the common read routine for NtReadFile calls.
|
|
For synchronous requests, the CommonRead is called with Wait == TRUE,
|
|
which means the request will always be completed in the current thread,
|
|
and never passed to the Fsp. If it is not a synchronous request,
|
|
CommonRead is called with Wait == FALSE, which means the request
|
|
will be passed to the Fsp only if there is a need to block.
|
|
|
|
Arguments:
|
|
|
|
VolumeDeviceObject - Supplies the volume device object where the
|
|
file being Read exists
|
|
|
|
Irp - Supplies the Irp being processed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The FSD status for the IRP
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB Fcb = NULL;
|
|
NTSTATUS Status;
|
|
PIRP_CONTEXT IrpContext = NULL;
|
|
|
|
BOOLEAN TopLevel = FALSE;
|
|
|
|
DebugTrace(+1, Dbg, "FatFsdRead\n", 0);
|
|
|
|
//
|
|
// Call the common Read routine, with blocking allowed if synchronous
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
//
|
|
// We are first going to do a quick check for paging file IO. Since this
|
|
// is a fast path, we must replicate the check for the fsdo.
|
|
//
|
|
|
|
if (!FatDeviceIsFatFsdo( IoGetCurrentIrpStackLocation(Irp)->DeviceObject)) {
|
|
|
|
Fcb = (PFCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext);
|
|
|
|
if ((NodeType(Fcb) == FAT_NTC_FCB) &&
|
|
FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
|
|
|
|
//
|
|
// Do the usual STATUS_PENDING things.
|
|
//
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
//
|
|
// If there is not enough stack to do this read, then post this
|
|
// read to the overflow queue.
|
|
//
|
|
|
|
if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) {
|
|
|
|
KEVENT Event;
|
|
PAGING_FILE_OVERFLOW_PACKET Packet;
|
|
|
|
Packet.Irp = Irp;
|
|
Packet.Fcb = Fcb;
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
FsRtlPostPagingFileStackOverflow( &Packet, &Event, FatOverflowPagingFileRead );
|
|
|
|
//
|
|
// And wait for the worker thread to complete the item
|
|
//
|
|
|
|
(VOID) KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Perform the actual IO, it will be completed when the io finishes.
|
|
//
|
|
|
|
FatPagingFileIo( Irp, Fcb );
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
_SEH2_TRY {
|
|
|
|
TopLevel = FatIsIrpTopLevel( Irp );
|
|
|
|
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
|
|
|
//
|
|
// If this is an Mdl complete request, don't go through
|
|
// common read.
|
|
//
|
|
|
|
if ( FlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE) ) {
|
|
|
|
DebugTrace(0, Dbg, "Calling FatCompleteMdl\n", 0 );
|
|
try_return( Status = FatCompleteMdl( IrpContext, Irp ));
|
|
}
|
|
|
|
//
|
|
// Check if we have enough stack space to process this request. If there
|
|
// isn't enough then we will pass the request off to the stack overflow thread.
|
|
//
|
|
|
|
if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) {
|
|
|
|
DebugTrace(0, Dbg, "Passing StackOverflowRead off\n", 0 );
|
|
try_return( Status = FatPostStackOverflowRead( IrpContext, Irp, Fcb ) );
|
|
}
|
|
|
|
Status = FatCommonRead( IrpContext, Irp );
|
|
|
|
try_exit: NOTHING;
|
|
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
|
|
|
//
|
|
// We had some trouble trying to perform the requested
|
|
// operation, 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;
|
|
|
|
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
DebugTrace(-1, Dbg, "FatFsdRead -> %08lx\n", Status);
|
|
|
|
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
FatPostStackOverflowRead (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PFCB Fcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine posts a read request that could not be processed by
|
|
the fsp thread because of stack overflow potential.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the request to process.
|
|
|
|
Fcb - Supplies the file.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT Event;
|
|
PERESOURCE Resource;
|
|
PVCB Vcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(0, Dbg, "Getting too close to stack limit pass request to Fsp\n", 0 );
|
|
|
|
//
|
|
// Initialize an event and get shared on the resource we will
|
|
// be later using the common read.
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// Preacquire the resource the read path will require so we know the
|
|
// worker thread can proceed without waiting.
|
|
//
|
|
|
|
if (FlagOn(Irp->Flags, IRP_PAGING_IO) && (Fcb->Header.PagingIoResource != NULL)) {
|
|
|
|
Resource = Fcb->Header.PagingIoResource;
|
|
|
|
} else {
|
|
|
|
Resource = Fcb->Header.Resource;
|
|
}
|
|
|
|
//
|
|
// If there are no resources assodicated with the file (case: the virtual
|
|
// volume file), it is OK. No resources will be acquired on the other side
|
|
// as well.
|
|
//
|
|
|
|
if (Resource) {
|
|
|
|
ExAcquireResourceSharedLite( Resource, TRUE );
|
|
}
|
|
|
|
if (NodeType( Fcb ) == FAT_NTC_VCB) {
|
|
|
|
Vcb = (PVCB) Fcb;
|
|
|
|
} else {
|
|
|
|
Vcb = Fcb->Vcb;
|
|
}
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// Make the Irp just like a regular post request and
|
|
// then send the Irp to the special overflow thread.
|
|
// After the post we will wait for the stack overflow
|
|
// read routine to set the event that indicates we can
|
|
// now release the scb resource and return.
|
|
//
|
|
|
|
FatPrePostIrp( IrpContext, Irp );
|
|
|
|
//
|
|
// If this read is the result of a verify, we have to
|
|
// tell the overflow read routne to temporarily
|
|
// hijack the Vcb->VerifyThread field so that reads
|
|
// can go through.
|
|
//
|
|
|
|
if (Vcb->VerifyThread == KeGetCurrentThread()) {
|
|
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ);
|
|
}
|
|
|
|
FsRtlPostStackOverflow( IrpContext, &Event, FatStackOverflowRead );
|
|
|
|
//
|
|
// And wait for the worker thread to complete the item
|
|
//
|
|
|
|
KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
if (Resource) {
|
|
|
|
ExReleaseResourceLite( Resource );
|
|
}
|
|
} _SEH2_END;
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
VOID
|
|
NTAPI
|
|
FatStackOverflowRead (
|
|
IN PVOID Context,
|
|
IN PKEVENT Event
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes a read request that could not be processed by
|
|
the fsp thread because of stack overflow potential.
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies the IrpContext being processed
|
|
|
|
Event - Supplies the event to be signaled when we are done processing this
|
|
request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP_CONTEXT IrpContext = Context;
|
|
PKTHREAD SavedVerifyThread = NULL;
|
|
PVCB Vcb = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make it now look like we can wait for I/O to complete
|
|
//
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
|
|
|
|
//
|
|
// If this read was as the result of a verify we have to fake out the
|
|
// the Vcb->VerifyThread field.
|
|
//
|
|
|
|
if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ)) {
|
|
|
|
PFCB Fcb = (PFCB)IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->
|
|
FileObject->FsContext;
|
|
|
|
if (NodeType( Fcb ) == FAT_NTC_VCB) {
|
|
|
|
Vcb = (PVCB) Fcb;
|
|
|
|
} else {
|
|
|
|
Vcb = Fcb->Vcb;
|
|
}
|
|
|
|
NT_ASSERT( Vcb->VerifyThread != NULL );
|
|
SavedVerifyThread = Vcb->VerifyThread;
|
|
Vcb->VerifyThread = KeGetCurrentThread();
|
|
}
|
|
|
|
//
|
|
// Do the read operation protected by a try-except clause
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
(VOID) FatCommonRead( IrpContext, IrpContext->OriginatingIrp );
|
|
|
|
} _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
|
|
|
|
NTSTATUS ExceptionCode;
|
|
|
|
//
|
|
// We had some trouble trying to perform the requested
|
|
// operation, so we'll abort the I/O request with
|
|
// the error status that we get back from the
|
|
// execption code
|
|
//
|
|
|
|
ExceptionCode = _SEH2_GetExceptionCode();
|
|
|
|
if (ExceptionCode == STATUS_FILE_DELETED) {
|
|
|
|
IrpContext->ExceptionStatus = ExceptionCode = STATUS_END_OF_FILE;
|
|
IrpContext->OriginatingIrp->IoStatus.Information = 0;
|
|
}
|
|
|
|
(VOID) FatProcessException( IrpContext, IrpContext->OriginatingIrp, ExceptionCode );
|
|
} _SEH2_END;
|
|
|
|
//
|
|
// Restore the original VerifyVolumeThread
|
|
//
|
|
|
|
if (SavedVerifyThread != NULL) {
|
|
|
|
NT_ASSERT( Vcb->VerifyThread == KeGetCurrentThread() );
|
|
Vcb->VerifyThread = SavedVerifyThread;
|
|
}
|
|
|
|
//
|
|
// Set the stack overflow item's event to tell the original
|
|
// thread that we're done.
|
|
//
|
|
|
|
KeSetEvent( Event, 0, FALSE );
|
|
}
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
FatCommonRead (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the common read routine for NtReadFile, called from both
|
|
the Fsd, or from the Fsp if a request could not be completed without
|
|
blocking in the Fsd. This routine has no code where it determines
|
|
whether it is running in the Fsd or Fsp. Instead, its actions are
|
|
conditionalized by the Wait input parameter, which determines whether
|
|
it is allowed to block or not. If a blocking condition is encountered
|
|
with Wait == FALSE, however, the request is posted to the Fsp, who
|
|
always calls with WAIT == TRUE.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to process
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
PVCB Vcb;
|
|
PFCB FcbOrDcb;
|
|
PCCB Ccb;
|
|
|
|
VBO StartingVbo;
|
|
ULONG ByteCount;
|
|
ULONG RequestedByteCount;
|
|
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PFILE_OBJECT FileObject;
|
|
TYPE_OF_OPEN TypeOfOpen;
|
|
|
|
BOOLEAN PostIrp = FALSE;
|
|
BOOLEAN OplockPostIrp = FALSE;
|
|
|
|
BOOLEAN FcbOrDcbAcquired = FALSE;
|
|
|
|
BOOLEAN Wait;
|
|
BOOLEAN PagingIo;
|
|
BOOLEAN NonCachedIo;
|
|
BOOLEAN SynchronousIo;
|
|
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
FAT_IO_CONTEXT StackFatIoContext;
|
|
|
|
//
|
|
// A system buffer is only used if we have to access the
|
|
// buffer directly from the Fsp to clear a portion or to
|
|
// do a synchronous I/O, or a cached transfer. It is
|
|
// possible that our caller may have already mapped a
|
|
// system buffer, in which case we must remember this so
|
|
// we do not unmap it on the way out.
|
|
//
|
|
|
|
PVOID SystemBuffer = NULL;
|
|
|
|
LARGE_INTEGER StartingByte;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get current Irp stack location.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
FileObject = IrpSp->FileObject;
|
|
|
|
//
|
|
// Initialize the appropriate local variables.
|
|
//
|
|
|
|
Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
|
|
NonCachedIo = BooleanFlagOn(Irp->Flags,IRP_NOCACHE);
|
|
SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
|
|
|
|
DebugTrace(+1, Dbg, "CommonRead\n", 0);
|
|
DebugTrace( 0, Dbg, " Irp = %p\n", Irp);
|
|
DebugTrace( 0, Dbg, " ->ByteCount = %8lx\n", IrpSp->Parameters.Read.Length);
|
|
DebugTrace( 0, Dbg, " ->ByteOffset.LowPart = %8lx\n", IrpSp->Parameters.Read.ByteOffset.LowPart);
|
|
DebugTrace( 0, Dbg, " ->ByteOffset.HighPart = %8lx\n", IrpSp->Parameters.Read.ByteOffset.HighPart);
|
|
|
|
//
|
|
// Extract starting Vbo and offset.
|
|
//
|
|
|
|
StartingByte = IrpSp->Parameters.Read.ByteOffset;
|
|
|
|
StartingVbo = StartingByte.LowPart;
|
|
|
|
ByteCount = IrpSp->Parameters.Read.Length;
|
|
RequestedByteCount = ByteCount;
|
|
|
|
//
|
|
// Check for a null request, and return immediately
|
|
//
|
|
|
|
if (ByteCount == 0) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Extract the nature of the read from the file object, and case on it
|
|
//
|
|
|
|
TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &FcbOrDcb, &Ccb);
|
|
|
|
NT_ASSERT( Vcb != NULL );
|
|
|
|
//
|
|
// Save callers who try to do cached IO to the raw volume from themselves.
|
|
//
|
|
|
|
if (TypeOfOpen == UserVolumeOpen) {
|
|
|
|
NonCachedIo = TRUE;
|
|
}
|
|
|
|
NT_ASSERT(!(NonCachedIo == FALSE && TypeOfOpen == VirtualVolumeFile));
|
|
|
|
//
|
|
// Collect interesting statistics. The FLAG_USER_IO bit will indicate
|
|
// what type of io we're doing in the FatNonCachedIo function.
|
|
//
|
|
|
|
if (PagingIo) {
|
|
CollectReadStats(Vcb, TypeOfOpen, ByteCount);
|
|
|
|
if (TypeOfOpen == UserFileOpen) {
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO);
|
|
} else {
|
|
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO);
|
|
}
|
|
}
|
|
|
|
NT_ASSERT(!FlagOn( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT ));
|
|
|
|
//
|
|
// Allocate if necessary and initialize a FAT_IO_CONTEXT block for
|
|
// all non cached Io. For synchronous Io we use stack storage,
|
|
// otherwise we allocate pool.
|
|
//
|
|
|
|
if (NonCachedIo) {
|
|
|
|
if (IrpContext->FatIoContext == NULL) {
|
|
|
|
if (!Wait) {
|
|
|
|
IrpContext->FatIoContext =
|
|
FsRtlAllocatePoolWithTag( NonPagedPoolNx,
|
|
sizeof(FAT_IO_CONTEXT),
|
|
TAG_FAT_IO_CONTEXT );
|
|
|
|
} else {
|
|
|
|
IrpContext->FatIoContext = &StackFatIoContext;
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT );
|
|
}
|
|
}
|
|
|
|
RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) );
|
|
|
|
if (Wait) {
|
|
|
|
KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
} else {
|
|
|
|
if (PagingIo) {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.ResourceThreadId =
|
|
ExGetCurrentResourceThread();
|
|
|
|
} else {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.ResourceThreadId =
|
|
((ULONG_PTR)IrpContext->FatIoContext) | 3;
|
|
}
|
|
|
|
IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
|
|
ByteCount;
|
|
|
|
IrpContext->FatIoContext->Wait.Async.FileObject = FileObject;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// These two cases correspond to either a general opened volume, ie.
|
|
// open ("a:"), or a read of the volume file (boot sector + fat(s))
|
|
//
|
|
|
|
if ((TypeOfOpen == VirtualVolumeFile) ||
|
|
(TypeOfOpen == UserVolumeOpen)) {
|
|
|
|
LBO StartingLbo;
|
|
|
|
StartingLbo = StartingByte.QuadPart;
|
|
|
|
DebugTrace(0, Dbg, "Type of read is User Volume or virtual volume file\n", 0);
|
|
|
|
if (TypeOfOpen == UserVolumeOpen) {
|
|
|
|
//
|
|
// Verify that the volume for this handle is still valid
|
|
//
|
|
|
|
//
|
|
// Verify that the volume for this handle is still valid, permitting
|
|
// operations to proceed on dismounted volumes via the handle which
|
|
// performed the dismount.
|
|
//
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT | CCB_FLAG_SENT_FORMAT_UNIT )) {
|
|
|
|
FatQuickVerifyVcb( IrpContext, Vcb );
|
|
}
|
|
|
|
//
|
|
// If the caller previously sent a format unit command, then we will allow
|
|
// their read/write requests to ignore the verify flag on the device, since some
|
|
// devices send a media change event after format unit, but we don't want to
|
|
// process it yet since we're probably in the process of formatting the
|
|
// media.
|
|
//
|
|
|
|
if (FlagOn( Ccb->Flags, CCB_FLAG_SENT_FORMAT_UNIT )) {
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY );
|
|
}
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE )) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( &Vcb->Resource, TRUE );
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// If the volume isn't locked, flush it.
|
|
//
|
|
|
|
if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) {
|
|
|
|
FatFlushVolume( IrpContext, Vcb, Flush );
|
|
}
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
ExReleaseResourceLite( &Vcb->Resource );
|
|
} _SEH2_END;
|
|
|
|
SetFlag( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE );
|
|
}
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) {
|
|
|
|
LBO VolumeSize;
|
|
|
|
//
|
|
// Make sure we don't try to read past end of volume,
|
|
// reducing the byte count if necessary.
|
|
//
|
|
|
|
VolumeSize = (LBO) Vcb->Bpb.BytesPerSector *
|
|
(Vcb->Bpb.Sectors != 0 ? Vcb->Bpb.Sectors :
|
|
Vcb->Bpb.LargeSectors);
|
|
|
|
if (StartingLbo >= VolumeSize) {
|
|
Irp->IoStatus.Information = 0;
|
|
FatCompleteRequest( IrpContext, Irp, STATUS_END_OF_FILE );
|
|
return STATUS_END_OF_FILE;
|
|
}
|
|
|
|
if (ByteCount > VolumeSize - StartingLbo) {
|
|
|
|
ByteCount = RequestedByteCount = (ULONG) (VolumeSize - StartingLbo);
|
|
|
|
//
|
|
// For async reads we had set the byte count in the FatIoContext
|
|
// above, so fix that here.
|
|
//
|
|
|
|
if (!Wait) {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
|
|
ByteCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// For DASD we have to probe and lock the user's buffer
|
|
//
|
|
|
|
FatLockUserBuffer( IrpContext, Irp, IoWriteAccess, ByteCount );
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Virtual volume file open -- increment performance counters.
|
|
//
|
|
|
|
Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common.MetaDataDiskReads += 1;
|
|
|
|
}
|
|
|
|
//
|
|
// Read the data and wait for the results
|
|
//
|
|
|
|
FatSingleAsync( IrpContext,
|
|
Vcb,
|
|
StartingLbo,
|
|
ByteCount,
|
|
Irp );
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
|
|
|
//
|
|
// Account for DASD Ios
|
|
//
|
|
|
|
if (FatDiskAccountingEnabled) {
|
|
|
|
PETHREAD ThreadIssuingIo = PsGetCurrentThread();
|
|
|
|
PsUpdateDiskCounters( PsGetThreadProcess( ThreadIssuingIo ),
|
|
ByteCount,
|
|
0,
|
|
1,
|
|
0,
|
|
0 );
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!Wait) {
|
|
|
|
//
|
|
// We, nor anybody else, need the IrpContext any more.
|
|
//
|
|
|
|
IrpContext->FatIoContext = NULL;
|
|
|
|
FatDeleteIrpContext( IrpContext );
|
|
|
|
DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
FatWaitSync( IrpContext );
|
|
|
|
//
|
|
// If the call didn't succeed, raise the error status
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
|
|
|
|
NT_ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED );
|
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|
}
|
|
|
|
//
|
|
// Update the current file position
|
|
//
|
|
|
|
if (SynchronousIo && !PagingIo) {
|
|
FileObject->CurrentByteOffset.QuadPart =
|
|
StartingLbo + Irp->IoStatus.Information;
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status );
|
|
|
|
FatCompleteRequest( IrpContext, Irp, Status );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// At this point we know there is an Fcb/Dcb.
|
|
//
|
|
|
|
NT_ASSERT( FcbOrDcb != NULL );
|
|
|
|
//
|
|
// Check for a non-zero high part offset
|
|
//
|
|
|
|
if ( StartingByte.HighPart != 0 ) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
FatCompleteRequest( IrpContext, Irp, STATUS_END_OF_FILE );
|
|
return STATUS_END_OF_FILE;
|
|
}
|
|
|
|
//
|
|
// Use a try-finally to free Fcb/Dcb and buffers on the way out.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// This case corresponds to a normal user read file.
|
|
//
|
|
|
|
if ( TypeOfOpen == UserFileOpen
|
|
) {
|
|
|
|
ULONG FileSize;
|
|
ULONG ValidDataLength;
|
|
|
|
DebugTrace(0, Dbg, "Type of read is user file open\n", 0);
|
|
|
|
//
|
|
// If this is a noncached transfer and is not a paging I/O, and
|
|
// the file has a data section, then we will do a flush here
|
|
// to avoid stale data problems. Note that we must flush before
|
|
// acquiring the Fcb shared since the write may try to acquire
|
|
// it exclusive.
|
|
//
|
|
|
|
if (!PagingIo && NonCachedIo
|
|
|
|
&&
|
|
|
|
(FileObject->SectionObjectPointer->DataSectionObject != NULL)) {
|
|
|
|
IO_STATUS_BLOCK IoStatus = {0};
|
|
|
|
#ifndef REDUCE_SYNCHRONIZATION
|
|
if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) {
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
|
|
#endif //REDUCE_SYNCHRONIZATION
|
|
|
|
CcFlushCache( FileObject->SectionObjectPointer,
|
|
&StartingByte,
|
|
ByteCount,
|
|
&IoStatus );
|
|
|
|
#ifndef REDUCE_SYNCHRONIZATION
|
|
ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
|
|
FatReleaseFcb( IrpContext, FcbOrDcb );
|
|
#endif //REDUCE_SYNCHRONIZATION
|
|
|
|
if (!NT_SUCCESS( IoStatus.Status)) {
|
|
|
|
try_return( IoStatus.Status );
|
|
}
|
|
|
|
#ifndef REDUCE_SYNCHRONIZATION
|
|
ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
|
|
ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
|
|
#endif //REDUCE_SYNCHRONIZATION
|
|
}
|
|
|
|
//
|
|
// We need shared access to the Fcb/Dcb before proceeding.
|
|
//
|
|
|
|
if ( PagingIo ) {
|
|
|
|
if (!ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource,
|
|
TRUE )) {
|
|
|
|
DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %p shared without waiting\n", FcbOrDcb );
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
if (!Wait) {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.Resource =
|
|
FcbOrDcb->Header.PagingIoResource;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this is async I/O, we will wait if there is an
|
|
// exclusive waiter.
|
|
//
|
|
|
|
if (!Wait && NonCachedIo) {
|
|
|
|
if (!FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) {
|
|
|
|
DebugTrace( 0,
|
|
Dbg,
|
|
"Cannot acquire FcbOrDcb = %p shared without waiting\n",
|
|
FcbOrDcb );
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
IrpContext->FatIoContext->Wait.Async.Resource =
|
|
FcbOrDcb->Header.Resource;
|
|
|
|
} else {
|
|
|
|
if (!FatAcquireSharedFcb( IrpContext, FcbOrDcb )) {
|
|
|
|
DebugTrace( 0,
|
|
Dbg,
|
|
"Cannot acquire FcbOrDcb = %p shared without waiting\n",
|
|
FcbOrDcb );
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
FcbOrDcbAcquired = TRUE;
|
|
|
|
//
|
|
// Make sure the FcbOrDcb is still good
|
|
//
|
|
|
|
FatVerifyFcb( IrpContext, FcbOrDcb );
|
|
|
|
//
|
|
// We now check whether we can proceed based on the state of
|
|
// the file oplocks.
|
|
//
|
|
|
|
if (!PagingIo) {
|
|
|
|
Status = FsRtlCheckOplock( FatGetFcbOplock(FcbOrDcb),
|
|
Irp,
|
|
IrpContext,
|
|
FatOplockComplete,
|
|
FatPrePostIrp );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
OplockPostIrp = TRUE;
|
|
PostIrp = TRUE;
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Reset the flag indicating if Fast I/O is possible since the oplock
|
|
// check could have broken existing (conflicting) oplocks.
|
|
//
|
|
|
|
FcbOrDcb->Header.IsFastIoPossible = FatIsFastIoPossible( FcbOrDcb );
|
|
|
|
//
|
|
// We have to check for read access according to the current
|
|
// state of the file locks, and set FileSize from the Fcb.
|
|
//
|
|
|
|
if (!PagingIo &&
|
|
!FsRtlCheckLockForReadAccess( &FcbOrDcb->Specific.Fcb.FileLock,
|
|
Irp )) {
|
|
|
|
try_return( Status = STATUS_FILE_LOCK_CONFLICT );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Pick up our sizes and check/trim the IO.
|
|
//
|
|
|
|
FileSize = FcbOrDcb->Header.FileSize.LowPart;
|
|
ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart;
|
|
|
|
//
|
|
// If the read starts beyond End of File, return EOF.
|
|
//
|
|
|
|
if (StartingVbo >= FileSize) {
|
|
|
|
DebugTrace( 0, Dbg, "End of File\n", 0 );
|
|
|
|
try_return ( Status = STATUS_END_OF_FILE );
|
|
}
|
|
|
|
//
|
|
// If the read extends beyond EOF, truncate the read
|
|
//
|
|
|
|
if (ByteCount > FileSize - StartingVbo) {
|
|
|
|
ByteCount = RequestedByteCount = FileSize - StartingVbo;
|
|
|
|
if (NonCachedIo && !Wait) {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
|
|
RequestedByteCount;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// HANDLE THE NON-CACHED CASE
|
|
//
|
|
|
|
if ( NonCachedIo ) {
|
|
|
|
ULONG SectorSize;
|
|
ULONG BytesToRead;
|
|
|
|
DebugTrace(0, Dbg, "Non cached read.\n", 0);
|
|
|
|
//
|
|
// Get the sector size
|
|
//
|
|
|
|
SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
|
|
|
|
//
|
|
// Start by zeroing any part of the read after Valid Data
|
|
//
|
|
|
|
if (ValidDataLength < FcbOrDcb->ValidDataToDisk) {
|
|
|
|
ValidDataLength = FcbOrDcb->ValidDataToDisk;
|
|
}
|
|
|
|
|
|
if ( StartingVbo + ByteCount > ValidDataLength ) {
|
|
|
|
SystemBuffer = FatMapUserBuffer( IrpContext, Irp );
|
|
|
|
if (StartingVbo < ValidDataLength) {
|
|
|
|
ULONG ZeroingOffset;
|
|
|
|
//
|
|
// Now zero out the user's request sector aligned beyond
|
|
// vdl. We will handle the straddling sector at completion
|
|
// time via the bytecount reduction which immediately
|
|
// follows this check.
|
|
//
|
|
// Note that we used to post in this case for async requests.
|
|
// Note also that if the request was wholly beyond VDL that
|
|
// we did not post, therefore this is consistent. Synchronous
|
|
// zeroing is fine for async requests.
|
|
//
|
|
|
|
ZeroingOffset = ((ValidDataLength - StartingVbo) + (SectorSize - 1))
|
|
& ~(SectorSize - 1);
|
|
|
|
//
|
|
// If the offset is at or above the byte count, no harm: just means
|
|
// that the read ends in the last sector and the zeroing will be
|
|
// done at completion.
|
|
//
|
|
|
|
if (ByteCount > ZeroingOffset) {
|
|
|
|
SafeZeroMemory( (PUCHAR) SystemBuffer + ZeroingOffset,
|
|
ByteCount - ZeroingOffset);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// All we have to do now is sit here and zero the
|
|
// user's buffer, no reading is required.
|
|
//
|
|
|
|
SafeZeroMemory( (PUCHAR)SystemBuffer, ByteCount );
|
|
|
|
Irp->IoStatus.Information = ByteCount;
|
|
|
|
|
|
try_return ( Status = STATUS_SUCCESS );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Reduce the byte count to actually read if it extends beyond
|
|
// Valid Data Length
|
|
//
|
|
|
|
ByteCount = (ValidDataLength - StartingVbo < ByteCount) ?
|
|
ValidDataLength - StartingVbo : ByteCount;
|
|
|
|
//
|
|
// Round up to a sector boundary, and remember that if we are
|
|
// reading extra bytes we will zero them out during completion.
|
|
//
|
|
|
|
BytesToRead = (ByteCount + (SectorSize - 1))
|
|
& ~(SectorSize - 1);
|
|
|
|
//
|
|
// Just to help alleviate confusion. At this point:
|
|
//
|
|
// RequestedByteCount - is the number of bytes originally
|
|
// taken from the Irp, but constrained
|
|
// to filesize.
|
|
//
|
|
// ByteCount - is RequestedByteCount constrained to
|
|
// ValidDataLength.
|
|
//
|
|
// BytesToRead - is ByteCount rounded up to sector
|
|
// boundry. This is the number of bytes
|
|
// that we must physically read.
|
|
//
|
|
|
|
//
|
|
// If this request is not properly aligned, or extending
|
|
// to a sector boundary would overflow the buffer, send it off
|
|
// on a special-case path.
|
|
//
|
|
|
|
if ( (StartingVbo & (SectorSize - 1)) ||
|
|
(BytesToRead > IrpSp->Parameters.Read.Length) ) {
|
|
|
|
//
|
|
// If we can't wait, we must post this.
|
|
//
|
|
|
|
if (!Wait) {
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
//
|
|
// Do the physical read
|
|
//
|
|
|
|
FatNonCachedNonAlignedRead( IrpContext,
|
|
Irp,
|
|
FcbOrDcb,
|
|
StartingVbo,
|
|
ByteCount );
|
|
|
|
//
|
|
// Set BytesToRead to ByteCount to satify the following ASSERT.
|
|
//
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress:28931, "needed for debug build" )
|
|
#endif
|
|
BytesToRead = ByteCount;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Perform the actual IO
|
|
//
|
|
|
|
|
|
if (FatNonCachedIo( IrpContext,
|
|
Irp,
|
|
FcbOrDcb,
|
|
StartingVbo,
|
|
BytesToRead,
|
|
ByteCount,
|
|
0) == STATUS_PENDING) {
|
|
|
|
|
|
IrpContext->FatIoContext = NULL;
|
|
|
|
Irp = NULL;
|
|
|
|
try_return( Status = STATUS_PENDING );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the call didn't succeed, raise the error status
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
|
|
|
|
NT_ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED );
|
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Else set the Irp information field to reflect the
|
|
// entire desired read.
|
|
//
|
|
|
|
NT_ASSERT( Irp->IoStatus.Information == BytesToRead );
|
|
|
|
Irp->IoStatus.Information = RequestedByteCount;
|
|
}
|
|
|
|
//
|
|
// The transfer is complete.
|
|
//
|
|
|
|
try_return( Status );
|
|
|
|
} // if No Intermediate Buffering
|
|
|
|
|
|
//
|
|
// HANDLE CACHED CASE
|
|
//
|
|
|
|
else {
|
|
|
|
//
|
|
// We delay setting up the file cache until now, in case the
|
|
// caller never does any I/O to the file, and thus
|
|
// FileObject->PrivateCacheMap == NULL.
|
|
//
|
|
|
|
if (FileObject->PrivateCacheMap == NULL) {
|
|
|
|
DebugTrace(0, Dbg, "Initialize cache mapping.\n", 0);
|
|
|
|
//
|
|
// Get the file allocation size, and if it is less than
|
|
// the file size, raise file corrupt error.
|
|
//
|
|
|
|
if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
|
|
|
|
FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
|
|
}
|
|
|
|
if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) {
|
|
|
|
FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
|
}
|
|
|
|
//
|
|
// Now initialize the cache map.
|
|
//
|
|
|
|
FatInitializeCacheMap( FileObject,
|
|
(PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize,
|
|
FALSE,
|
|
&FatData.CacheManagerCallbacks,
|
|
FcbOrDcb );
|
|
|
|
CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY );
|
|
}
|
|
|
|
|
|
//
|
|
// DO A NORMAL CACHED READ, if the MDL bit is not set,
|
|
//
|
|
|
|
DebugTrace(0, Dbg, "Cached read.\n", 0);
|
|
|
|
if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
|
|
|
|
//
|
|
// Get hold of the user's buffer.
|
|
//
|
|
|
|
SystemBuffer = FatMapUserBuffer( IrpContext, Irp );
|
|
|
|
//
|
|
// Now try to do the copy.
|
|
//
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
|
if (!CcCopyReadEx( FileObject,
|
|
&StartingByte,
|
|
ByteCount,
|
|
Wait,
|
|
SystemBuffer,
|
|
&Irp->IoStatus,
|
|
Irp->Tail.Overlay.Thread )) {
|
|
#else
|
|
if (!CcCopyRead( FileObject,
|
|
&StartingByte,
|
|
ByteCount,
|
|
Wait,
|
|
SystemBuffer,
|
|
&Irp->IoStatus )) {
|
|
#endif
|
|
|
|
DebugTrace( 0, Dbg, "Cached Read could not wait\n", 0 );
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
NT_ASSERT( NT_SUCCESS( Status ));
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
|
|
//
|
|
// HANDLE A MDL READ
|
|
//
|
|
|
|
else {
|
|
|
|
DebugTrace(0, Dbg, "MDL read.\n", 0);
|
|
|
|
NT_ASSERT( Wait );
|
|
|
|
CcMdlRead( FileObject,
|
|
&StartingByte,
|
|
ByteCount,
|
|
&Irp->MdlAddress,
|
|
&Irp->IoStatus );
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
NT_ASSERT( NT_SUCCESS( Status ));
|
|
|
|
try_return( Status );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// These cases correspond to a system read directory file or
|
|
// ea file.
|
|
//
|
|
|
|
if (( TypeOfOpen == DirectoryFile ) || ( TypeOfOpen == EaFile)
|
|
) {
|
|
|
|
ULONG SectorSize;
|
|
|
|
|
|
#if FASTFATDBG
|
|
if ( TypeOfOpen == DirectoryFile ) {
|
|
DebugTrace(0, Dbg, "Type of read is directoryfile\n", 0);
|
|
} else if ( TypeOfOpen == EaFile) {
|
|
DebugTrace(0, Dbg, "Type of read is eafile\n", 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// For the noncached case, assert that everything is sector
|
|
// alligned.
|
|
//
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress:28931, "needed for debug build" )
|
|
#endif
|
|
SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
|
|
|
|
//
|
|
// We make several assumptions about these two types of files.
|
|
// Make sure all of them are true.
|
|
//
|
|
|
|
NT_ASSERT( NonCachedIo && PagingIo );
|
|
NT_ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 );
|
|
|
|
|
|
//
|
|
// These calls must allways be within the allocation size
|
|
//
|
|
|
|
if (StartingVbo >= FcbOrDcb->Header.AllocationSize.LowPart) {
|
|
|
|
DebugTrace( 0, Dbg, "PagingIo dirent started beyond EOF.\n", 0 );
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
try_return( Status = STATUS_SUCCESS );
|
|
}
|
|
|
|
if ( StartingVbo + ByteCount > FcbOrDcb->Header.AllocationSize.LowPart ) {
|
|
|
|
DebugTrace( 0, Dbg, "PagingIo dirent extending beyond EOF.\n", 0 );
|
|
ByteCount = FcbOrDcb->Header.AllocationSize.LowPart - StartingVbo;
|
|
}
|
|
|
|
|
|
//
|
|
// Perform the actual IO
|
|
//
|
|
|
|
if (FatNonCachedIo( IrpContext,
|
|
Irp,
|
|
FcbOrDcb,
|
|
StartingVbo,
|
|
ByteCount,
|
|
ByteCount,
|
|
0 ) == STATUS_PENDING) {
|
|
|
|
IrpContext->FatIoContext = NULL;
|
|
|
|
Irp = NULL;
|
|
|
|
try_return( Status = STATUS_PENDING );
|
|
}
|
|
|
|
//
|
|
// If the call didn't succeed, raise the error status
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
|
|
|
|
NT_ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED );
|
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|
|
|
} else {
|
|
|
|
NT_ASSERT( Irp->IoStatus.Information == ByteCount );
|
|
}
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// This is the case of a user who openned a directory. No reading is
|
|
// allowed.
|
|
//
|
|
|
|
if ( TypeOfOpen == UserDirectoryOpen ) {
|
|
|
|
DebugTrace( 0, Dbg, "CommonRead -> STATUS_INVALID_PARAMETER\n", 0);
|
|
|
|
try_return( Status = STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// If we get this far, something really serious is wrong.
|
|
//
|
|
|
|
DebugDump("Illegal TypeOfOpen\n", 0, FcbOrDcb );
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
|
|
#endif
|
|
FatBugCheck( TypeOfOpen, (ULONG_PTR) FcbOrDcb, 0 );
|
|
|
|
try_exit: NOTHING;
|
|
|
|
//
|
|
// If the request was not posted and there's an Irp, deal with it.
|
|
//
|
|
|
|
if ( Irp ) {
|
|
|
|
if ( !PostIrp ) {
|
|
|
|
ULONG ActualBytesRead;
|
|
|
|
DebugTrace( 0, Dbg, "Completing request with status = %08lx\n",
|
|
Status);
|
|
|
|
DebugTrace( 0, Dbg, " Information = %08lx\n",
|
|
Irp->IoStatus.Information);
|
|
|
|
//
|
|
// Record the total number of bytes actually read
|
|
//
|
|
|
|
ActualBytesRead = (ULONG)Irp->IoStatus.Information;
|
|
|
|
//
|
|
// If the file was opened for Synchronous IO, update the current
|
|
// file position.
|
|
//
|
|
|
|
if (SynchronousIo && !PagingIo) {
|
|
|
|
FileObject->CurrentByteOffset.LowPart =
|
|
StartingVbo + (NT_ERROR( Status ) ? 0 : ActualBytesRead);
|
|
}
|
|
|
|
//
|
|
// If this was not PagingIo, mark that the last access
|
|
// time on the dirent needs to be updated on close.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status) && !PagingIo) {
|
|
|
|
SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ );
|
|
}
|
|
|
|
} else {
|
|
|
|
DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 );
|
|
|
|
if (!OplockPostIrp) {
|
|
|
|
Status = FatFsdPostRequest( IrpContext, Irp );
|
|
}
|
|
}
|
|
}
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
DebugUnwind( FatCommonRead );
|
|
|
|
//
|
|
// If the FcbOrDcb has been acquired, release it.
|
|
//
|
|
|
|
if (FcbOrDcbAcquired && Irp) {
|
|
|
|
if ( PagingIo ) {
|
|
|
|
ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
|
|
|
|
} else {
|
|
|
|
FatReleaseFcb( NULL, FcbOrDcb );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Complete the request if we didn't post it and no exception
|
|
//
|
|
// Note that FatCompleteRequest does the right thing if either
|
|
// IrpContext or Irp are NULL
|
|
//
|
|
|
|
if (!PostIrp) {
|
|
|
|
//
|
|
// If we had a stack io context, we have to make sure the contents
|
|
// are cleaned up before we leave.
|
|
//
|
|
// At present with zero mdls, this will only really happen on exceptional
|
|
// termination where we failed to dispatch the IO. Cleanup of zero mdls
|
|
// normally occurs during completion, but when we bail we must make sure
|
|
// the cleanup occurs here or the fatiocontext will go out of scope.
|
|
//
|
|
// If the operation was posted, cleanup occured there.
|
|
//
|
|
|
|
if (FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) {
|
|
|
|
if (IrpContext->FatIoContext->ZeroMdl) {
|
|
IoFreeMdl( IrpContext->FatIoContext->ZeroMdl );
|
|
}
|
|
|
|
ClearFlag(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT);
|
|
IrpContext->FatIoContext = NULL;
|
|
}
|
|
|
|
if (!_SEH2_AbnormalTermination()) {
|
|
|
|
FatCompleteRequest( IrpContext, Irp, Status );
|
|
}
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status );
|
|
} _SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
NTAPI
|
|
FatOverflowPagingFileRead (
|
|
IN PVOID Context,
|
|
IN PKEVENT Event
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine simply call FatPagingFileIo. It is invoked in cases when
|
|
there was not enough stack space to perform the pagefault in the
|
|
original thread. It is also responsible for freeing the packet pool.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp being processed
|
|
|
|
Fcb - Supplies the paging file Fcb, since we have it handy.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
PPAGING_FILE_OVERFLOW_PACKET Packet = Context;
|
|
|
|
FatPagingFileIo( Packet->Irp, Packet->Fcb );
|
|
|
|
//
|
|
// Set the stack overflow item's event to tell the original
|
|
// thread that we're done.
|
|
//
|
|
|
|
KeSetEvent( Event, 0, FALSE );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|