2521 lines
81 KiB
C
2521 lines
81 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"
|
||
#include "lockorder.h"
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_READ)
|
||
|
||
#ifdef NTFS_RWC_DEBUG
|
||
PRWC_HISTORY_ENTRY
|
||
NtfsGetHistoryEntry (
|
||
IN PSCB Scb
|
||
);
|
||
BOOLEAN NtfsBreakOnConflict = TRUE;
|
||
#endif
|
||
|
||
//
|
||
// Define stack overflow read threshhold.
|
||
//
|
||
|
||
#ifdef _X86_
|
||
#if DBG
|
||
#define OVERFLOW_READ_THRESHHOLD (0xD00)
|
||
#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
|
||
NtfsNonCachedResidentRead (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PSCB Scb,
|
||
IN ULONG StartingVbo,
|
||
IN ULONG ByteCount
|
||
);
|
||
|
||
//
|
||
// 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) { \
|
||
PFILE_SYSTEM_STATISTICS FsStats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()]; \
|
||
if (!FlagOn( (FCB)->FcbState, FCB_STATE_SYSTEM_FILE)) { \
|
||
if (NtfsIsTypeCodeUserData( (SCB)->AttributeTypeCode )) { \
|
||
FsStats->Common.UserFileReads += 1; \
|
||
FsStats->Common.UserFileReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} else { \
|
||
FsStats->Ntfs.UserIndexReads += 1; \
|
||
FsStats->Ntfs.UserIndexReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} \
|
||
} else { \
|
||
if ((SCB) != (VCB)->LogFileScb) { \
|
||
FsStats->Common.MetaDataReads += 1; \
|
||
FsStats->Common.MetaDataReadBytes += (ULONG)(BYTE_COUNT); \
|
||
} else { \
|
||
FsStats->Ntfs.LogFileReads += 1; \
|
||
FsStats->Ntfs.LogFileReadBytes += (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:
|
||
|
||
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:
|
||
|
||
IrpContext - If present this an IrpContext put on the caller's stack
|
||
to avoid having to allocate it from pool.
|
||
|
||
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;
|
||
|
||
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 = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
|
||
|
||
ASSERT( ThreadTopLevelContext == &TopLevelContext );
|
||
|
||
do {
|
||
|
||
try {
|
||
|
||
//
|
||
// We are either initiating this request or retrying it.
|
||
//
|
||
|
||
if (IrpContext == NULL) {
|
||
|
||
//
|
||
// Allocate the Irp and update the top level storage. For synchronous
|
||
// paging io allocate the irp on the stack
|
||
//
|
||
|
||
if (CanFsdWait( Irp ) && FlagOn( Irp->Flags, IRP_PAGING_IO )) {
|
||
|
||
IrpContext = (PIRP_CONTEXT) NtfsAllocateFromStack( sizeof( IRP_CONTEXT ));
|
||
}
|
||
|
||
NtfsInitializeIrpContext( Irp, CanFsdWait( Irp ), &IrpContext );
|
||
|
||
if (ThreadTopLevelContext->ScbBeingHotFixed != NULL) {
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_HOTFIX_UNDERWAY );
|
||
}
|
||
|
||
//
|
||
// Initialize the thread top level structure, if needed.
|
||
//
|
||
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
||
|
||
} 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) {
|
||
|
||
KEVENT 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 );
|
||
if ((TypeOfOpen != UserFileOpen) &&
|
||
(TypeOfOpen != StreamFileOpen) &&
|
||
(TypeOfOpen != UserVolumeOpen)) {
|
||
|
||
NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// 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) ||
|
||
!ExIsResourceAcquiredExclusiveLite(&NtfsReservedBufferResource) );
|
||
|
||
//
|
||
// To avoid deadlocks we only should post recursive paging file and mft requests
|
||
// o.w we might need to do lockups for example and reacquire the main in a diff. thread
|
||
// from where it was preacquired
|
||
//
|
||
|
||
// ASSERT( (Scb == Vcb->MftScb) || (FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE )) );
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
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 {
|
||
|
||
NtfsAcquireResourceShared( IrpContext, Scb, TRUE );
|
||
Resource = Scb->Header.Resource;
|
||
|
||
}
|
||
|
||
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) {
|
||
|
||
NtfsReleaseResource( IrpContext, Scb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
|
||
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsFsdRead -> %08lx\n", Status) );
|
||
|
||
return Status;
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
}
|
||
|
||
|
||
//
|
||
// 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->State, IRP_CONTEXT_STATE_WAIT );
|
||
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &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.
|
||
//
|
||
|
||
ThreadTopLevelContext->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 );
|
||
}
|
||
|
||
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
||
|
||
//
|
||
// 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;
|
||
|
||
PNTFS_ADVANCED_FCB_HEADER Header;
|
||
|
||
PTOP_LEVEL_CONTEXT TopLevelContext;
|
||
|
||
VBO StartingVbo;
|
||
LONGLONG ByteCount;
|
||
LONGLONG ByteRange;
|
||
ULONG RequestedByteCount;
|
||
|
||
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;
|
||
#ifdef COMPRESS_ON_WIRE
|
||
PCOMPRESSION_SYNC CompressionSync = NULL;
|
||
BOOLEAN CompressedIo = FALSE;
|
||
#endif
|
||
|
||
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) &&
|
||
(TypeOfOpen != StreamFileOpen) &&
|
||
(TypeOfOpen != UserVolumeOpen)) {
|
||
|
||
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 = (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
PagingIo = BooleanFlagOn( Irp->Flags, IRP_PAGING_IO );
|
||
NonCachedIo = BooleanFlagOn( Irp->Flags,IRP_NOCACHE );
|
||
SynchronousIo = BooleanFlagOn( FileObject->Flags, FO_SYNCHRONOUS_IO );
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
if (FileObject->SectionObjectPointer == &Scb->NonpagedScb->SegmentObjectC) {
|
||
|
||
CompressedIo = TRUE;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Extract starting Vbo and offset.
|
||
//
|
||
|
||
StartingVbo = IrpSp->Parameters.Read.ByteOffset.QuadPart;
|
||
ByteCount = (LONGLONG)IrpSp->Parameters.Read.Length;
|
||
|
||
//
|
||
// Check for overflow and underflow.
|
||
//
|
||
|
||
if (MAXLONGLONG - StartingVbo < ByteCount) {
|
||
|
||
ASSERT( !PagingIo );
|
||
|
||
NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Convert all paging i/o against a usa_protected_sequence or compressed file
|
||
// to synchrnonous because we must do the transform after finishing the i/o
|
||
// If the header isn't initialized just do the op synchronous rather than
|
||
// trying to figure out if its compressed by resyncing with disk
|
||
//
|
||
|
||
if (!Wait &&
|
||
PagingIo &&
|
||
(FlagOn( Scb->ScbState, SCB_STATE_USA_PRESENT ) ||
|
||
Scb->CompressionUnit != 0 ||
|
||
Scb->EncryptionContext) ||
|
||
!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
|
||
|
||
Wait = TRUE;
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
}
|
||
|
||
|
||
//
|
||
// 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->State, IRP_CONTEXT_STATE_ALLOC_IO_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->State, IRP_CONTEXT_STATE_ALLOC_IO_CONTEXT );
|
||
|
||
} else {
|
||
|
||
IrpContext->Union.NtfsIoContext = (PNTFS_IO_CONTEXT)ExAllocateFromNPagedLookasideList( &NtfsIoContextLookasideList );
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_IO_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->State, IRP_CONTEXT_STATE_ALLOC_IO_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)) {
|
||
|
||
//
|
||
// Acquire the paging exclusive to avoid collided flushes
|
||
//
|
||
|
||
ExAcquireResourceExclusiveLite( Scb->Header.PagingIoResource, TRUE );
|
||
|
||
if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
if (ScbIsBeingLogged( Scb )) {
|
||
ULONG Flags = SCE_FLAG_READ;
|
||
|
||
if (PagingIo)
|
||
{
|
||
Flags |= SCE_FLAG_PAGING;
|
||
}
|
||
if (!SynchronousIo)
|
||
{
|
||
Flags |= SCE_FLAG_ASYNC;
|
||
}
|
||
|
||
FsRtlLogSyscacheEvent( Scb, SCE_CC_FLUSH, Flags, StartingVbo, ByteCount, 0 );
|
||
}
|
||
#endif
|
||
|
||
|
||
CcFlushCache( FileObject->SectionObjectPointer,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount,
|
||
&Irp->IoStatus );
|
||
|
||
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
|
||
//
|
||
// Check for errors in the flush.
|
||
//
|
||
|
||
NtfsNormalizeAndCleanupTransaction( IrpContext,
|
||
&Irp->IoStatus.Status,
|
||
TRUE,
|
||
STATUS_UNEXPECTED_IO_ERROR );
|
||
|
||
} else {
|
||
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
}
|
||
|
||
}
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
|
||
//
|
||
// For top-level noncached reads (including page reads and read-ahead paged reads),
|
||
// to the normal data section when a compressed section exists, we have to flush the
|
||
// range in the compressed section first. Note that NtfsSynchronizeUncompressedIo
|
||
// is used to synchronize the cached case below.
|
||
//
|
||
// Currently only cached access is supported to the compressed section, and the
|
||
// coherency to that section is synchronized in rwcmpsup.c. You do not see a similar
|
||
// block of code in write.c, which would only be concerned about user-mapped files,
|
||
// since user-mapping is incompatible with writes to the compressed stream, and in
|
||
// fact the user mapper would break the oplocks that allow the only compressed stream
|
||
// access supported at this time.
|
||
//
|
||
|
||
if ((Scb->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) &&
|
||
!CompressedIo &&
|
||
NonCachedIo &&
|
||
(NtfsGetTopLevelContext()->SavedTopLevelIrp == NULL)) {
|
||
|
||
LONGLONG LocalVbo;
|
||
ULONG LocalCount;
|
||
|
||
ExAcquireResourceSharedLite( Scb->Header.PagingIoResource, TRUE );
|
||
|
||
LocalVbo = StartingVbo & ~((ULONG_PTR)Scb->CompressionUnit - 1);
|
||
LocalCount = (ULONG)(ByteCount + (StartingVbo - LocalVbo));
|
||
LocalCount = (LocalCount + Scb->CompressionUnit - 1) & ~(Scb->CompressionUnit - 1);
|
||
|
||
CcFlushCache( &Scb->NonpagedScb->SegmentObjectC,
|
||
(PLARGE_INTEGER)&LocalVbo,
|
||
LocalCount,
|
||
&Irp->IoStatus );
|
||
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
|
||
#ifdef NTFS_RWC_DEBUG
|
||
ASSERT( !NtfsBreakOnConflict ||
|
||
(IrpContext->TopLevelIrpContext->ExceptionStatus != STATUS_CANT_WAIT) );
|
||
#endif
|
||
|
||
//
|
||
// Check for errors in the flush.
|
||
//
|
||
|
||
NtfsNormalizeAndCleanupTransaction( IrpContext,
|
||
&Irp->IoStatus.Status,
|
||
TRUE,
|
||
STATUS_UNEXPECTED_IO_ERROR );
|
||
|
||
}
|
||
#endif
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// If this is async I/O save away the async resource.
|
||
//
|
||
|
||
if (!Wait && NonCachedIo) {
|
||
|
||
IrpContext->Union.NtfsIoContext->Wait.Async.Resource = Scb->Header.PagingIoResource;
|
||
}
|
||
|
||
//
|
||
// 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 (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) && 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->CleanupStructure = Scb;
|
||
|
||
NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE, NULL );
|
||
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->CleanupStructure = NULL;
|
||
}
|
||
|
||
FileObject = Scb->FileObject;
|
||
}
|
||
|
||
//
|
||
// 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) &&
|
||
!NtfsIsSharedScbPagingIo( Scb )) {
|
||
|
||
//
|
||
// If we cannot acquire the resource, then raise.
|
||
//
|
||
|
||
if (!NtfsAcquireResourceShared( IrpContext, Scb, Wait )) {
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
|
||
ScbAcquired = TRUE;
|
||
|
||
//
|
||
// If this is async I/O save away the async resource.
|
||
//
|
||
|
||
if (!Wait && NonCachedIo) {
|
||
IrpContext->Union.NtfsIoContext->Wait.Async.Resource = Scb->Header.Resource;
|
||
}
|
||
|
||
|
||
//
|
||
// 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) {
|
||
|
||
NtfsAcquireResourceShared( IrpContext, Scb, TRUE );
|
||
ScbAcquired = TRUE;
|
||
ReleaseScb = TRUE;
|
||
}
|
||
|
||
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
|
||
|
||
if (ReleaseScb) {
|
||
|
||
NtfsReleaseResource( IrpContext, Scb );
|
||
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
|
||
//
|
||
|
||
NtfsAcquireFsrtlHeader( (PSCB) Header );
|
||
|
||
//
|
||
// 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->CleanupStructure == Fcb) ||
|
||
(IrpContext->TopLevelIrpContext->CleanupStructure == Scb)) {
|
||
|
||
DoingIoAtEof = TRUE;
|
||
|
||
} else {
|
||
|
||
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
|
||
NtfsWaitForIoAtEof( Header,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount );
|
||
|
||
//
|
||
// Set the Flag if we are in fact beyond ValidDataLength.
|
||
//
|
||
|
||
if (DoingIoAtEof) {
|
||
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
|
||
IrpContext->CleanupStructure = Scb;
|
||
|
||
#if (DBG || defined( NTFS_FREE_ASSERTS ))
|
||
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
|
||
|
||
} else {
|
||
|
||
ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
NtfsReleaseFsrtlHeader( (PSCB) Header );
|
||
|
||
//
|
||
// 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) {
|
||
|
||
if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
|
||
!FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
|
||
|
||
FileSize += (Scb->CompressionUnit - 1);
|
||
((PLARGE_INTEGER) &FileSize)->LowPart &= ~(Scb->CompressionUnit - 1);
|
||
}
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
|
||
//
|
||
// If we are reading the compressed stream then we may need the
|
||
// data past file size.
|
||
//
|
||
|
||
if (CompressedIo) {
|
||
|
||
ValidDataLength += (Scb->CompressionUnit - 1);
|
||
((PLARGE_INTEGER) &ValidDataLength)->LowPart &= ~(Scb->CompressionUnit - 1);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If this is the Usn Journal then bias the Io to the correct location in the
|
||
// file.
|
||
//
|
||
|
||
if (FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) {
|
||
|
||
StartingVbo += Vcb->UsnCacheBias;
|
||
ByteRange = StartingVbo + (LONGLONG) IrpSp->Parameters.Read.Length;
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
|
||
#ifdef NTFS_RWC_DEBUG
|
||
#ifdef COMPRESS_ON_WIRE
|
||
if (CompressedIo &&
|
||
(StartingVbo < NtfsRWCHighThreshold) &&
|
||
(ByteRange > NtfsRWCLowThreshold)) {
|
||
|
||
PRWC_HISTORY_ENTRY NextBuffer;
|
||
|
||
NextBuffer = NtfsGetHistoryEntry( Scb );
|
||
|
||
NextBuffer->Operation = TrimCompressedRead;
|
||
NextBuffer->Information = Scb->Header.FileSize.LowPart;
|
||
NextBuffer->FileOffset = (ULONG) StartingVbo;
|
||
NextBuffer->Length = (ULONG) ByteRange;
|
||
}
|
||
#endif
|
||
#endif
|
||
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) {
|
||
NtfsAcquireResourceShared( IrpContext, Scb, TRUE );
|
||
ScbAcquired = TRUE;
|
||
ReleaseScb = TRUE;
|
||
}
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
|
||
|
||
NtfsNonCachedResidentRead( IrpContext, Irp, Scb, (ULONG)StartingVbo, (ULONG)ByteCount );
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
} else {
|
||
|
||
if (ReleaseScb) {
|
||
NtfsReleaseResource( IrpContext, Scb );
|
||
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 );
|
||
}
|
||
|
||
//
|
||
// If this is a read of an encrypted file then make it synchronous. We
|
||
// need to do this so that the encryption driver has a thread to run in.
|
||
//
|
||
|
||
if ((Scb->EncryptionContext != NULL) &&
|
||
!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ) &&
|
||
(NtfsData.EncryptionCallBackTable.AfterReadProcess != NULL) &&
|
||
NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
|
||
|
||
Wait = TRUE;
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_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->State, IRP_CONTEXT_STATE_ALLOC_IO_CONTEXT );
|
||
|
||
KeInitializeEvent( &IrpContext->Union.NtfsIoContext->Wait.SyncEvent,
|
||
NotificationEvent,
|
||
FALSE );
|
||
}
|
||
|
||
//
|
||
// Start by zeroing any part of the read after Valid Data
|
||
//
|
||
|
||
if (ByteRange > ValidDataLength) {
|
||
|
||
ReleaseScb = FALSE;
|
||
|
||
if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
||
|
||
//
|
||
// For compressed files we need to look at ValidDataToDisk, because it could be higher.
|
||
// This requires the main resource
|
||
//
|
||
|
||
if (AcquireScb && !ScbAcquired) {
|
||
NtfsAcquireResourceShared( IrpContext, Scb, 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->State, IRP_CONTEXT_STATE_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->State, IRP_CONTEXT_STATE_ALLOC_IO_CONTEXT );
|
||
|
||
KeInitializeEvent( &IrpContext->Union.NtfsIoContext->Wait.SyncEvent,
|
||
NotificationEvent,
|
||
FALSE );
|
||
}
|
||
|
||
//
|
||
// Reserve the clusters in the range beyond VDL
|
||
//
|
||
|
||
if ((PagingIo) &&
|
||
(FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) &&
|
||
(FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ) == ATTRIBUTE_FLAG_SPARSE)) {
|
||
|
||
if (!NtfsReserveClusters( IrpContext,
|
||
Scb,
|
||
ZeroOffset,
|
||
ZeroLength )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
|
||
}
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Reserve space for mapped sparse files which would normally be
|
||
// done in NtfsPrepareBuffers
|
||
//
|
||
|
||
if ((PagingIo) &&
|
||
(FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) &&
|
||
(FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ) == ATTRIBUTE_FLAG_SPARSE)) {
|
||
|
||
if (!NtfsReserveClusters( IrpContext,
|
||
Scb,
|
||
StartingVbo,
|
||
(ULONG)ByteCount )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
|
||
}
|
||
}
|
||
|
||
//
|
||
// All we have to do now is sit here and zero the
|
||
// user's buffer, no reading is required.
|
||
//
|
||
|
||
SafeZeroMemory( (PUCHAR)SystemBuffer, (ULONG)ByteCount );
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
if (ScbIsBeingLogged( Scb )) {
|
||
ULONG Flags = SCE_FLAG_READ;
|
||
|
||
if (PagingIo)
|
||
{
|
||
Flags |= SCE_FLAG_PAGING;
|
||
}
|
||
if (!SynchronousIo)
|
||
{
|
||
Flags |= SCE_FLAG_ASYNC;
|
||
}
|
||
|
||
ASSERT( Scb->NonpagedScb->SegmentObject.ImageSectionObject == NULL );
|
||
|
||
FsRtlLogSyscacheEvent( Scb, SCE_ZERO_NC, Flags, StartingVbo, ByteCount, ValidDataLength );
|
||
}
|
||
#endif
|
||
|
||
|
||
Irp->IoStatus.Information = (ULONG)ByteCount;
|
||
|
||
try_return ( Status = STATUS_SUCCESS );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now free the Scb if we only acquired it here.
|
||
//
|
||
|
||
if (ReleaseScb) {
|
||
NtfsReleaseResource( IrpContext, Scb );
|
||
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))
|
||
|
||
&&
|
||
|
||
!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
||
|
||
//
|
||
// If we can't wait, we must post this.
|
||
//
|
||
|
||
if (!Wait) {
|
||
|
||
try_return( PostIrp = TRUE );
|
||
}
|
||
|
||
//
|
||
// Do the physical read.
|
||
//
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
ASSERT( !CompressedIo );
|
||
#endif
|
||
ASSERT( Wait );
|
||
|
||
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 - all resources will be released by
|
||
// the completion routine after this point if its successful
|
||
//
|
||
|
||
if (NtfsNonCachedIo( IrpContext,
|
||
Irp,
|
||
Scb,
|
||
StartingVbo,
|
||
BytesToRead,
|
||
#ifdef COMPRESS_ON_WIRE
|
||
(CompressedIo ? COMPRESSED_STREAM : 0)
|
||
#else
|
||
0
|
||
#endif
|
||
) == STATUS_PENDING ) {
|
||
|
||
IrpContext->Union.NtfsIoContext = NULL;
|
||
PagingIoAcquired = FALSE;
|
||
#ifdef NTFSDBG
|
||
//
|
||
// Reflect transfer of main ownership to completion routine
|
||
//
|
||
|
||
if (ScbAcquired) {
|
||
|
||
NTFS_RESOURCE_NAME ResourceName;
|
||
|
||
ResourceName = NtfsIdentifyFcb( Vcb, Scb->Fcb );
|
||
NtfsChangeResourceOrderState( IrpContext, ResourceName, TRUE, FALSE );
|
||
}
|
||
#endif
|
||
|
||
ScbAcquired = 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 );
|
||
}
|
||
|
||
#ifdef NTFS_RWC_DEBUG
|
||
if (CompressedIo &&
|
||
(StartingVbo + ByteCount < NtfsRWCHighThreshold) &&
|
||
(StartingVbo + BytesToRead > NtfsRWCLowThreshold)) {
|
||
|
||
PRWC_HISTORY_ENTRY NextBuffer;
|
||
|
||
NextBuffer = NtfsGetHistoryEntry( Scb );
|
||
|
||
NextBuffer->Operation = ZeroCompressedRead;
|
||
NextBuffer->Information = Scb->Header.ValidDataLength.LowPart;
|
||
NextBuffer->FileOffset = (ULONG) StartingVbo + (ULONG) ByteCount;
|
||
NextBuffer->Length = (ULONG) BytesToRead - (ULONG) ByteCount;
|
||
}
|
||
#endif
|
||
|
||
SafeZeroMemory( (PUCHAR)SystemBuffer + (ULONG)ByteCount,
|
||
BytesToRead - (ULONG)ByteCount );
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
if (ScbIsBeingLogged( Scb )) {
|
||
ULONG Flags = SCE_FLAG_READ;
|
||
|
||
if (PagingIo)
|
||
{
|
||
Flags |= SCE_FLAG_PAGING;
|
||
}
|
||
if (!SynchronousIo)
|
||
{
|
||
Flags |= SCE_FLAG_ASYNC;
|
||
}
|
||
|
||
FsRtlLogSyscacheEvent( Scb, SCE_ZERO_NC, Flags, ByteCount + StartingVbo, BytesToRead - ByteCount, 0 );
|
||
}
|
||
#endif
|
||
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
}
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
#ifdef NTFS_RWC_DEBUG
|
||
if ((Scb->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) &&
|
||
PagingIo &&
|
||
!CompressedIo &&
|
||
(StartingVbo < NtfsRWCHighThreshold) &&
|
||
(StartingVbo + BytesToRead > NtfsRWCLowThreshold)) {
|
||
|
||
PRWC_HISTORY_ENTRY NextBuffer;
|
||
|
||
NextBuffer = NtfsGetHistoryEntry( Scb );
|
||
|
||
NextBuffer->Operation = FaultIntoUncompressed;
|
||
NextBuffer->Information = 0;
|
||
NextBuffer->FileOffset = (ULONG) StartingVbo;
|
||
NextBuffer->Length = (ULONG) BytesToRead;
|
||
}
|
||
#endif
|
||
#endif
|
||
SafeZeroMemory( Add2Ptr( SystemBuffer, ZeroOffset ), ZeroLength );
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
if (ScbIsBeingLogged( Scb )) {
|
||
ULONG Flags = SCE_FLAG_READ;
|
||
|
||
if (PagingIo)
|
||
{
|
||
Flags |= SCE_FLAG_PAGING;
|
||
}
|
||
if (!SynchronousIo)
|
||
{
|
||
Flags |= SCE_FLAG_ASYNC;
|
||
}
|
||
|
||
ASSERT( Scb->NonpagedScb->SegmentObject.ImageSectionObject == NULL );
|
||
FsRtlLogSyscacheEvent( Scb, SCE_ZERO_NC, Flags, ZeroOffset + StartingVbo, ZeroLength, 0 );
|
||
}
|
||
#endif
|
||
|
||
}
|
||
|
||
//
|
||
// 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 COMPRESS_ON_WIRE
|
||
if (!FlagOn(IrpContext->MinorFunction, IRP_MN_COMPRESSED)) {
|
||
#endif
|
||
|
||
//
|
||
// 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->CleanupStructure = Scb;
|
||
}
|
||
|
||
CcInitializeCacheMap( FileObject,
|
||
(PCC_FILE_SIZES)&Header->AllocationSize,
|
||
FALSE,
|
||
&NtfsData.CacheManagerCallbacks,
|
||
Scb );
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->CleanupStructure = 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 there is a compressed section, we have to do cache coherency for
|
||
// that stream, and loop here to do a Cache Manager view at a time.
|
||
//
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
if (Scb->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) {
|
||
|
||
LONGLONG LocalOffset = StartingVbo;
|
||
ULONG LocalLength;
|
||
ULONG LengthLeft = (ULONG) ByteCount;
|
||
|
||
//
|
||
// Create the compressed stream if not there.
|
||
//
|
||
|
||
if (Header->FileObjectC == NULL) {
|
||
NtfsCreateInternalCompressedStream( IrpContext, Scb, FALSE, NULL );
|
||
}
|
||
|
||
if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
|
||
|
||
//
|
||
// Get hold of the user's buffer.
|
||
//
|
||
|
||
SystemBuffer = NtfsMapUserBuffer( Irp );
|
||
}
|
||
|
||
//
|
||
// We must loop to do a view at a time, because that is how much
|
||
// we synchronize at once below.
|
||
//
|
||
|
||
do {
|
||
|
||
ULONG PageCount;
|
||
ULONG ViewOffset;
|
||
|
||
//
|
||
// Calculate length left in view.
|
||
//
|
||
|
||
LocalLength = LengthLeft;
|
||
if (LocalLength > (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)))) {
|
||
LocalLength = (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)));
|
||
}
|
||
|
||
//
|
||
// Trim the read so we don't inadvertently go beyond the end of the
|
||
// view because of the MM read ahead.
|
||
//
|
||
|
||
ViewOffset = ((ULONG) LocalOffset & (VACB_MAPPING_GRANULARITY - 1));
|
||
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(((PVOID)(ULONG_PTR)((ULONG)LocalOffset)), LocalLength);
|
||
|
||
if (LocalLength > (VACB_MAPPING_GRANULARITY - ((PageCount - 1) * PAGE_SIZE) - ViewOffset)) {
|
||
|
||
#ifdef NTFS_RWC_DEBUG
|
||
if ((LocalOffset < NtfsRWCHighThreshold) &&
|
||
(LocalOffset + LocalLength > NtfsRWCLowThreshold)) {
|
||
|
||
PRWC_HISTORY_ENTRY NextBuffer;
|
||
|
||
NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
|
||
|
||
NextBuffer->Operation = TrimCopyRead;
|
||
NextBuffer->Information = PageCount;
|
||
NextBuffer->FileOffset = (ULONG) LocalOffset;
|
||
NextBuffer->Length = (ULONG) LocalLength;
|
||
}
|
||
#endif
|
||
LocalLength = (VACB_MAPPING_GRANULARITY - ((PageCount - 1) * PAGE_SIZE) - ViewOffset);
|
||
}
|
||
|
||
Status = NtfsSynchronizeUncompressedIo( Scb,
|
||
&LocalOffset,
|
||
LocalLength,
|
||
FALSE,
|
||
&CompressionSync );
|
||
|
||
//
|
||
// If we successfully synchronized, then do a piece of the transfer.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
|
||
|
||
//
|
||
// Now try to do the copy.
|
||
//
|
||
|
||
if (!CcCopyRead( FileObject,
|
||
(PLARGE_INTEGER)&LocalOffset,
|
||
LocalLength,
|
||
Wait,
|
||
SystemBuffer,
|
||
&Irp->IoStatus )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Cached Read could not wait\n") );
|
||
|
||
try_return( PostIrp = TRUE );
|
||
}
|
||
|
||
SystemBuffer = Add2Ptr( SystemBuffer, LocalLength );
|
||
|
||
//
|
||
// HANDLE A MDL READ
|
||
//
|
||
|
||
} else {
|
||
|
||
DebugTrace( 0, Dbg, ("MDL read.\n") );
|
||
|
||
ASSERT( Wait );
|
||
|
||
#ifdef NTFS_RWCMP_TRACE
|
||
if (NtfsCompressionTrace && IsSyscache(Header)) {
|
||
DbgPrint("CcMdlRead: FO = %08lx, Len = %08lx\n", (ULONG)LocalOffset, LocalLength );
|
||
}
|
||
#endif
|
||
|
||
CcMdlRead( FileObject,
|
||
(PLARGE_INTEGER)&LocalOffset,
|
||
LocalLength,
|
||
&Irp->MdlAddress,
|
||
&Irp->IoStatus );
|
||
}
|
||
|
||
Status = Irp->IoStatus.Status;
|
||
|
||
LocalOffset += LocalLength;
|
||
LengthLeft -= LocalLength;
|
||
}
|
||
|
||
} while ((LengthLeft != 0) && NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Make sure to return the total of all of the IOs.
|
||
//
|
||
|
||
Irp->IoStatus.Information = (ULONG) ByteCount;
|
||
try_return( Status );
|
||
}
|
||
#endif
|
||
|
||
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 );
|
||
}
|
||
|
||
//
|
||
// HANDLE A MDL READ
|
||
//
|
||
|
||
} else {
|
||
|
||
DebugTrace( 0, Dbg, ("MDL read.\n") );
|
||
|
||
ASSERT( Wait );
|
||
|
||
#ifdef NTFS_RWCMP_TRACE
|
||
if (NtfsCompressionTrace && IsSyscache(Header)) {
|
||
DbgPrint("CcMdlRead: FO = %08lx, Len = %08lx\n", (ULONG)StartingVbo, (ULONG)ByteCount );
|
||
}
|
||
#endif
|
||
|
||
CcMdlRead( FileObject,
|
||
(PLARGE_INTEGER)&StartingVbo,
|
||
(ULONG)ByteCount,
|
||
&Irp->MdlAddress,
|
||
&Irp->IoStatus );
|
||
}
|
||
|
||
Status = Irp->IoStatus.Status;
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
try_return( Status );
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
|
||
//
|
||
// Handle the compressed calls.
|
||
//
|
||
|
||
} else {
|
||
|
||
PCOMPRESSED_DATA_INFO CompressedDataInfo;
|
||
PMDL *NewMdl;
|
||
|
||
ASSERT((StartingVbo & (NTFS_CHUNK_SIZE - 1)) == 0);
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT | SCB_STATE_REALLOCATE_ON_WRITE )) {
|
||
try_return( Status = STATUS_INVALID_READ_MODE );
|
||
}
|
||
|
||
//
|
||
// Get out if COW is not supported.
|
||
//
|
||
|
||
if (!NtfsEnableCompressedIO) {
|
||
|
||
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->CleanupStructure = Scb;
|
||
}
|
||
|
||
NtfsCreateInternalCompressedStream( IrpContext, Scb, FALSE, NULL );
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->CleanupStructure = 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;
|
||
|
||
//
|
||
// 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) {
|
||
|
||
//
|
||
// Make sure to reset this if we pass through the loop again.
|
||
//
|
||
|
||
CompressedDataInfo->NumberOfChunks = 0;
|
||
|
||
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) && (*NewMdl != NULL)) {
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Make sure we are serialized with the FileSizes, and
|
||
// will remove this condition if we abort.
|
||
//
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlLockFsRtlHeader( Header );
|
||
IrpContext->CleanupStructure = Scb;
|
||
}
|
||
|
||
//
|
||
// Create the normal data stream and loop back to try again.
|
||
//
|
||
|
||
NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE, NULL );
|
||
|
||
if (!DoingIoAtEof) {
|
||
FsRtlUnlockFsRtlHeader( Header );
|
||
IrpContext->CleanupStructure = NULL;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
|
||
//
|
||
// If the request was not posted, deal with it.
|
||
//
|
||
|
||
if (Irp) {
|
||
|
||
if (!PostIrp) {
|
||
|
||
DebugTrace( 0, Dbg, ("Completing request with status = %08lx\n",
|
||
Status));
|
||
|
||
DebugTrace( 0, Dbg, (" Information = %08lx\n",
|
||
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.
|
||
// Information field contains the actual bytes read
|
||
//
|
||
|
||
if (!PagingIo) {
|
||
|
||
if (SynchronousIo) {
|
||
|
||
IrpSp->FileObject->CurrentByteOffset.QuadPart = StartingVbo + Irp->IoStatus.Information;
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsCommonRead );
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
//
|
||
// Clean up any Bcb from read/synchronize compressed.
|
||
//
|
||
|
||
if (CompressionSync != NULL) {
|
||
NtfsReleaseCompressionSync( CompressionSync );
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If the Scb has been acquired, release it.
|
||
//
|
||
|
||
if (PagingIoAcquired) {
|
||
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
}
|
||
|
||
if (Irp) {
|
||
|
||
if (ScbAcquired) {
|
||
|
||
NtfsReleaseResource( IrpContext, Scb );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
|
||
NtfsCompleteRequest( IrpContext, Irp, Status );
|
||
|
||
} else if (!OplockPostIrp) {
|
||
|
||
Status = NtfsPostRequest( IrpContext, Irp );
|
||
}
|
||
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsNonCachedResidentRead (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PSCB Scb,
|
||
IN ULONG StartingVbo,
|
||
IN ULONG ByteCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Read a resident file record directly for the non cached path. This is
|
||
done separately to scope the attribute enumeration context so its not present
|
||
in most of the read path and for simplicity
|
||
|
||
Arguments:
|
||
|
||
IrpContext - If present this an IrpContext put on the caller's stack
|
||
to avoid having to allocate it from pool.
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Scb - scb to read from
|
||
|
||
StartingVbo - start offset in the file - since its resident can be stored in a ulong
|
||
|
||
ByteCount - bytes to read - since its resident can be stored in a ulong
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the IRP
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID SystemBuffer;
|
||
PUCHAR AttrValue;
|
||
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
|
||
|
||
//
|
||
// 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 );
|
||
|
||
try {
|
||
|
||
NtfsLookupAttributeForScb( IrpContext,
|
||
Scb,
|
||
NULL,
|
||
&AttrContext );
|
||
|
||
AttrValue = NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
|
||
|
||
RtlCopyMemory( SystemBuffer,
|
||
Add2Ptr( AttrValue, StartingVbo ),
|
||
ByteCount );
|
||
|
||
Irp->IoStatus.Information = ByteCount;
|
||
|
||
} finally {
|
||
NtfsCleanupAttributeContext( IrpContext, &AttrContext );
|
||
}
|
||
|
||
return;
|
||
}
|