mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1678 lines
45 KiB
1678 lines
45 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Read.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the File Read routine for Read called by the
|
|
dispatch driver.
|
|
|
|
Author:
|
|
|
|
David Goebel [DavidGoe] 28-Feb-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#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 that for a risc platform.
|
|
//
|
|
|
|
#if defined(_M_IX86)
|
|
#if DBG
|
|
#define OVERFLOW_READ_THRESHHOLD (0xE00)
|
|
#else
|
|
#define OVERFLOW_READ_THRESHHOLD (0xA00)
|
|
#endif // DBG
|
|
#else
|
|
#define OVERFLOW_READ_THRESHHOLD (0x1000)
|
|
#endif // defined(_M_IX86)
|
|
|
|
|
|
//
|
|
// The following procedures handles read stack overflow operations.
|
|
//
|
|
|
|
VOID
|
|
FatStackOverflowRead (
|
|
IN PVOID Context,
|
|
IN PKEVENT Event
|
|
);
|
|
|
|
NTSTATUS
|
|
FatPostStackOverflowRead (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp,
|
|
IN PFCB Fcb
|
|
);
|
|
|
|
VOID
|
|
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) { \
|
|
try { \
|
|
RtlZeroMemory((AT), (BYTE_COUNT)); \
|
|
} except(EXCEPTION_EXECUTE_HANDLER) { \
|
|
FatRaiseStatus( IrpContext, STATUS_INVALID_USER_BUFFER ); \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// Macro to increment appropriate performance counters.
|
|
//
|
|
|
|
#define CollectReadStats(VCB,OPEN_TYPE,BYTE_COUNT) { \
|
|
PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()]; \
|
|
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
|
|
|
|
|
|
NTSTATUS
|
|
FatFsdRead (
|
|
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
|
IN 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;
|
|
NTSTATUS Status;
|
|
PIRP_CONTEXT IrpContext = NULL;
|
|
|
|
BOOLEAN TopLevel;
|
|
|
|
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.
|
|
//
|
|
|
|
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;
|
|
}
|
|
|
|
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 ));
|
|
}
|
|
|
|
//
|
|
// We can't handle DPC calls yet, post it.
|
|
//
|
|
|
|
if ( FlagOn(IrpContext->MinorFunction, IRP_MN_DPC) ) {
|
|
|
|
DebugTrace(0, Dbg, "Passing DPC call to Fsp\n", 0 );
|
|
try_return( Status = FatFsdPostRequest( 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) &&
|
|
((NodeType(Fcb) == FAT_NTC_FCB) ||
|
|
(NodeType(Fcb) == FAT_NTC_DCB) ||
|
|
(NodeType(Fcb) == FAT_NTC_ROOT_DCB))) {
|
|
|
|
DebugTrace(0, Dbg, "Passing StackOverflowRead off\n", 0 );
|
|
try_return( Status = FatPostStackOverflowRead( IrpContext, Irp, Fcb ) );
|
|
}
|
|
|
|
Status = FatCommonRead( IrpContext, Irp );
|
|
|
|
try_exit: NOTHING;
|
|
} except(FatExceptionFilter( IrpContext, 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, GetExceptionCode() );
|
|
}
|
|
|
|
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
|
|
//
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEVENT Event;
|
|
PERESOURCE Resource;
|
|
|
|
DebugTrace(0, Dbg, "Getting too close to stack limit pass request to Fsp\n", 0 );
|
|
|
|
//
|
|
// Allocate an event and get shared on the resource we will
|
|
// be later using the common read.
|
|
//
|
|
|
|
Event = FsRtlAllocatePool( NonPagedPool, sizeof(KEVENT) );
|
|
KeInitializeEvent( Event, NotificationEvent, FALSE );
|
|
|
|
if (FlagOn(Irp->Flags, IRP_PAGING_IO) && (Fcb->Header.PagingIoResource != NULL)) {
|
|
|
|
Resource = Fcb->Header.PagingIoResource;
|
|
|
|
} else {
|
|
|
|
Resource = Fcb->Header.Resource;
|
|
}
|
|
|
|
ExAcquireResourceShared( Resource, TRUE );
|
|
|
|
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 (Fcb->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
|
|
//
|
|
|
|
(VOID) KeWaitForSingleObject( Event, Executive, KernelMode, FALSE, NULL );
|
|
|
|
} finally {
|
|
|
|
ExReleaseResource( Resource );
|
|
|
|
ExFreePool( Event );
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
VOID
|
|
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;
|
|
|
|
//
|
|
// 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)) {
|
|
|
|
Vcb = ((PFCB)IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->
|
|
FileObject->FsContext)->Vcb;
|
|
|
|
SavedVerifyThread = Vcb->VerifyThread;
|
|
Vcb->VerifyThread = KeGetCurrentThread();
|
|
}
|
|
|
|
//
|
|
// Do the read operation protected by a try-except clause
|
|
//
|
|
|
|
try {
|
|
|
|
(VOID) FatCommonRead( IrpContext, IrpContext->OriginatingIrp );
|
|
|
|
} except(FatExceptionFilter( IrpContext, 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 = GetExceptionCode();
|
|
|
|
if (ExceptionCode == STATUS_FILE_DELETED) {
|
|
|
|
IrpContext->ExceptionStatus = ExceptionCode = STATUS_END_OF_FILE;
|
|
IrpContext->OriginatingIrp->IoStatus.Information = 0;
|
|
}
|
|
|
|
(VOID) FatProcessException( IrpContext, IrpContext->OriginatingIrp, ExceptionCode );
|
|
}
|
|
|
|
//
|
|
// Restore the original VerifyVolumeThread
|
|
//
|
|
|
|
if (SavedVerifyThread != NULL) {
|
|
|
|
Vcb->VerifyThread = SavedVerifyThread;
|
|
}
|
|
|
|
//
|
|
// Set the stack overflow item's event to tell the original
|
|
// thread that we're done.
|
|
//
|
|
|
|
KeSetEvent( Event, 0, FALSE );
|
|
}
|
|
|
|
|
|
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 TypeOfRead;
|
|
|
|
BOOLEAN PostIrp = FALSE;
|
|
BOOLEAN OplockPostIrp = FALSE;
|
|
|
|
BOOLEAN FcbOrDcbAcquired = FALSE;
|
|
|
|
BOOLEAN Wait;
|
|
BOOLEAN PagingIo;
|
|
BOOLEAN NonCachedIo;
|
|
BOOLEAN SynchronousIo;
|
|
|
|
NTSTATUS Status;
|
|
|
|
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;
|
|
|
|
//
|
|
// 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 = %8lx\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;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Extract the nature of the read from the file object, and case on it
|
|
//
|
|
|
|
TypeOfRead = FatDecodeFileObject(FileObject, &Vcb, &FcbOrDcb, &Ccb);
|
|
|
|
//
|
|
// 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, TypeOfRead, ByteCount);
|
|
|
|
if (TypeOfRead == UserFileOpen) {
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO);
|
|
} else {
|
|
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is a previous STACK FatIoContext pointer, NULL it.
|
|
//
|
|
|
|
if ((IrpContext->FatIoContext != NULL) &&
|
|
FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) {
|
|
|
|
IrpContext->FatIoContext = NULL;
|
|
}
|
|
|
|
//
|
|
// Allocate if necessary and initialize a FAT_IO_CONTEXT block for
|
|
// all non cached Io, except Cvf file Io. For synchronous Io
|
|
// we use stack storage, otherwise we allocate pool.
|
|
//
|
|
|
|
if (NonCachedIo &&
|
|
!(FcbOrDcb && FlagOn(FcbOrDcb->FcbState,
|
|
FCB_STATE_COMPRESSED_VOLUME_FILE))) {
|
|
|
|
if (IrpContext->FatIoContext == NULL) {
|
|
|
|
if (!Wait) {
|
|
|
|
IrpContext->FatIoContext =
|
|
FsRtlAllocatePool( NonPagedPool, sizeof(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 {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.ResourceThreadId =
|
|
ExGetCurrentResourceThread();
|
|
|
|
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 ((TypeOfRead == VirtualVolumeFile) ||
|
|
(TypeOfRead == UserVolumeOpen)) {
|
|
|
|
DebugTrace(0, Dbg, "Type of read is User Volume or virtual volume file\n", 0);
|
|
|
|
if (TypeOfRead == UserVolumeOpen) {
|
|
|
|
//
|
|
// Verify that the volume for this handle is still valid
|
|
//
|
|
|
|
FatQuickVerifyVcb( IrpContext, Vcb );
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE )) {
|
|
|
|
(VOID)ExAcquireResourceExclusive( &Vcb->Resource, TRUE );
|
|
|
|
try {
|
|
|
|
//
|
|
// If the volume isn't locked, flush it.
|
|
//
|
|
|
|
if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) {
|
|
|
|
FatFlushVolume( IrpContext, Vcb );
|
|
}
|
|
|
|
} finally {
|
|
|
|
ExReleaseResource( &Vcb->Resource );
|
|
}
|
|
|
|
SetFlag( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE );
|
|
}
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) {
|
|
|
|
ULONG VolumeSize;
|
|
|
|
//
|
|
// Make sure we don't try to read past end of volume,
|
|
// reducing the byte count if necessary.
|
|
//
|
|
|
|
VolumeSize = Vcb->Bpb.BytesPerSector *
|
|
(Vcb->Bpb.Sectors != 0 ? Vcb->Bpb.Sectors :
|
|
Vcb->Bpb.LargeSectors);
|
|
|
|
if (StartingVbo >= VolumeSize) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
FatCompleteRequest( IrpContext, Irp, STATUS_END_OF_FILE );
|
|
return STATUS_END_OF_FILE;
|
|
}
|
|
|
|
if (ByteCount > VolumeSize - StartingVbo) {
|
|
|
|
ByteCount = VolumeSize - StartingVbo;
|
|
}
|
|
}
|
|
|
|
//
|
|
// For DASD we have to probe and lock the user's buffer
|
|
//
|
|
|
|
FatLockUserBuffer( IrpContext, Irp, IoWriteAccess, ByteCount );
|
|
|
|
//
|
|
// Deal with stupid people who open the volume DASD with
|
|
// caching.
|
|
//
|
|
|
|
if (!IrpContext->FatIoContext) {
|
|
|
|
IrpContext->FatIoContext =
|
|
FsRtlAllocatePool( NonPagedPool, sizeof(FAT_IO_CONTEXT) );
|
|
|
|
RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) );
|
|
|
|
if (Wait) {
|
|
|
|
KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
} else {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
|
|
ByteCount;
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Virtual volume file open -- increment performance counters.
|
|
//
|
|
|
|
Vcb->Statistics[KeGetCurrentProcessorNumber()].MetaDataDiskReads += 1;
|
|
|
|
}
|
|
|
|
//
|
|
// Read the data and wait for the results
|
|
//
|
|
|
|
FatSingleAsync( IrpContext,
|
|
Vcb,
|
|
StartingVbo,
|
|
ByteCount,
|
|
Irp );
|
|
|
|
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 )) {
|
|
|
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|
}
|
|
|
|
//
|
|
// Update the current file position
|
|
//
|
|
|
|
if (SynchronousIo && !PagingIo) {
|
|
FileObject->CurrentByteOffset.LowPart =
|
|
StartingVbo + 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.
|
|
//
|
|
|
|
ASSERT( FcbOrDcb != NULL );
|
|
|
|
//
|
|
// If this is a Cvf file, just send it to the device driver.
|
|
// We assume Mm is a good citizen.
|
|
//
|
|
|
|
if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_COMPRESSED_VOLUME_FILE)) {
|
|
|
|
//
|
|
// If this is the comprerssed file, check the FcbCondition
|
|
//
|
|
|
|
FatVerifyFcb( IrpContext, FcbOrDcb );
|
|
|
|
//
|
|
// If for any reason the Mcb was reset, re-initialize it.
|
|
//
|
|
|
|
if (FcbOrDcb->Header.AllocationSize.LowPart == 0xffffffff) {
|
|
|
|
FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
|
|
}
|
|
|
|
//
|
|
// Do the usual STATUS_PENDING things.
|
|
//
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
//
|
|
// Perform the actual IO, it will be completed when the io finishes.
|
|
//
|
|
|
|
FatPagingFileIo( Irp, FcbOrDcb );
|
|
|
|
//
|
|
// We, nor anybody else, need the IrpContext any more.
|
|
//
|
|
|
|
FatDeleteIrpContext( IrpContext );
|
|
|
|
DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// Use a try-finally to free Fcb/Dcb and buffers on the way out.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// This case corresponds to a normal user read file.
|
|
//
|
|
|
|
if ( TypeOfRead == 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)) {
|
|
|
|
//
|
|
// We hold the main resource exclusive here because the flush
|
|
// may generate a recursive write in this thread. The PagingIo
|
|
// resource is held shared so the drop-and-release serialization
|
|
// below will work.
|
|
//
|
|
|
|
if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) {
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
ExAcquireResourceShared( FcbOrDcb->Header.PagingIoResource, TRUE );
|
|
|
|
CcFlushCache( FileObject->SectionObjectPointer,
|
|
&StartingByte,
|
|
ByteCount,
|
|
&Irp->IoStatus );
|
|
|
|
ExReleaseResource( FcbOrDcb->Header.PagingIoResource );
|
|
FatReleaseFcb( IrpContext, FcbOrDcb );
|
|
|
|
if (!NT_SUCCESS( Irp->IoStatus.Status)) {
|
|
|
|
try_return( Irp->IoStatus.Status );
|
|
}
|
|
|
|
//
|
|
// Acquiring and immediately dropping the resource serializes
|
|
// us behind any other writes taking place (either from the
|
|
// lazy writer or modified page writer).
|
|
//
|
|
|
|
ExAcquireResourceExclusive( FcbOrDcb->Header.PagingIoResource, TRUE );
|
|
ExReleaseResource( FcbOrDcb->Header.PagingIoResource );
|
|
}
|
|
|
|
//
|
|
// We need shared access to the Fcb/Dcb before proceeding.
|
|
//
|
|
|
|
if ( PagingIo ) {
|
|
|
|
if (!ExAcquireResourceShared( FcbOrDcb->Header.PagingIoResource,
|
|
Wait )) {
|
|
|
|
DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx 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 directly to the disk we need to check that
|
|
// we don't exhaust the number of times a single thread can
|
|
// acquire the resource. Also, we will wait if there is an
|
|
// exclusive waiter.
|
|
//
|
|
|
|
if (!Wait && NonCachedIo) {
|
|
|
|
if (!FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) {
|
|
|
|
DebugTrace( 0,
|
|
Dbg,
|
|
"Cannot acquire FcbOrDcb = %08lx shared without waiting\n",
|
|
FcbOrDcb );
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
if (ExIsResourceAcquiredShared( FcbOrDcb->Header.Resource )
|
|
> MAX_FCB_ASYNC_ACQUIRE) {
|
|
|
|
FcbOrDcbAcquired = TRUE;
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
IrpContext->FatIoContext->Wait.Async.Resource =
|
|
FcbOrDcb->Header.Resource;
|
|
|
|
} else {
|
|
|
|
if (!FatAcquireSharedFcb( IrpContext, FcbOrDcb )) {
|
|
|
|
DebugTrace( 0,
|
|
Dbg,
|
|
"Cannot acquire FcbOrDcb = %08lx shared without waiting\n",
|
|
FcbOrDcb );
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
FcbOrDcbAcquired = TRUE;
|
|
|
|
//
|
|
// We check whether we can proceed
|
|
// based on the state of the file oplocks.
|
|
//
|
|
|
|
Status = FsRtlCheckOplock( &FcbOrDcb->Specific.Fcb.Oplock,
|
|
Irp,
|
|
IrpContext,
|
|
FatOplockComplete,
|
|
FatPrePostIrp );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
OplockPostIrp = TRUE;
|
|
PostIrp = TRUE;
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Set the flag indicating if Fast I/O is possible
|
|
//
|
|
|
|
FcbOrDcb->Header.IsFastIoPossible = FatIsFastIoPossible( FcbOrDcb );
|
|
|
|
//
|
|
// Make sure the FcbOrDcb is still good
|
|
//
|
|
|
|
FatVerifyFcb( IrpContext, 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 );
|
|
}
|
|
|
|
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 = FileSize - StartingVbo;
|
|
|
|
RequestedByteCount = ByteCount;
|
|
|
|
if (NonCachedIo && !Wait) {
|
|
|
|
IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
|
|
RequestedByteCount;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// HANDLE THE NON-CACHED CASE
|
|
//
|
|
|
|
if ( NonCachedIo ) {
|
|
|
|
ULONG SectorSize;
|
|
ULONG BytesToRead;
|
|
|
|
BOOLEAN ZeroBeyondValidData = FALSE;
|
|
ULONG ZeroingOffset;
|
|
ULONG BytesToZero;
|
|
|
|
DebugTrace(0, Dbg, "Non cached read.\n", 0);
|
|
|
|
//
|
|
// 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) {
|
|
|
|
//
|
|
// If we can't wait, we must post this.
|
|
//
|
|
|
|
if (!Wait) {
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
ZeroingOffset = ValidDataLength - StartingVbo;
|
|
BytesToZero = ByteCount - ZeroingOffset;
|
|
ZeroBeyondValidData = TRUE;
|
|
|
|
} 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;
|
|
//
|
|
// Get the sector size
|
|
//
|
|
|
|
SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
|
|
|
|
//
|
|
// Round up to a sector boundary, and remember if we are
|
|
// reading extra bytes.
|
|
//
|
|
|
|
BytesToRead = (ByteCount + (SectorSize - 1))
|
|
& ~(SectorSize - 1);
|
|
|
|
if (BytesToRead > ByteCount) {
|
|
|
|
//
|
|
// If we can't wait, we must post this.
|
|
//
|
|
|
|
if (!Wait) {
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 );
|
|
|
|
//
|
|
// This routine correctly copied to the byte, so no
|
|
// zeroing is required. Also Set BytesToRead to
|
|
// ByteCount to satify the following ASSERT.
|
|
//
|
|
|
|
ZeroBeyondValidData = FALSE;
|
|
|
|
BytesToRead = ByteCount;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Perform the actual IO
|
|
//
|
|
|
|
if (FatNonCachedIo( IrpContext,
|
|
Irp,
|
|
FcbOrDcb,
|
|
StartingVbo,
|
|
BytesToRead ) == 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 )) {
|
|
|
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Else set the Irp information field to reflect the
|
|
// entire desired read.
|
|
//
|
|
|
|
ASSERT( Irp->IoStatus.Information == BytesToRead );
|
|
|
|
Irp->IoStatus.Information = RequestedByteCount;
|
|
}
|
|
|
|
//
|
|
// If we rounded up to a sector boundry before, zero out
|
|
// the other garbage we read from the disk.
|
|
//
|
|
|
|
if (BytesToRead > ByteCount) {
|
|
|
|
if (SystemBuffer == NULL) {
|
|
|
|
SystemBuffer = FatMapUserBuffer( IrpContext, Irp );
|
|
}
|
|
|
|
SafeZeroMemory( (PUCHAR)SystemBuffer + ByteCount,
|
|
BytesToRead - ByteCount );
|
|
}
|
|
|
|
//
|
|
// If we rounded up to a sector boundry before, zero out
|
|
// the other garbage we read from the disk.
|
|
//
|
|
|
|
if ( ZeroBeyondValidData ) {
|
|
|
|
SafeZeroMemory( (PUCHAR)SystemBuffer + ZeroingOffset,
|
|
BytesToZero );
|
|
}
|
|
|
|
//
|
|
// 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.LowPart == 0xffffffff) {
|
|
|
|
FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
|
|
}
|
|
|
|
if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) {
|
|
|
|
FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
|
|
|
|
FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
|
}
|
|
|
|
//
|
|
// Now initialize the cache map.
|
|
//
|
|
|
|
CcInitializeCacheMap( 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 (!CcCopyRead( FileObject,
|
|
&StartingByte,
|
|
ByteCount,
|
|
Wait,
|
|
SystemBuffer,
|
|
&Irp->IoStatus )) {
|
|
|
|
DebugTrace( 0, Dbg, "Cached Read could not wait\n", 0 );
|
|
|
|
try_return( PostIrp = TRUE );
|
|
}
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
|
|
//
|
|
// HANDLE A MDL READ
|
|
//
|
|
|
|
else {
|
|
|
|
DebugTrace(0, Dbg, "MDL read.\n", 0);
|
|
|
|
ASSERT( Wait );
|
|
|
|
CcMdlRead( FileObject,
|
|
&StartingByte,
|
|
ByteCount,
|
|
&Irp->MdlAddress,
|
|
&Irp->IoStatus );
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
|
|
try_return( Status );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// These two cases correspond to a system read directory file and
|
|
// ea file.
|
|
//
|
|
|
|
if (( TypeOfRead == DirectoryFile ) || ( TypeOfRead == EaFile)) {
|
|
|
|
ULONG SectorSize;
|
|
|
|
DebugTrace(0, Dbg, "Read Directory or Ea file.\n", 0);
|
|
|
|
//
|
|
// For the noncached case, assert that everything is sector
|
|
// alligned.
|
|
//
|
|
|
|
SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
|
|
|
|
//
|
|
// We make several assumptions about these two types of files.
|
|
// Make sure all of them are true.
|
|
//
|
|
|
|
ASSERT( NonCachedIo && PagingIo );
|
|
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 ) == 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 )) {
|
|
|
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|
|
|
} else {
|
|
|
|
ASSERT( Irp->IoStatus.Information == ByteCount );
|
|
}
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// This is the case of a user who openned a directory. No reading is
|
|
// allowed.
|
|
//
|
|
|
|
if ( TypeOfRead == 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 TypeOfRead\n", 0, FcbOrDcb );
|
|
|
|
FatBugCheck( TypeOfRead, 0, 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 = Irp->IoStatus.Information;
|
|
|
|
//
|
|
// If the file was opened for Synchronous IO, update the current
|
|
// file position.
|
|
//
|
|
|
|
if (SynchronousIo && !PagingIo) {
|
|
|
|
FileObject->CurrentByteOffset.LowPart =
|
|
StartingVbo + 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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( FatCommonRead );
|
|
|
|
//
|
|
// If the FcbOrDcb has been acquired, release it.
|
|
//
|
|
|
|
if (FcbOrDcbAcquired && Irp) {
|
|
|
|
if ( PagingIo ) {
|
|
|
|
ExReleaseResource( 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 && !AbnormalTermination() ) {
|
|
|
|
FatCompleteRequest( IrpContext, Irp, Status );
|
|
}
|
|
|
|
|
|
DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
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;
|
|
}
|
|
|
|
|