1957 lines
61 KiB
C
1957 lines
61 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Read.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the File Read routine for Ntfs called by the
|
||
dispatch driver.
|
||
|
||
Author:
|
||
|
||
Brian Andrew BrianAn 15-Aug-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "NtfsProc.h"
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_READ)
|
||
|
||
//
|
||
// Define stack overflow read threshhold.
|
||
//
|
||
|
||
#ifdef _X86_
|
||
#if DBG
|
||
#define OVERFLOW_READ_THRESHHOLD (0xB80)
|
||
#else
|
||
#define OVERFLOW_READ_THRESHHOLD (0xA00)
|
||
#endif
|
||
#else
|
||
#define OVERFLOW_READ_THRESHHOLD (0x1000)
|
||
#endif // _X86_
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
//
|
||
// The following procedure is used to handling read stack overflow operations.
|
||
//
|
||
|
||
VOID
|
||
NtfsStackOverflowRead (
|
||
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) { \
|
||
NtfsRaiseStatus( IrpContext, STATUS_INVALID_USER_BUFFER, NULL, NULL );\
|
||
} \
|
||
}
|
||
|
||
#define CollectReadStats(VCB,OPEN_TYPE,SCB,FCB,BYTE_COUNT) { \
|
||
PFILESYSTEM_STATISTICS FsStats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()]; \
|
||
if (!FlagOn( (FCB)->FcbState, FCB_STATE_SYSTEM_FILE)) { \
|
||
if (NtfsIsTypeCodeUserData( (SCB)->AttributeTypeCode )) { \
|
||
FsStats->UserFileReads += 1; \
|
||
FsStats->UserFileReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} else { \
|
||
FsStats->Ntfs.UserIndexReads += 1; \
|
||
FsStats->Ntfs.UserIndexReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} \
|
||
} else { \
|
||
FsStats->MetaDataReads += 1; \
|
||
FsStats->MetaDataReadBytes += (ULONG)(BYTE_COUNT); \
|
||
\
|
||
if ((SCB) == (VCB)->MftScb) { \
|
||
FsStats->Ntfs.MftReads += 1; \
|
||
FsStats->Ntfs.MftReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} else if ((SCB) == (VCB)->RootIndexScb) { \
|
||
FsStats->Ntfs.RootIndexReads += 1; \
|
||
FsStats->Ntfs.RootIndexReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} else if ((SCB) == (VCB)->BitmapScb) { \
|
||
FsStats->Ntfs.BitmapReads += 1; \
|
||
FsStats->Ntfs.BitmapReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} else if ((SCB) == (VCB)->MftBitmapScb) { \
|
||
FsStats->Ntfs.MftBitmapReads += 1; \
|
||
FsStats->Ntfs.MftBitmapReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} \
|
||
} \
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsFsdRead (
|
||
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 exists
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the IRP
|
||
|
||
--*/
|
||
|
||
{
|
||
TOP_LEVEL_CONTEXT TopLevelContext;
|
||
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
ULONG RetryCount = 0;
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
|
||
ASSERT_IRP( Irp );
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsFsdRead\n") );
|
||
|
||
//
|
||
// Call the common Read routine
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
//
|
||
// Always make the reads appear to be top level. As long as we don't have
|
||
// log file full we won't post these requests. This will prevent paging
|
||
// reads from trying to attach to uninitialized top level requests.
|
||
//
|
||
|
||
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, TRUE, TRUE );
|
||
|
||
do {
|
||
|
||
try {
|
||
|
||
//
|
||
// We are either initiating this request or retrying it.
|
||
//
|
||
|
||
if (IrpContext == NULL) {
|
||
|
||
IrpContext = NtfsCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
||
|
||
if (ThreadTopLevelContext->ScbBeingHotFixed != NULL) {
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_HOTFIX_UNDERWAY );
|
||
}
|
||
|
||
|
||
} else if (Status == STATUS_LOG_FILE_FULL) {
|
||
|
||
NtfsCheckpointForLogFileFull( IrpContext );
|
||
}
|
||
|
||
//
|
||
// If this is an Mdl complete request, don't go through
|
||
// common read.
|
||
//
|
||
|
||
ASSERT(!FlagOn( IrpContext->MinorFunction, IRP_MN_DPC ));
|
||
|
||
if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Calling NtfsCompleteMdl\n") );
|
||
Status = NtfsCompleteMdl( IrpContext, Irp );
|
||
|
||
//
|
||
// Check if we have enough stack space to process this request. If there
|
||
// isn't enough then we will create a new thread to process this single
|
||
// request
|
||
//
|
||
|
||
} else if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) {
|
||
|
||
PKEVENT Event;
|
||
PFILE_OBJECT FileObject;
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PSCB Scb;
|
||
PCCB Ccb;
|
||
PERESOURCE Resource;
|
||
|
||
DebugTrace( 0, Dbg, ("Getting too close to stack limit pass request to Fsp\n") );
|
||
|
||
//
|
||
// Decode the file object to get the Scb
|
||
//
|
||
|
||
FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
|
||
|
||
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
|
||
|
||
//
|
||
// We cannot post any compressed reads, because that would interfere
|
||
// with our reserved buffer strategy. We may currently own
|
||
// NtfsReservedBufferResource, and it is important for our read to
|
||
// be able to get a buffer.
|
||
//
|
||
|
||
ASSERT(Scb->CompressionUnit == 0);
|
||
|
||
//
|
||
// Allocate an event and get shared on the scb. We won't grab the
|
||
// Scb for the paging file path or for non-cached io for our
|
||
// system files.
|
||
//
|
||
|
||
Event = (PKEVENT)ExAllocateFromNPagedLookasideList( &NtfsKeventLookasideList );
|
||
KeInitializeEvent( Event, NotificationEvent, FALSE );
|
||
|
||
if ((FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )
|
||
&& FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) ||
|
||
(NtfsLeqMftRef( &Fcb->FileReference, &VolumeFileReference ))) {
|
||
|
||
//
|
||
// There is nothing to release in this case.
|
||
//
|
||
|
||
Resource = NULL;
|
||
|
||
} else {
|
||
|
||
Resource = Scb->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.
|
||
//
|
||
|
||
NtfsPrePostIrp( IrpContext, Irp );
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
|
||
FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
|
||
|
||
FsRtlPostPagingFileStackOverflow( IrpContext, Event, NtfsStackOverflowRead );
|
||
|
||
} else {
|
||
|
||
FsRtlPostStackOverflow( IrpContext, Event, NtfsStackOverflowRead );
|
||
}
|
||
|
||
//
|
||
// And wait for the worker thread to complete the item
|
||
//
|
||
|
||
(VOID) KeWaitForSingleObject( Event, Executive, KernelMode, FALSE, NULL );
|
||
|
||
Status = STATUS_PENDING;
|
||
|
||
} finally {
|
||
|
||
if (Resource != NULL) {
|
||
|
||
ExReleaseResource( Resource );
|
||
}
|
||
|
||
ExFreeToNPagedLookasideList( &NtfsKeventLookasideList, Event );
|
||
}
|
||
|
||
//
|
||
// Identify read requests which can't wait and post them to the
|
||
// Fsp.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// Capture the auxiliary buffer and clear its address if it
|
||
// is not supposed to be deleted by the I/O system on I/O completion.
|
||
//
|
||
|
||
if (Irp->Tail.Overlay.AuxiliaryBuffer != NULL) {
|
||
|
||
IrpContext->Union.AuxiliaryBuffer =
|
||
(PFSRTL_AUXILIARY_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
|
||
|
||
if (!FlagOn(IrpContext->Union.AuxiliaryBuffer->Flags,
|
||
FSRTL_AUXILIARY_FLAG_DEALLOCATE)) {
|
||
|
||
Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
||
}
|
||
}
|
||
|
||
Status = NtfsCommonRead( IrpContext, Irp, TRUE );
|
||
}
|
||
|
||
break;
|
||
|
||
} except(NtfsExceptionFilter( 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;
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
Status = NtfsProcessException( IrpContext,
|
||
Irp,
|
||
ExceptionCode );
|
||
}
|
||
|
||
//
|
||
// Retry if this is a top level request, and the Irp was not completed due
|
||
// to a retryable error.
|
||
//
|
||
|
||
RetryCount += 1;
|
||
|
||
} while ((Status == STATUS_CANT_WAIT || Status == STATUS_LOG_FILE_FULL) &&
|
||
TopLevelContext.TopLevelRequest);
|
||
|
||
if (ThreadTopLevelContext == &TopLevelContext) {
|
||
NtfsRestoreTopLevelIrp( ThreadTopLevelContext );
|
||
}
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsFsdRead -> %08lx\n", Status) );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
|
||
VOID
|
||
NtfsStackOverflowRead (
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
TOP_LEVEL_CONTEXT TopLevelContext;
|
||
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
||
PIRP_CONTEXT IrpContext = Context;
|
||
|
||
//
|
||
// Make it now look like we can wait for I/O to complete
|
||
//
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
|
||
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, TRUE, FALSE );
|
||
|
||
//
|
||
// Do the read operation protected by a try-except clause
|
||
//
|
||
|
||
try {
|
||
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
||
|
||
//
|
||
// Set the flag to indicate that we are in the overflow thread.
|
||
//
|
||
|
||
TopLevelContext.OverflowReadThread = TRUE;
|
||
|
||
(VOID) NtfsCommonRead( IrpContext, IrpContext->OriginatingIrp, FALSE );
|
||
|
||
} except(NtfsExceptionFilter( 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) NtfsProcessException( IrpContext, IrpContext->OriginatingIrp, ExceptionCode );
|
||
}
|
||
|
||
NtfsRestoreTopLevelIrp( ThreadTopLevelContext );
|
||
|
||
//
|
||
// Set the stack overflow item's event to tell the original
|
||
// thread that we're done and then go get another work item.
|
||
//
|
||
|
||
KeSetEvent( Event, 0, FALSE );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsCommonRead (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN BOOLEAN AcquireScb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine for Read called by both the fsd and fsp
|
||
threads.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process
|
||
|
||
AcquireScb - Indicates if this routine should acquire the scb
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PFILE_OBJECT FileObject;
|
||
|
||
TYPE_OF_OPEN TypeOfOpen;
|
||
PVCB Vcb;
|
||
PFCB Fcb;
|
||
PSCB Scb;
|
||
PCCB Ccb;
|
||
|
||
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
||
|
||
EOF_WAIT_BLOCK EofWaitBlock;
|
||
PFSRTL_ADVANCED_FCB_HEADER Header;
|
||
|
||
PTOP_LEVEL_CONTEXT TopLevelContext;
|
||
|
||
VBO StartingVbo;
|
||
LONGLONG ByteCount;
|
||
LONGLONG ByteRange;
|
||
ULONG RequestedByteCount;
|
||
|
||
PBCB Bcb = NULL;
|
||
|
||
BOOLEAN FoundAttribute = FALSE;
|
||
BOOLEAN PostIrp = FALSE;
|
||
BOOLEAN OplockPostIrp = FALSE;
|
||
|
||
BOOLEAN ScbAcquired = FALSE;
|
||
BOOLEAN ReleaseScb;
|
||
BOOLEAN PagingIoAcquired = FALSE;
|
||
BOOLEAN DoingIoAtEof = FALSE;
|
||
|
||
BOOLEAN Wait;
|
||
BOOLEAN PagingIo;
|
||
BOOLEAN NonCachedIo;
|
||
BOOLEAN SynchronousIo;
|
||
|
||
NTFS_IO_CONTEXT LocalContext;
|
||
|
||
//
|
||
// 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;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_IRP( Irp );
|
||
|
||
//
|
||
// Get the current Irp stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsCommonRead\n") );
|
||
DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
|
||
DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
|
||
DebugTrace( 0, Dbg, ("ByteCount = %08lx\n", IrpSp->Parameters.Read.Length) );
|
||
DebugTrace( 0, Dbg, ("ByteOffset = %016I64x\n", IrpSp->Parameters.Read.ByteOffset) );
|
||
//
|
||
// Extract and decode the file object
|
||
//
|
||
|
||
FileObject = IrpSp->FileObject;
|
||
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
|
||
|
||
//
|
||
// Let's kill invalid read requests.
|
||
//
|
||
|
||
if (TypeOfOpen != UserFileOpen &&
|
||
#ifdef _CAIRO_
|
||
DebugDoit( TypeOfOpen != UserPropertySetOpen && )
|
||
#endif // _CAIRO_
|
||
TypeOfOpen != UserVolumeOpen &&
|
||
TypeOfOpen != StreamFileOpen) {
|
||
|
||
DebugTrace( 0, Dbg, ("Invalid file object for read\n") );
|
||
DebugTrace( -1, Dbg, ("NtfsCommonRead: Exit -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST) );
|
||
|
||
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_INVALID_DEVICE_REQUEST );
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
|
||
//
|
||
// Extract starting Vbo and offset.
|
||
//
|
||
|
||
StartingVbo = IrpSp->Parameters.Read.ByteOffset.QuadPart;
|
||
|
||
ByteCount = IrpSp->Parameters.Read.Length;
|
||
ByteRange = StartingVbo + ByteCount;
|
||
|
||
RequestedByteCount = (ULONG)ByteCount;
|
||
|
||
//
|
||
// Check for a null request, and return immediately
|
||
//
|
||
|
||
if ((ULONG)ByteCount == 0) {
|
||
|
||
DebugTrace( 0, Dbg, ("No bytes to read\n") );
|
||
DebugTrace( -1, Dbg, ("NtfsCommonRead: Exit -> %08lx\n", STATUS_SUCCESS) );
|
||
|
||
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Make sure there is an initialized NtfsIoContext block.
|
||
//
|
||
|
||
if (TypeOfOpen == UserVolumeOpen
|
||
|| NonCachedIo) {
|
||
|
||
//
|
||
// If there is a context pointer, we need to make sure it was
|
||
// allocated and not a stale stack pointer.
|
||
//
|
||
|
||
if (IrpContext->Union.NtfsIoContext == NULL
|
||
|| !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_CONTEXT )) {
|
||
|
||
//
|
||
// If we can wait, use the context on the stack. Otherwise
|
||
// we need to allocate one.
|
||
//
|
||
|
||
if (Wait) {
|
||
|
||
IrpContext->Union.NtfsIoContext = &LocalContext;
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_CONTEXT );
|
||
|
||
} else {
|
||
|
||
IrpContext->Union.NtfsIoContext = (PNTFS_IO_CONTEXT)ExAllocateFromNPagedLookasideList( &NtfsIoContextLookasideList );
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_CONTEXT );
|
||
}
|
||
}
|
||
|
||
RtlZeroMemory( IrpContext->Union.NtfsIoContext, sizeof( NTFS_IO_CONTEXT ));
|
||
|
||
//
|
||
// Store whether we allocated this context structure in the structure
|
||
// itself.
|
||
//
|
||
|
||
IrpContext->Union.NtfsIoContext->AllocatedContext =
|
||
BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_CONTEXT );
|
||
|
||
if (Wait) {
|
||
|
||
KeInitializeEvent( &IrpContext->Union.NtfsIoContext->Wait.SyncEvent,
|
||
NotificationEvent,
|
||
FALSE );
|
||
|
||
} else {
|
||
|
||
IrpContext->Union.NtfsIoContext->PagingIo = PagingIo;
|
||
IrpContext->Union.NtfsIoContext->Wait.Async.ResourceThreadId =
|
||
ExGetCurrentResourceThread();
|
||
|
||
IrpContext->Union.NtfsIoContext->Wait.Async.RequestedByteCount =
|
||
(ULONG)ByteCount;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Handle volume Dasd here.
|
||
//
|
||
|
||
if (TypeOfOpen == UserVolumeOpen) {
|
||
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// If the caller has not asked for extended DASD IO access then
|
||
// limit with the volume size.
|
||
//
|
||
|
||
if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_XTENDED_DASD_IO )) {
|
||
|
||
//
|
||
// If the starting vbo is past the end of the volume, we are done.
|
||
//
|
||
|
||
if (Scb->Header.FileSize.QuadPart <= StartingVbo) {
|
||
|
||
DebugTrace( 0, Dbg, ("No bytes to read\n") );
|
||
DebugTrace( -1, Dbg, ("NtfsCommonRead: Exit -> %08lx\n", STATUS_END_OF_FILE) );
|
||
|
||
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_END_OF_FILE );
|
||
return STATUS_END_OF_FILE;
|
||
|
||
//
|
||
// If the write extends beyond the end of the volume, truncate the
|
||
// bytes to write.
|
||
//
|
||
|
||
} else if (Scb->Header.FileSize.QuadPart < ByteRange) {
|
||
|
||
ByteCount = Scb->Header.FileSize.QuadPart - StartingVbo;
|
||
|
||
if (!Wait) {
|
||
|
||
IrpContext->Union.NtfsIoContext->Wait.Async.RequestedByteCount =
|
||
(ULONG)ByteCount;
|
||
}
|
||
}
|
||
}
|
||
|
||
Status = NtfsVolumeDasdIo( IrpContext,
|
||
Irp,
|
||
Vcb,
|
||
StartingVbo,
|
||
(ULONG)ByteCount );
|
||
|
||
//
|
||
// If the volume was opened for Synchronous IO, update the current
|
||
// file position.
|
||
//
|
||
|
||
if (SynchronousIo && !PagingIo &&
|
||
NT_SUCCESS(Status)) {
|
||
|
||
IrpSp->FileObject->CurrentByteOffset.QuadPart = StartingVbo + Irp->IoStatus.Information;
|
||
}
|
||
|
||
DebugTrace( 0, Dbg, ("Complete with %08lx bytes read\n", Irp->IoStatus.Information) );
|
||
DebugTrace( -1, Dbg, ("NtfsCommonRead: Exit -> %08lx\n", Status) );
|
||
|
||
if (Wait) {
|
||
|
||
NtfsCompleteRequest( &IrpContext, &Irp, Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Keep a pointer to the common fsrtl header.
|
||
//
|
||
|
||
Header = &Scb->Header;
|
||
|
||
//
|
||
// If this is a paging file, just send it to the device driver.
|
||
// We assume Mm is a good citizen.
|
||
//
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )
|
||
&& FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_FILE_DELETED, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Do the usual STATUS_PENDING things.
|
||
//
|
||
|
||
IoMarkIrpPending( Irp );
|
||
|
||
//
|
||
// Perform the actual IO, it will be completed when the io finishes.
|
||
//
|
||
|
||
NtfsPagingFileIo( IrpContext,
|
||
Irp,
|
||
Scb,
|
||
StartingVbo,
|
||
(ULONG)ByteCount );
|
||
|
||
//
|
||
// We, nor anybody else, need the IrpContext any more.
|
||
//
|
||
|
||
NtfsCompleteRequest( &IrpContext, NULL, 0 );
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
//
|
||
// Accumulate interesting statistics.
|
||
//
|
||
|
||
if (PagingIo) {
|
||
CollectReadStats( Vcb, TypeOfOpen, Scb, Fcb, ByteCount );
|
||
}
|
||
|
||
|
||
//
|
||
// Use a try-finally to free Scb and buffers on the way out.
|
||
// At this point we can treat all requests identically since we
|
||
// have a usable Scb for each of them. (Volume, User or Stream file)
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// This case corresponds to a non-directory file read.
|
||
//
|
||
|
||
LONGLONG FileSize;
|
||
LONGLONG ValidDataLength;
|
||
|
||
//
|
||
// 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. This is not necessary for compressed files, since
|
||
// we will turn user noncached writes into cached writes.
|
||
//
|
||
|
||
if (!PagingIo &&
|
||
NonCachedIo &&
|
||
(FileObject->SectionObjectPointer->DataSectionObject != NULL)) {
|
||
|
||
ExAcquireResourceShared( Scb->Header.PagingIoResource, TRUE );
|
||
|
||
if (Scb->CompressionUnit == 0) {
|
||
|
||
//
|
||
// It is possible that this read is part of a top level request or
|
||
// is being called by MM to create an image section. We will update
|
||
// the top-level context to reflect this. All of the exception
|
||
// handling will correctly handle the log file full in this case.
|
||
//
|
||
|
||
TopLevelContext = NtfsGetTopLevelContext();
|
||
|
||
if (TopLevelContext->SavedTopLevelIrp != NULL) {
|
||
|
||
TopLevelContext->TopLevelRequest = FALSE;
|
||
}
|
||
|
||
CcFlushCache( FileObject->SectionObjectPointer,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount,
|
||
&Irp->IoStatus );
|
||
|
||
//
|
||
// Make sure the data got out to disk.
|
||
//
|
||
|
||
ExReleaseResource( Scb->Header.PagingIoResource );
|
||
ExAcquireResourceExclusive( Scb->Header.PagingIoResource, TRUE );
|
||
ExReleaseResource( Scb->Header.PagingIoResource );
|
||
|
||
//
|
||
// Check for errors in the flush.
|
||
//
|
||
|
||
NtfsNormalizeAndCleanupTransaction( IrpContext,
|
||
&Irp->IoStatus.Status,
|
||
TRUE,
|
||
STATUS_UNEXPECTED_IO_ERROR );
|
||
|
||
} else {
|
||
|
||
ExReleaseResource( Scb->Header.PagingIoResource );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We need shared access to the Scb before proceeding.
|
||
// We won't acquire the Scb for a non-cached read of the first 4
|
||
// file records.
|
||
//
|
||
|
||
if (AcquireScb &&
|
||
|
||
(!NonCachedIo || NtfsGtrMftRef( &Fcb->FileReference, &VolumeFileReference))) {
|
||
|
||
//
|
||
// Figure out if we have been entered during the posting
|
||
// of a top level request.
|
||
//
|
||
|
||
TopLevelContext = NtfsGetTopLevelContext();
|
||
|
||
//
|
||
// Initially we always force reads to appear to be top level
|
||
// requests. If we reach this point the read not to the paging
|
||
// file so it is safe to determine if we are really a top level
|
||
// request. If there is an Ntfs request above us we will clear
|
||
// the TopLevelRequest field in the TopLevelContext.
|
||
//
|
||
|
||
if (TopLevelContext->ValidSavedTopLevel) {
|
||
TopLevelContext->TopLevelRequest = FALSE;
|
||
}
|
||
|
||
//
|
||
// If this is not a paging I/O (cached or user noncached I/O),
|
||
// then acquire the paging I/O resource. (Note, you can only
|
||
// do cached I/O to user streams, and they always have a paging
|
||
// I/O resource.
|
||
//
|
||
|
||
if (!PagingIo) {
|
||
|
||
//
|
||
// If we cannot acquire the resource, then raise.
|
||
//
|
||
|
||
if (!ExAcquireSharedWaitForExclusive( Scb->Header.PagingIoResource, Wait )) {
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
PagingIoAcquired = TRUE;
|
||
|
||
//
|
||
// Check if we have already gone through cleanup on this handle.
|
||
//
|
||
|
||
if (FlagOn( Ccb->Flags, CCB_FLAG_CLEANUP )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_FILE_CLOSED, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// The reason that we always handle the user requests through the cache,
|
||
// is that there is no better way to deal with alignment issues, for
|
||
// the frequent case where the user noncached I/O is not an integral of
|
||
// the Compression Unit. Also, the way we synchronize the case where
|
||
// a compression unit is being moved to a different spot on disk during
|
||
// a write, is to keep the pages locked in memory during the write, so
|
||
// that there will be no need to read the disk at the same time. (If
|
||
// we allowed real noncached I/O, then we would somehow have to synchronize
|
||
// the noncached read with the write of the same data.)
|
||
//
|
||
// Bottom line is we can only really support cached reads to compresed
|
||
// files.
|
||
//
|
||
|
||
if ((Scb->CompressionUnit != 0) && NonCachedIo) {
|
||
|
||
NonCachedIo = FALSE;
|
||
|
||
if (Scb->FileObject == NULL) {
|
||
|
||
//
|
||
// Make sure we are serialized with the FileSizes, and
|
||
// will remove this condition if we abort.
|
||
//
|
||
|
||
FsRtlLockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = (PFCB)Scb;
|
||
|
||
NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE );
|
||
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = NULL;
|
||
}
|
||
|
||
FileObject = Scb->FileObject;
|
||
}
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
if (!Wait && NonCachedIo) {
|
||
|
||
if (ExIsResourceAcquiredShared(Scb->Header.PagingIoResource) > MAX_SCB_ASYNC_ACQUIRE) {
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
|
||
IrpContext->Union.NtfsIoContext->Wait.Async.Resource = Scb->Header.PagingIoResource;
|
||
}
|
||
|
||
//
|
||
// Now check if the attribute has been deleted or if the
|
||
// volume has been dismounted.
|
||
//
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED | SCB_STATE_VOLUME_DISMOUNTED)) {
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_FILE_DELETED, NULL, NULL );
|
||
|
||
} else {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is a paging I/O, and there is a paging I/O resource, then
|
||
// we acquire the main resource here. Note that for most paging I/Os
|
||
// (like faulting for cached I/O), we already own the paging I/O resource,
|
||
// so we acquire nothing here! But, for other cases like user-mapped files,
|
||
// we do check if paging I/O is acquired, and acquire the main resource if
|
||
// not. The point is, we need some guarantee still that the file will not
|
||
// be truncated.
|
||
//
|
||
|
||
} else if ((Scb->Header.PagingIoResource != NULL) &&
|
||
!ExIsResourceAcquiredShared(Scb->Header.PagingIoResource)) {
|
||
|
||
//
|
||
// If we cannot acquire the resource, then raise.
|
||
//
|
||
|
||
if (!ExAcquireResourceShared( Scb->Header.Resource, Wait )) {
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
|
||
ScbAcquired = TRUE;
|
||
|
||
//
|
||
// Now check if the attribute has been deleted or if the
|
||
// volume has been dismounted.
|
||
//
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED | SCB_STATE_VOLUME_DISMOUNTED)) {
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_FILE_DELETED, NULL, NULL );
|
||
|
||
} else {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the Scb is uninitialized, we initialize it now.
|
||
//
|
||
|
||
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Initializing Scb -> %08lx\n", Scb) );
|
||
|
||
ReleaseScb = FALSE;
|
||
|
||
if (AcquireScb && !ScbAcquired) {
|
||
|
||
ExAcquireResourceShared( Scb->Header.Resource, TRUE );
|
||
ScbAcquired = TRUE;
|
||
ReleaseScb = TRUE;
|
||
}
|
||
|
||
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
|
||
|
||
if (ReleaseScb) {
|
||
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
ScbAcquired = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We check whether we can proceed
|
||
// based on the state of the file oplocks.
|
||
//
|
||
|
||
if (TypeOfOpen == UserFileOpen) {
|
||
|
||
Status = FsRtlCheckOplock( &Scb->ScbType.Data.Oplock,
|
||
Irp,
|
||
IrpContext,
|
||
NtfsOplockComplete,
|
||
NtfsPrePostIrp );
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
OplockPostIrp = TRUE;
|
||
PostIrp = TRUE;
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// This oplock call can affect whether fast IO is possible.
|
||
// We may have broken an oplock to no oplock held. If the
|
||
// current state of the file is FastIoIsNotPossible then
|
||
// recheck the fast IO state.
|
||
//
|
||
|
||
if (Scb->Header.IsFastIoPossible == FastIoIsNotPossible) {
|
||
|
||
NtfsAcquireFsrtlHeader( Scb );
|
||
Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
}
|
||
|
||
//
|
||
// We have to check for read access according to the current
|
||
// state of the file locks.
|
||
//
|
||
|
||
if (!PagingIo
|
||
&& Scb->ScbType.Data.FileLock != NULL
|
||
&& !FsRtlCheckLockForReadAccess( Scb->ScbType.Data.FileLock,
|
||
Irp )) {
|
||
|
||
try_return( Status = STATUS_FILE_LOCK_CONFLICT );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now synchronize with the FsRtl Header
|
||
//
|
||
|
||
ExAcquireFastMutex( Header->FastMutex );
|
||
|
||
//
|
||
// Now see if we are reading beyond ValidDataLength. We have to
|
||
// do it now so that our reads are not nooped. We only need to block
|
||
// on nonrecursive I/O (cached or page fault to user section, because
|
||
// if it is paging I/O, we must be part of a reader or writer who is
|
||
// synchronized.
|
||
//
|
||
|
||
if ((ByteRange > Header->ValidDataLength.QuadPart) && !PagingIo) {
|
||
|
||
//
|
||
// We must serialize with anyone else doing I/O at beyond
|
||
// ValidDataLength, and then remember if we need to declare
|
||
// when we are done. If our caller has already serialized
|
||
// with EOF then there is nothing for us to do here.
|
||
//
|
||
|
||
if ((IrpContext->TopLevelIrpContext->FcbWithPagingExclusive == Fcb) ||
|
||
(IrpContext->TopLevelIrpContext->FcbWithPagingExclusive == (PFCB) Scb)) {
|
||
|
||
DoingIoAtEof = TRUE;
|
||
|
||
} else {
|
||
|
||
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
|
||
NtfsWaitForIoAtEof( Header,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount,
|
||
&EofWaitBlock );
|
||
|
||
//
|
||
// Set the Flag if we are in fact beyond ValidDataLength.
|
||
//
|
||
|
||
if (DoingIoAtEof) {
|
||
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
|
||
IrpContext->FcbWithPagingExclusive = (PFCB) Scb;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get file sizes from the Scb.
|
||
//
|
||
// We must get ValidDataLength first since it is always
|
||
// increased second (the case we are unprotected) and
|
||
// we don't want to capture ValidDataLength > FileSize.
|
||
//
|
||
|
||
ValidDataLength = Header->ValidDataLength.QuadPart;
|
||
FileSize = Header->FileSize.QuadPart;
|
||
|
||
ExReleaseFastMutex( Header->FastMutex );
|
||
|
||
//
|
||
// Optimize for the case where we are trying to fault in an entire
|
||
// compression unit, even if past the end of the file. Go ahead
|
||
// and round the local FileSize to a compression unit boundary.
|
||
// This will allow all of these pages to come into memory when
|
||
// CC touches the first page out of memory. Otherwise CC will
|
||
// force them into memory one page at a time.
|
||
//
|
||
|
||
if (PagingIo && (Scb->CompressionUnit != 0) && !FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
|
||
FileSize += (Scb->CompressionUnit - 1);
|
||
((PLARGE_INTEGER) &FileSize)->LowPart &= ~(Scb->CompressionUnit - 1);
|
||
}
|
||
|
||
//
|
||
// If the read starts beyond End of File, return EOF.
|
||
//
|
||
|
||
if (StartingVbo >= FileSize) {
|
||
|
||
DebugTrace( 0, Dbg, ("End of File\n") );
|
||
|
||
try_return ( Status = STATUS_END_OF_FILE );
|
||
}
|
||
|
||
//
|
||
// If the read extends beyond EOF, truncate the read
|
||
//
|
||
|
||
if (ByteRange > FileSize) {
|
||
|
||
ByteCount = FileSize - StartingVbo;
|
||
ByteRange = StartingVbo + ByteCount;
|
||
|
||
RequestedByteCount = (ULONG)ByteCount;
|
||
|
||
if (NonCachedIo && !Wait) {
|
||
|
||
IrpContext->Union.NtfsIoContext->Wait.Async.RequestedByteCount =
|
||
(ULONG)ByteCount;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// HANDLE THE NONCACHED RESIDENT ATTRIBUTE CASE
|
||
//
|
||
// We let the cached case take the normal path for the following
|
||
// reasons:
|
||
//
|
||
// o To insure data coherency if a user maps the file
|
||
// o To get a page in the cache to keep the Fcb around
|
||
// o So the data can be accessed via the Fast I/O path
|
||
//
|
||
// The disadvantage is the overhead to fault the data in the
|
||
// first time, but we may be able to do this with asynchronous
|
||
// read ahead.
|
||
//
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT | SCB_STATE_CONVERT_UNDERWAY ) &&
|
||
NonCachedIo) {
|
||
|
||
ReleaseScb = FALSE;
|
||
|
||
if (AcquireScb && !ScbAcquired) {
|
||
ExAcquireResourceShared( Scb->Header.Resource, TRUE );
|
||
ScbAcquired = TRUE;
|
||
ReleaseScb = TRUE;
|
||
}
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )
|
||
&& NonCachedIo) {
|
||
|
||
PUCHAR AttrValue;
|
||
|
||
//
|
||
// Get hold of the user's buffer.
|
||
//
|
||
|
||
SystemBuffer = NtfsMapUserBuffer( Irp );
|
||
|
||
//
|
||
// This is a resident attribute, we need to look it up
|
||
// and copy the desired range of bytes to the user's
|
||
// buffer.
|
||
//
|
||
|
||
NtfsInitializeAttributeContext( &AttrContext );
|
||
FoundAttribute = TRUE;
|
||
|
||
NtfsLookupAttributeForScb( IrpContext,
|
||
Scb,
|
||
NULL,
|
||
&AttrContext );
|
||
|
||
AttrValue = NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
|
||
|
||
RtlCopyMemory( SystemBuffer,
|
||
Add2Ptr( AttrValue, ((ULONG)StartingVbo) ),
|
||
(ULONG)ByteCount );
|
||
|
||
Irp->IoStatus.Information = (ULONG)ByteCount;
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
} else {
|
||
|
||
if (ReleaseScb) {
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
ScbAcquired = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// HANDLE THE NON-CACHED CASE
|
||
//
|
||
|
||
if (NonCachedIo) {
|
||
|
||
ULONG BytesToRead;
|
||
|
||
ULONG SectorSize;
|
||
|
||
ULONG ZeroOffset;
|
||
ULONG ZeroLength = 0;
|
||
|
||
DebugTrace( 0, Dbg, ("Non cached read.\n") );
|
||
|
||
//
|
||
// For a compressed stream, which is user-mapped, reserve space
|
||
// as pages come in.
|
||
//
|
||
|
||
if (FlagOn(Header->Flags, FSRTL_FLAG_USER_MAPPED_FILE) &&
|
||
FlagOn(Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK) &&
|
||
!NtfsReserveClusters(IrpContext, Scb, StartingVbo, (ULONG)ByteCount)) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Start by zeroing any part of the read after Valid Data
|
||
//
|
||
|
||
if (ByteRange > ValidDataLength) {
|
||
|
||
ReleaseScb = FALSE;
|
||
|
||
//
|
||
// We need to look at ValidDataToDisk, because it could be higher.
|
||
//
|
||
|
||
//
|
||
// We have to have the main resource to look at ValidDataToDisk.
|
||
//
|
||
|
||
if (AcquireScb && !ScbAcquired) {
|
||
ExAcquireResourceShared( Scb->Header.Resource, TRUE );
|
||
ScbAcquired = TRUE;
|
||
ReleaseScb = TRUE;
|
||
}
|
||
|
||
//
|
||
// If ValidDataToDisk is actually greater than
|
||
// ValidDataLength, then we must have lost a page
|
||
// during the middle of a write, and we should not
|
||
// zero that data on the way back in!
|
||
//
|
||
|
||
if (ValidDataLength < Scb->ValidDataToDisk) {
|
||
ValidDataLength = Scb->ValidDataToDisk;
|
||
}
|
||
|
||
if (ByteRange > ValidDataLength) {
|
||
|
||
SystemBuffer = NtfsMapUserBuffer( Irp );
|
||
|
||
if (StartingVbo < ValidDataLength) {
|
||
|
||
//
|
||
// Assume we will zero the entire amount.
|
||
//
|
||
|
||
ZeroLength = (ULONG)ByteCount;
|
||
|
||
//
|
||
// The new byte count and the offset to start filling with zeroes.
|
||
//
|
||
|
||
ByteCount = ValidDataLength - StartingVbo;
|
||
ZeroOffset = (ULONG)ByteCount;
|
||
|
||
//
|
||
// Now reduce the amount to zero by the zero offset.
|
||
//
|
||
|
||
ZeroLength -= ZeroOffset;
|
||
|
||
//
|
||
// If this was non-cached I/O then convert it to synchronous.
|
||
// This is because we don't want to zero the buffer now or
|
||
// we will lose the data when the driver purges the cache.
|
||
//
|
||
|
||
if (!Wait) {
|
||
|
||
Wait = TRUE;
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
|
||
|
||
RtlZeroMemory( IrpContext->Union.NtfsIoContext, sizeof( NTFS_IO_CONTEXT ));
|
||
|
||
//
|
||
// Store whether we allocated this context structure in the structure
|
||
// itself.
|
||
//
|
||
|
||
IrpContext->Union.NtfsIoContext->AllocatedContext =
|
||
BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_CONTEXT );
|
||
|
||
KeInitializeEvent( &IrpContext->Union.NtfsIoContext->Wait.SyncEvent,
|
||
NotificationEvent,
|
||
FALSE );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// All we have to do now is sit here and zero the
|
||
// user's buffer, no reading is required.
|
||
//
|
||
|
||
SafeZeroMemory( (PUCHAR)SystemBuffer, (ULONG)ByteCount );
|
||
|
||
Irp->IoStatus.Information = (ULONG)ByteCount;
|
||
|
||
try_return ( Status = STATUS_SUCCESS );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now free the Scb if we only acquired it here.
|
||
//
|
||
|
||
if (ReleaseScb) {
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
ScbAcquired = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the sector size
|
||
//
|
||
|
||
SectorSize = Vcb->BytesPerSector;
|
||
|
||
//
|
||
// Round up to a sector boundry
|
||
//
|
||
|
||
BytesToRead = ((ULONG)ByteCount + (SectorSize - 1)) & ~(SectorSize - 1);
|
||
|
||
//
|
||
// Call a special routine if we do not have sector alignment
|
||
// and the file is not compressed.
|
||
//
|
||
|
||
if ((((ULONG)StartingVbo) & (SectorSize - 1)
|
||
|| BytesToRead > IrpSp->Parameters.Read.Length)
|
||
|
||
&&
|
||
|
||
(Scb->CompressionUnit == 0)) {
|
||
|
||
//
|
||
// If we can't wait, we must post this.
|
||
//
|
||
|
||
if (!Wait) {
|
||
|
||
try_return( PostIrp = TRUE );
|
||
}
|
||
|
||
//
|
||
// Do the physical read.
|
||
//
|
||
|
||
ASSERT(FileObject->SectionObjectPointer == &Scb->NonpagedScb->SegmentObject);
|
||
|
||
NtfsNonCachedNonAlignedIo( IrpContext,
|
||
Irp,
|
||
Scb,
|
||
StartingVbo,
|
||
(ULONG)ByteCount );
|
||
|
||
BytesToRead = (ULONG)ByteCount;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Just to help reduce 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.
|
||
//
|
||
|
||
//
|
||
// Perform the actual IO
|
||
//
|
||
|
||
if (NtfsNonCachedIo( IrpContext,
|
||
Irp,
|
||
Scb,
|
||
StartingVbo,
|
||
BytesToRead,
|
||
(FileObject->SectionObjectPointer != &Scb->NonpagedScb->SegmentObject) )
|
||
== STATUS_PENDING) {
|
||
|
||
IrpContext->Union.NtfsIoContext = NULL;
|
||
PagingIoAcquired = FALSE;
|
||
Irp = NULL;
|
||
|
||
try_return( Status = STATUS_PENDING );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the call didn't succeed, raise the error status
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
|
||
|
||
NtfsNormalizeAndRaiseStatus( IrpContext,
|
||
Status,
|
||
STATUS_UNEXPECTED_IO_ERROR );
|
||
}
|
||
|
||
//
|
||
// 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 > (ULONG)ByteCount) {
|
||
|
||
if (SystemBuffer == NULL) {
|
||
|
||
SystemBuffer = NtfsMapUserBuffer( Irp );
|
||
}
|
||
|
||
SafeZeroMemory( (PUCHAR)SystemBuffer + (ULONG)ByteCount,
|
||
BytesToRead - (ULONG)ByteCount );
|
||
}
|
||
|
||
//
|
||
// If we need to zero the tail of the buffer because of valid data
|
||
// then do so now.
|
||
//
|
||
|
||
if (ZeroLength != 0) {
|
||
|
||
if (SystemBuffer == NULL) {
|
||
|
||
SystemBuffer = NtfsMapUserBuffer( Irp );
|
||
}
|
||
|
||
SafeZeroMemory( Add2Ptr( SystemBuffer, ZeroOffset ), ZeroLength );
|
||
}
|
||
|
||
//
|
||
// The transfer is complete.
|
||
//
|
||
|
||
try_return( Status );
|
||
|
||
} // if No Intermediate Buffering
|
||
|
||
|
||
//
|
||
// HANDLE THE CACHED CASE
|
||
//
|
||
|
||
else {
|
||
|
||
//
|
||
// We need to go through the cache for this
|
||
// file object. First handle the noncompressed calls.
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
if (!FlagOn(IrpContext->MinorFunction, IRP_MN_COMPRESSED)) {
|
||
#endif _CAIRO_
|
||
|
||
//
|
||
// 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") );
|
||
|
||
//
|
||
// Now initialize the cache map.
|
||
//
|
||
// Make sure we are serialized with the FileSizes, and
|
||
// will remove this condition if we abort.
|
||
//
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlLockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = (PFCB)Scb;
|
||
}
|
||
|
||
CcInitializeCacheMap( FileObject,
|
||
(PCC_FILE_SIZES)&Header->AllocationSize,
|
||
FALSE,
|
||
&NtfsData.CacheManagerCallbacks,
|
||
Scb );
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = NULL;
|
||
}
|
||
|
||
CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY );
|
||
}
|
||
|
||
//
|
||
// DO A NORMAL CACHED READ, if the MDL bit is not set,
|
||
//
|
||
|
||
DebugTrace( 0, Dbg, ("Cached read.\n") );
|
||
|
||
if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
|
||
|
||
//
|
||
// Get hold of the user's buffer.
|
||
//
|
||
|
||
SystemBuffer = NtfsMapUserBuffer( Irp );
|
||
|
||
//
|
||
// Now try to do the copy.
|
||
//
|
||
|
||
if (!CcCopyRead( FileObject,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount,
|
||
Wait,
|
||
SystemBuffer,
|
||
&Irp->IoStatus )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Cached Read could not wait\n") );
|
||
|
||
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") );
|
||
|
||
ASSERT( Wait );
|
||
|
||
CcMdlRead( FileObject,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount,
|
||
&Irp->MdlAddress,
|
||
&Irp->IoStatus );
|
||
|
||
Status = Irp->IoStatus.Status;
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
try_return( Status );
|
||
}
|
||
|
||
//
|
||
// Handle the compressed calls.
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
} else {
|
||
|
||
PCOMPRESSED_DATA_INFO CompressedDataInfo;
|
||
PMDL *NewMdl;
|
||
|
||
ASSERT((StartingVbo & (NTFS_CHUNK_SIZE - 1)) == 0);
|
||
|
||
if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
|
||
try_return(Status = STATUS_INVALID_READ_MODE);
|
||
}
|
||
|
||
if ((Header->FileObjectC == NULL) ||
|
||
(Header->FileObjectC->PrivateCacheMap == NULL)) {
|
||
|
||
//
|
||
// Make sure we are serialized with the FileSizes, and
|
||
// will remove this condition if we abort.
|
||
//
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlLockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = (PFCB)Scb;
|
||
}
|
||
|
||
NtfsCreateInternalCompressedStream( IrpContext, Scb, FALSE );
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Assume success.
|
||
//
|
||
|
||
Irp->IoStatus.Status = Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = (ULONG)(ByteRange - StartingVbo);
|
||
|
||
//
|
||
// Based on the Mdl minor function, set up the appropriate
|
||
// parameters for the call below.
|
||
//
|
||
|
||
if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
|
||
|
||
//
|
||
// Get hold of the user's buffer.
|
||
//
|
||
|
||
SystemBuffer = NtfsMapUserBuffer( Irp );
|
||
NewMdl = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We will deliver the Mdl directly to the Irp.
|
||
//
|
||
|
||
SystemBuffer = NULL;
|
||
NewMdl = &Irp->MdlAddress;
|
||
}
|
||
|
||
CompressedDataInfo = (PCOMPRESSED_DATA_INFO)IrpContext->Union.AuxiliaryBuffer->Buffer;
|
||
|
||
CompressedDataInfo->CompressionFormatAndEngine =
|
||
(USHORT)((Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK) + 1);
|
||
CompressedDataInfo->CompressionUnitShift = (UCHAR)(Scb->CompressionUnitShift + Vcb->ClusterShift);
|
||
CompressedDataInfo->ChunkShift = NTFS_CHUNK_SHIFT;
|
||
CompressedDataInfo->ClusterShift = (UCHAR)Vcb->ClusterShift;
|
||
CompressedDataInfo->Reserved = 0;
|
||
CompressedDataInfo->NumberOfChunks = 0;
|
||
|
||
//
|
||
// Do the compressed read in common code with the Fast Io path.
|
||
// We do it from a loop because we may need to create the other
|
||
// data stream.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
Status = NtfsCompressedCopyRead( FileObject,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount,
|
||
SystemBuffer,
|
||
NewMdl,
|
||
CompressedDataInfo,
|
||
IrpContext->Union.AuxiliaryBuffer->Length,
|
||
IoGetRelatedDeviceObject(FileObject),
|
||
Header,
|
||
Scb->CompressionUnit,
|
||
NTFS_CHUNK_SIZE );
|
||
|
||
//
|
||
// On successful Mdl requests we hang on to the PagingIo resource.
|
||
//
|
||
|
||
if ((NewMdl != NULL) && NT_SUCCESS(Status)) {
|
||
PagingIoAcquired = FALSE;
|
||
}
|
||
|
||
//
|
||
// Check for the status that says we need to create the normal
|
||
// data stream, else we are done.
|
||
//
|
||
|
||
if (Status != STATUS_NOT_MAPPED_DATA) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Create the normal data stream and loop back to try again.
|
||
//
|
||
|
||
ASSERT(Scb->FileObject == NULL);
|
||
|
||
//
|
||
// Make sure we are serialized with the FileSizes, and
|
||
// will remove this condition if we abort.
|
||
//
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlLockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = (PFCB)Scb;
|
||
}
|
||
|
||
NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE );
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->FcbWithPagingExclusive = NULL;
|
||
}
|
||
}
|
||
}
|
||
#endif _CAIRO_
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
|
||
//
|
||
// If the request was not posted, deal with it.
|
||
//
|
||
|
||
if (Irp) {
|
||
|
||
if (!PostIrp) {
|
||
|
||
LONGLONG 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. Make sure to use the original file object
|
||
// not an internal stream we may use within this routine.
|
||
//
|
||
|
||
if (!PagingIo) {
|
||
|
||
if (SynchronousIo) {
|
||
|
||
IrpSp->FileObject->CurrentByteOffset.QuadPart = StartingVbo + ActualBytesRead;
|
||
}
|
||
|
||
//
|
||
// On success, do the following to let us update last access time.
|
||
//
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
SetFlag( IrpSp->FileObject->Flags, FO_FILE_FAST_IO_READ );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Abort transaction on error by raising.
|
||
//
|
||
|
||
NtfsCleanupTransaction( IrpContext, Status, FALSE );
|
||
|
||
} else {
|
||
|
||
DebugTrace( 0, Dbg, ("Passing request to Fsp\n") );
|
||
|
||
if (!OplockPostIrp) {
|
||
|
||
Status = NtfsPostRequest( IrpContext, Irp );
|
||
}
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsCommonRead );
|
||
|
||
//
|
||
// Clean up any Bcb from read compressed.
|
||
//
|
||
|
||
if (Bcb != NULL) {
|
||
|
||
CcUnpinData( Bcb );
|
||
}
|
||
|
||
//
|
||
// If the Scb has been acquired, release it.
|
||
//
|
||
|
||
if (PagingIoAcquired) {
|
||
|
||
ExReleaseResource( Scb->Header.PagingIoResource );
|
||
}
|
||
|
||
if (Irp) {
|
||
|
||
if (ScbAcquired) {
|
||
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free the attribute enumeration context if
|
||
// used.
|
||
//
|
||
|
||
if (FoundAttribute) {
|
||
|
||
NtfsCleanupAttributeContext( &AttrContext );
|
||
}
|
||
|
||
//
|
||
// Complete the request if we didn't post it and no exception
|
||
//
|
||
// Note that NtfsCompleteRequest does the right thing if either
|
||
// IrpContext or Irp are NULL
|
||
//
|
||
|
||
if (!PostIrp && !AbnormalTermination()) {
|
||
|
||
NtfsCompleteRequest( &IrpContext,
|
||
Irp ? &Irp : NULL,
|
||
Status );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsCommonRead -> %08lx\n", Status) );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|