/*
 * COPYRIGHT:        GNU GENERAL PUBLIC LICENSE VERSION 2
 * PROJECT:          ReiserFs file system driver for Windows NT/2000/XP/Vista.
 * FILE:             read.c
 * PURPOSE:          
 * PROGRAMMER:       Mark Piper, Matt Wu, Bo Brantén.
 * HOMEPAGE:         
 * UPDATE HISTORY: 
 */

/* INCLUDES *****************************************************************/

#include "rfsd.h"

/* GLOBALS ***************************************************************/

extern PRFSD_GLOBAL RfsdGlobal;

/* DEFINITIONS *************************************************************/

NTSTATUS
RfsdReadComplete (IN PRFSD_IRP_CONTEXT IrpContext);

NTSTATUS
RfsdReadFile (IN PRFSD_IRP_CONTEXT IrpContext);

NTSTATUS
RfsdReadVolume (IN PRFSD_IRP_CONTEXT IrpContext);

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RfsdCompleteIrpContext)
#pragma alloc_text(PAGE, RfsdCopyRead)
#pragma alloc_text(PAGE, RfsdRead)
#pragma alloc_text(PAGE, RfsdReadVolume)
#pragma alloc_text(PAGE, RfsdReadInode)
#pragma alloc_text(PAGE, RfsdReadFile)
#pragma alloc_text(PAGE, RfsdReadComplete)
#endif

/* FUNCTIONS *************************************************************/

/** Proxy to CcCopyRead, which simply asserts the success of the IoStatus. */
BOOLEAN 
RfsdCopyRead(
    IN PFILE_OBJECT  FileObject,
    IN PLARGE_INTEGER  FileOffset,
    IN ULONG  Length,
    IN BOOLEAN  Wait,
    OUT PVOID  Buffer,
    OUT PIO_STATUS_BLOCK  IoStatus
    )
{
    BOOLEAN bRet;

    PAGED_CODE();

    bRet=  CcCopyRead(FileObject,
                FileOffset,
                Length,
                Wait,
                Buffer,
                IoStatus    );

    if (bRet) {
        ASSERT(NT_SUCCESS(IoStatus->Status));
    }

    return bRet;
/*
    PVOID Bcb = NULL;
    PVOID Buf = NULL;

    if (CcMapData(  FileObject,
                    FileOffset,
                    Length,
                    Wait,
                    &Bcb,
                    &Buf    )) {
        RtlCopyMemory(Buffer,  Buf, Length);
        IoStatus->Status = STATUS_SUCCESS;
        IoStatus->Information = Length;
        CcUnpinData(Bcb);
        return TRUE;

    } else {
        // IoStatus->Status = STATUS_
        return FALSE;
    }
*/
}

__drv_mustHoldCriticalRegion
NTSTATUS
RfsdReadVolume (IN PRFSD_IRP_CONTEXT IrpContext)
{
    NTSTATUS            Status = STATUS_UNSUCCESSFUL;

    PRFSD_VCB           Vcb = 0;
    PRFSD_CCB           Ccb;
    PRFSD_FCBVCB        FcbOrVcb;
    PFILE_OBJECT        FileObject;

    PDEVICE_OBJECT      DeviceObject;

    PIRP                Irp;
    PIO_STACK_LOCATION  IoStackLocation;

    ULONG               Length;
    LARGE_INTEGER       ByteOffset;

    BOOLEAN             PagingIo;
    BOOLEAN             Nocache;
    BOOLEAN             SynchronousIo;
    BOOLEAN             MainResourceAcquired = FALSE;
    BOOLEAN             PagingIoResourceAcquired = FALSE;

    PUCHAR              Buffer = NULL;
    PRFSD_BDL           rfsd_bdl = NULL;

    PAGED_CODE();

    _SEH2_TRY {

        ASSERT(IrpContext);
        
        ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
            (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
        
        DeviceObject = IrpContext->DeviceObject;
    
        Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;

        ASSERT(Vcb != NULL);
        
        ASSERT((Vcb->Identifier.Type == RFSDVCB) &&
            (Vcb->Identifier.Size == sizeof(RFSD_VCB)));
        
        FileObject = IrpContext->FileObject;

        FcbOrVcb = (PRFSD_FCBVCB) FileObject->FsContext;
        
        ASSERT(FcbOrVcb);
        
        if (!(FcbOrVcb->Identifier.Type == RFSDVCB && (PVOID)FcbOrVcb == (PVOID)Vcb)) {

            Status = STATUS_INVALID_DEVICE_REQUEST;
            _SEH2_LEAVE;
        }

        Ccb = (PRFSD_CCB) FileObject->FsContext2;

        Irp = IrpContext->Irp;
            
        Irp->IoStatus.Information = 0;

        IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
            
        Length = IoStackLocation->Parameters.Read.Length;
        ByteOffset = IoStackLocation->Parameters.Read.ByteOffset;
            
        PagingIo = (Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE);
        Nocache = (Irp->Flags & IRP_NOCACHE ? TRUE : FALSE);
        SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO ? TRUE : FALSE);
            
        if (Length == 0) {

            Irp->IoStatus.Information = 0;
            Status = STATUS_SUCCESS;
            _SEH2_LEAVE;
        }

        if (Ccb != NULL) {

            if(!IsFlagOn(Ccb->Flags, CCB_ALLOW_EXTENDED_DASD_IO)) {
                if (ByteOffset.QuadPart + Length > Vcb->Header.FileSize.QuadPart) {
                    Length = (ULONG)(Vcb->Header.FileSize.QuadPart - ByteOffset.QuadPart);
                }
            }

            {
                RFSD_BDL BlockArray;

                if ((ByteOffset.LowPart & (SECTOR_SIZE - 1)) ||
                   (Length & (SECTOR_SIZE - 1)) ) {
                    Status = STATUS_INVALID_PARAMETER;
                    _SEH2_LEAVE;
                }

                Status = RfsdLockUserBuffer(
                    IrpContext->Irp,
                    Length,
                    IoReadAccess );
                
                if (!NT_SUCCESS(Status)) {
                    _SEH2_LEAVE;
                }

                BlockArray.Irp = NULL;
                BlockArray.Lba = ByteOffset.QuadPart;;
                BlockArray.Offset = 0;
                BlockArray.Length = Length;

                Status = RfsdReadWriteBlocks(IrpContext,
                                    Vcb,
                                    &BlockArray,
                                    Length,
                                    1,
                                    FALSE   );
                Irp = IrpContext->Irp;

                _SEH2_LEAVE;
            }
        }
            
        if (Nocache &&
            ( (ByteOffset.LowPart & (SECTOR_SIZE - 1)) ||
              (Length & (SECTOR_SIZE - 1)) )) {
            DbgBreak();

            Status = STATUS_INVALID_PARAMETER;
            _SEH2_LEAVE;
        }

        if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC)) {
            ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC);
            Status = STATUS_PENDING;
            _SEH2_LEAVE;
        }
        
        if (!PagingIo) {
            if (!ExAcquireResourceSharedLite(
                &Vcb->MainResource,
                IrpContext->IsSynchronous )) {
                Status = STATUS_PENDING;
                _SEH2_LEAVE;
            }
            
            MainResourceAcquired = TRUE;

        } else {

            if (!ExAcquireResourceSharedLite(
                &Vcb->PagingIoResource,
                IrpContext->IsSynchronous ))
            {
                Status = STATUS_PENDING;
                _SEH2_LEAVE;
            }
            
            PagingIoResourceAcquired = TRUE;
        }
        
    
        if (ByteOffset.QuadPart >=
            Vcb->PartitionInformation.PartitionLength.QuadPart  ) {
            Irp->IoStatus.Information = 0;
            Status = STATUS_END_OF_FILE;
            _SEH2_LEAVE;
        }

        if (!Nocache) {

            if ((ByteOffset.QuadPart + Length) >
                    Vcb->PartitionInformation.PartitionLength.QuadPart ){
                Length = (ULONG) (
                    Vcb->PartitionInformation.PartitionLength.QuadPart -
                    ByteOffset.QuadPart);
                Length &= ~((ULONG)SECTOR_SIZE - 1);
            }

            if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {

                CcMdlRead(
                    Vcb->StreamObj,
                    &ByteOffset,
                    Length,
                    &Irp->MdlAddress,
                    &Irp->IoStatus );
                
                Status = Irp->IoStatus.Status;

            } else {

                Buffer = RfsdGetUserBuffer(Irp);
                    
                if (Buffer == NULL) {
                    DbgBreak();
                    Status = STATUS_INVALID_USER_BUFFER;
                    _SEH2_LEAVE;
                }

                if (!CcCopyRead(
                    Vcb->StreamObj,
                    (PLARGE_INTEGER)&ByteOffset,
                    Length,
                    IrpContext->IsSynchronous,
                    Buffer,
                    &Irp->IoStatus )) {
                    Status = STATUS_PENDING;
                    _SEH2_LEAVE;
                }
                
                Status = Irp->IoStatus.Status;
            }

        } else {

            if ((ByteOffset.QuadPart + Length) >
                    Vcb->PartitionInformation.PartitionLength.QuadPart ) {
                Length = (ULONG) (
                    Vcb->PartitionInformation.PartitionLength.QuadPart -
                    ByteOffset.QuadPart);

                Length &= ~((ULONG)SECTOR_SIZE - 1);
            }

            Status = RfsdLockUserBuffer(
                IrpContext->Irp,
                Length,
                IoWriteAccess );
                
            if (!NT_SUCCESS(Status)) {
                _SEH2_LEAVE;
            }

#if DBG
            Buffer = RfsdGetUserBuffer(Irp);
#endif
            rfsd_bdl = ExAllocatePoolWithTag(PagedPool, sizeof(RFSD_BDL), RFSD_POOL_TAG);

            if (!rfsd_bdl)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                _SEH2_LEAVE;
            }

            rfsd_bdl->Irp = NULL;
            rfsd_bdl->Lba = ByteOffset.QuadPart;
            rfsd_bdl->Length = Length;
            rfsd_bdl->Offset = 0;

            Status = RfsdReadWriteBlocks(IrpContext,
                                Vcb,
                                rfsd_bdl,
                                Length,
                                1,
                                FALSE   );

            Irp = IrpContext->Irp;

            if (!Irp)
                _SEH2_LEAVE;
        }

    } _SEH2_FINALLY {

        if (PagingIoResourceAcquired) {
            ExReleaseResourceForThreadLite(
                &Vcb->PagingIoResource,
                ExGetCurrentResourceThread());
        }
        
        if (MainResourceAcquired) {
            ExReleaseResourceForThreadLite(
                &Vcb->MainResource,
                ExGetCurrentResourceThread());
        }

        if (rfsd_bdl)
            ExFreePool(rfsd_bdl);

        if (!IrpContext->ExceptionInProgress) {

            if (IrpContext->Irp) {

                if (Status == STATUS_PENDING &&
                    !IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_REQUEUED)) {

                    Status = RfsdLockUserBuffer(
                        IrpContext->Irp,
                        Length,
                        IoWriteAccess );
                    
                    if (NT_SUCCESS(Status)) {
                        Status = RfsdQueueRequest(IrpContext);
                    } else {
                        RfsdCompleteIrpContext(IrpContext, Status);
                    }

                } else {

                    if (NT_SUCCESS(Status)) {

                        if (!PagingIo) {

                            if (SynchronousIo) {

                                IrpContext->FileObject->CurrentByteOffset.QuadPart =
                                    ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
                            }

                            IrpContext->FileObject->Flags |= FO_FILE_FAST_IO_READ;
                        }
                    }

                    RfsdCompleteIrpContext(IrpContext, Status);;
                }

            } else {
                RfsdFreeIrpContext(IrpContext);
            }
        }
    } _SEH2_END;

    return Status;
}

// [mark] read some goop [from the file pt'd to by inode -- from whatever blocks buildbdl makes] into the buffer
NTSTATUS
RfsdReadInode (
            IN PRFSD_IRP_CONTEXT    IrpContext,				// [may be null]
            IN PRFSD_VCB            Vcb,
            IN PRFSD_KEY_IN_MEMORY  Key,					// Key that identifies the data on disk to be read.  This is simply forwarded through to BuildBDL. (NOTE: IN THIS CASE, THE OFFSET AND TYPE FIELDS MATTER)
            IN PRFSD_INODE          Inode,					// a filled Inode / stat data structure
            IN ULONGLONG            Offset,					// User's requested offset to read within the file (relative to the file)
            IN OUT PVOID            Buffer,					// buffer to read out to
            IN ULONG                Size,					// size of destination buffer
            OUT PULONG              dwRet )					// some kind of size [probably bytes read?]
{
    PRFSD_BDL   Bdl     = NULL;
    ULONG       blocks, i, j;
    NTSTATUS    Status = STATUS_UNSUCCESSFUL;
    IO_STATUS_BLOCK IoStatus;

    ULONGLONG   FileSize;
    ULONGLONG   AllocSize;

    PAGED_CODE();

    if (dwRet) {
        *dwRet = 0;
    }

    //
    // Calculate the inode size
    //

    FileSize = (ULONGLONG) Inode->i_size;

	//KdPrint(("Rfsd: RfsdReadInode: file size = %I64u, offset = %I64u, length = %u\n", FileSize, Offset, Size));

	// TODO: temporary hack to get correct alloc size for dir tails... but i doubt 8 works in all cases :-)  [what i should really be using is the size of the direct item in the block header!]
    // AllocSize = CEILING_ALIGNED(FileSize, (ULONGLONG)Vcb->BlockSize);
	// AllocSize = CEILING_ALIGNED(FileSize, (ULONGLONG) 8);
	AllocSize = CEILING_ALIGNED(FileSize, (ULONGLONG) 1);		// temp hack to ensure that i'll read out EXACTLY the # of bytes he requested

    //
    // Check inputed parameters: Offset / Size
    //

    if (Offset >= AllocSize) {

        RfsdPrint((DBG_ERROR, "RfsdReadInode: beyond the file range.\n"));
        return STATUS_SUCCESS;
    }

    if (Offset + Size > AllocSize) {

        Size = (ULONG)(AllocSize - Offset);
    }


//-----------------------------	  
	
    //
    // Build the scatterred block ranges to be read
    //

    Status = RfsdBuildBDL2(
		Vcb, Key, Inode, 
		&(blocks), &(Bdl)
		);

    if (!NT_SUCCESS(Status)) {
        goto errorout;
    }

    if (blocks <= 0) {
        Status = STATUS_SUCCESS;
        goto errorout;
    }

	
	{
	  ULONGLONG bufferPos = 0;

      for(i = 0, j = 0; i < blocks; i++) {
		  if ( // The block is needed for the user's requested contents
			   // (The user's requested offset lies within the block, or the block's start is within the user's requested range)
			  ( (Offset >= Bdl[i].Offset) && (Offset < (Bdl[i].Offset + Bdl[i].Length)) ) ||		// The user's offset is within the block's range
			  ( (Bdl[i].Offset >= Offset) && (Bdl[i].Offset < (Offset + Size)) )					// The block's offset is within the user's range
		      )
		  {
			  ULONGLONG	offsetFromDisk	= Bdl[i].Lba;			  
			  ULONGLONG	lengthToRead	= min(Size - bufferPos, Bdl[i].Length);
			  j++;

			  //KdPrint(("Rfsd: blocks = %u, i = %u, j = %u\n", blocks, i, j));
			  //KdPrint(("Rfsd: Bdl[%u].Lba = %I64u, Bdl[%u].Offset = %I64u, Bdl[%u].Length = %u\n", i, Bdl[i].Lba, i, Bdl[i].Offset, i, Bdl[i].Length));
			  //KdPrint(("Rfsd: offsetFromDisk = %I64u, lengthToRead = %I64u\n", offsetFromDisk, lengthToRead));
			  //KdPrint(("Rfsd: Buffer = %p, bufferPos = %I64u\n", Buffer, bufferPos));

			  IoStatus.Information = 0;				

			  RfsdCopyRead(
					  Vcb->StreamObj, 
					  (PLARGE_INTEGER) (&offsetFromDisk),	// offset (relative to partition)
					  (ULONG) lengthToRead,					// length to read
					  PIN_WAIT,								// 
					  (PVOID)((PUCHAR)Buffer + bufferPos),	// buffer to read into
					  &IoStatus   );

			  Status = IoStatus.Status;
			  bufferPos += IoStatus.Information;
			  //KdPrint(("Rfsd: IoStatus.Status = %#x, IoStatus.Information = %u\n", IoStatus.Status, IoStatus.Information));
		  }
      }

	}

errorout:

    if (Bdl)				ExFreePool(Bdl);

    if (NT_SUCCESS(Status)) {

        if (dwRet) *dwRet = Size;
    }

    return Status;
}

__drv_mustHoldCriticalRegion
NTSTATUS
RfsdReadFile(IN PRFSD_IRP_CONTEXT IrpContext)
{
    NTSTATUS            Status = STATUS_UNSUCCESSFUL;

    PRFSD_VCB           Vcb;
    PRFSD_FCB           Fcb = 0;
    PRFSD_CCB           Ccb;
    PFILE_OBJECT        FileObject;
    PFILE_OBJECT        CacheObject;

    PDEVICE_OBJECT      DeviceObject;

    PIRP                Irp;
    PIO_STACK_LOCATION  IoStackLocation;

    ULONG               Length;
    ULONG               ReturnedLength;
    LARGE_INTEGER       ByteOffset;

    BOOLEAN             PagingIo;
    BOOLEAN             Nocache;
    BOOLEAN             SynchronousIo;
    BOOLEAN             MainResourceAcquired = FALSE;
    BOOLEAN             PagingIoResourceAcquired = FALSE;

    PUCHAR              Buffer;

    PAGED_CODE();

    _SEH2_TRY {

        ASSERT(IrpContext);
        
        ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
            (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
        
        DeviceObject = IrpContext->DeviceObject;
    
        Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;
        
        ASSERT(Vcb != NULL);
        
        ASSERT((Vcb->Identifier.Type == RFSDVCB) &&
            (Vcb->Identifier.Size == sizeof(RFSD_VCB)));
        
        FileObject = IrpContext->FileObject;
        
        Fcb = (PRFSD_FCB) FileObject->FsContext;
        
        ASSERT(Fcb);
    
        ASSERT((Fcb->Identifier.Type == RFSDFCB) &&
            (Fcb->Identifier.Size == sizeof(RFSD_FCB)));		

        Ccb = (PRFSD_CCB) FileObject->FsContext2;

        Irp = IrpContext->Irp;
        
        IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
        
        Length = IoStackLocation->Parameters.Read.Length;
        ByteOffset = IoStackLocation->Parameters.Read.ByteOffset;
        
#ifdef _MSC_VER
		KdPrint(("$$$ " __FUNCTION__ " on key: %x,%xh to read %i bytes at the offset %xh in the file\n", 
			Fcb->RfsdMcb->Key.k_dir_id, Fcb->RfsdMcb->Key.k_objectid,
			Length, ByteOffset.QuadPart));
#endif

        PagingIo = (Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE);
        Nocache = (Irp->Flags & IRP_NOCACHE ? TRUE : FALSE);
        SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO ? TRUE : FALSE);

/*
        if (IsFlagOn(Fcb->Flags, FCB_FILE_DELETED)) {
            Status = STATUS_FILE_DELETED;
            _SEH2_LEAVE;
        }

        if (IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING)) {
            Status = STATUS_DELETE_PENDING;
            _SEH2_LEAVE;
        }
*/

        if (Length == 0) {
            Irp->IoStatus.Information = 0;
            Status = STATUS_SUCCESS;
            _SEH2_LEAVE;
        }

        if (Nocache &&
           (ByteOffset.LowPart & (SECTOR_SIZE - 1) ||
            Length & (SECTOR_SIZE - 1))) {
            Status = STATUS_INVALID_PARAMETER;
            DbgBreak();
            _SEH2_LEAVE;
        }

        if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC)) {
            ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC);
            Status = STATUS_PENDING;
            DbgBreak();
            _SEH2_LEAVE;
        }
        
        if (!PagingIo) {
            if (!ExAcquireResourceSharedLite(
                &Fcb->MainResource,
                IrpContext->IsSynchronous )) {
                Status = STATUS_PENDING;
                _SEH2_LEAVE;
            }
            
            MainResourceAcquired = TRUE;

            if (!FsRtlCheckLockForReadAccess(
                &Fcb->FileLockAnchor,
                Irp         )) {
                Status = STATUS_FILE_LOCK_CONFLICT;
                _SEH2_LEAVE;
            }
        } else {
            if (!ExAcquireResourceSharedLite(
                &Fcb->PagingIoResource,
                IrpContext->IsSynchronous )) {
                Status = STATUS_PENDING;
                _SEH2_LEAVE;
            }
            
            PagingIoResourceAcquired = TRUE;
        }
        
		if (!Nocache) {
			// Attempt cached access...

            if ((ByteOffset.QuadPart + (LONGLONG)Length) >
                Fcb->Header.FileSize.QuadPart ) {
                if (ByteOffset.QuadPart >= (Fcb->Header.FileSize.QuadPart)) {
                    Irp->IoStatus.Information = 0;
                    Status = STATUS_END_OF_FILE;
                    _SEH2_LEAVE;
                }

                Length =
                    (ULONG)(Fcb->Header.FileSize.QuadPart - ByteOffset.QuadPart);

            }

            ReturnedLength = Length;

            if (IsDirectory(Fcb)) {
                _SEH2_LEAVE;
            }

            {
                if (FileObject->PrivateCacheMap == NULL) {
                    CcInitializeCacheMap(
                        FileObject,
                        (PCC_FILE_SIZES)(&Fcb->Header.AllocationSize),
                        FALSE,
                        &RfsdGlobal->CacheManagerCallbacks,
                        Fcb );
                }

                CacheObject = FileObject;
            }

            if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
                CcMdlRead(
                    CacheObject,
                    (&ByteOffset),
                    Length,
                    &Irp->MdlAddress,
                    &Irp->IoStatus );
                
                Status = Irp->IoStatus.Status;

            } else {
                Buffer = RfsdGetUserBuffer(Irp);
                
                if (Buffer == NULL) {
                    Status = STATUS_INVALID_USER_BUFFER;
                    DbgBreak();
                    _SEH2_LEAVE;
                }
                
                if (!CcCopyRead(
                    CacheObject,						// the file object (representing the open operation performed by the thread)
                    (PLARGE_INTEGER)&ByteOffset,		// starting offset IN THE FILE, from where the read should be performed
                    Length,								// number of bytes requested in the read operation
                    IrpContext->IsSynchronous,
                    Buffer,								// < buffer to read the contents to
                    &Irp->IoStatus )) {
                    Status = STATUS_PENDING;
                    DbgBreak();
                    _SEH2_LEAVE;
                }
                
                Status = Irp->IoStatus.Status;
            }

        } else {
			// Attempt access without the cache...

            if ((ByteOffset.QuadPart + (LONGLONG)Length) > Fcb->Header.AllocationSize.QuadPart) {

                if (ByteOffset.QuadPart >= Fcb->Header.AllocationSize.QuadPart) {
                    Irp->IoStatus.Information = 0;
                    Status = STATUS_END_OF_FILE;
                    DbgBreak();
                    _SEH2_LEAVE;
                }

                Length =
                     (ULONG)(Fcb->Header.AllocationSize.QuadPart- ByteOffset.QuadPart);
            }

            ReturnedLength = Length;

            /* lock the user buffer into MDL and make them paged-in */
            Status = RfsdLockUserBuffer(
                        IrpContext->Irp,
                        Length,
                        IoWriteAccess );
                
            if (NT_SUCCESS(Status)) {

                /* Zero the total buffer */
                PVOID SystemVA = RfsdGetUserBuffer(IrpContext->Irp);
                if (SystemVA) {

                    RtlZeroMemory(SystemVA, Length);

                    RfsdPrint((DBG_INFO, "RfsdReadFile: Zero read buffer: Offset=%I64xh Size=%xh ... \n",
                               ByteOffset.QuadPart, Length));
                }

            } else {
                _SEH2_LEAVE;
            }

            Irp->IoStatus.Status = STATUS_SUCCESS;
            Irp->IoStatus.Information = Length;

            
            Status = RfsdReadInode(
                        IrpContext,
                        Vcb,
                        &(Fcb->RfsdMcb->Key),
                        Fcb->Inode,
                        ByteOffset.QuadPart,
                        RfsdGetUserBuffer(IrpContext->Irp),		//  NOTE: Ext2fsd just passes NULL for the buffer, and relies on the initial cache call to retrieve tha data.  We'll instead be explicitly putting it into the user's buffer, via a much different mechanism.
                        Length,
                        &ReturnedLength);

            Irp = IrpContext->Irp;

        }

    } _SEH2_FINALLY {

        if (PagingIoResourceAcquired) {
            ExReleaseResourceForThreadLite(
                &Fcb->PagingIoResource,
                ExGetCurrentResourceThread());
        }
        
        if (MainResourceAcquired) {
            ExReleaseResourceForThreadLite(
                &Fcb->MainResource,
                ExGetCurrentResourceThread());
        }
        
        if (!IrpContext->ExceptionInProgress) {
            if (IrpContext->Irp) {
                if (Status == STATUS_PENDING) {

                    Status = RfsdLockUserBuffer(
                        IrpContext->Irp,
                        Length,
                        IoWriteAccess );
                    
                    if (NT_SUCCESS(Status)) {
                        Status = RfsdQueueRequest(IrpContext);
                    } else {
                        RfsdCompleteIrpContext(IrpContext, Status);
                    }
                } else {
                    if (NT_SUCCESS(Status)) {
                        if (!PagingIo) {
                            if (SynchronousIo) {
                                IrpContext->FileObject->CurrentByteOffset.QuadPart =
                                    ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
                            }

                            IrpContext->FileObject->Flags |= FO_FILE_FAST_IO_READ;
                        }
                    }

                    RfsdCompleteIrpContext(IrpContext, Status);
                }
            } else {
                RfsdFreeIrpContext(IrpContext);
            }
        }
    } _SEH2_END;
    
    return Status;
}

NTSTATUS
RfsdReadComplete (IN PRFSD_IRP_CONTEXT IrpContext)
{
    NTSTATUS        Status = STATUS_UNSUCCESSFUL;
    PFILE_OBJECT    FileObject;
    PIRP            Irp;

    PAGED_CODE();

    _SEH2_TRY {

        ASSERT(IrpContext);
        
        ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
            (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
        
        FileObject = IrpContext->FileObject;
        
        Irp = IrpContext->Irp;
        
        CcMdlReadComplete(FileObject, Irp->MdlAddress);
        
        Irp->MdlAddress = NULL;
        
        Status = STATUS_SUCCESS;

    } _SEH2_FINALLY {

        if (!IrpContext->ExceptionInProgress) {
            RfsdCompleteIrpContext(IrpContext, Status);
        }
    } _SEH2_END;
    
    return Status;
}

__drv_mustHoldCriticalRegion
NTSTATUS
RfsdRead (IN PRFSD_IRP_CONTEXT IrpContext)
{
    NTSTATUS            Status;
    PRFSD_VCB           Vcb;
    PRFSD_FCBVCB        FcbOrVcb;
    PDEVICE_OBJECT      DeviceObject;
    PFILE_OBJECT        FileObject;
    BOOLEAN             bCompleteRequest;

    PAGED_CODE();

    ASSERT(IrpContext);
    
    ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
        (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));

    _SEH2_TRY {

        if (FlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE)) {
			// Caller wants to tell the Cache Manager that a previously allocated MDL can be freed.
            Status =  RfsdReadComplete(IrpContext);
            bCompleteRequest = FALSE;

        } else {

            DeviceObject = IrpContext->DeviceObject;

            if (DeviceObject == RfsdGlobal->DeviceObject) {
                Status = STATUS_INVALID_DEVICE_REQUEST;
                bCompleteRequest = TRUE;
                _SEH2_LEAVE;
            }

            Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;

            if (Vcb->Identifier.Type != RFSDVCB ||
                Vcb->Identifier.Size != sizeof(RFSD_VCB) ) {
                Status = STATUS_INVALID_DEVICE_REQUEST;
                bCompleteRequest = TRUE;

                _SEH2_LEAVE;
            }

            if (IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) {

                Status = STATUS_TOO_LATE;
                bCompleteRequest = TRUE;
                _SEH2_LEAVE;
            }

            FileObject = IrpContext->FileObject;
            
            FcbOrVcb = (PRFSD_FCBVCB) FileObject->FsContext;

            if (FcbOrVcb->Identifier.Type == RFSDVCB) {
                Status = RfsdReadVolume(IrpContext);
                bCompleteRequest = FALSE;
            } else if (FcbOrVcb->Identifier.Type == RFSDFCB) {
                Status = RfsdReadFile(IrpContext);
                bCompleteRequest = FALSE;
            } else {
                RfsdPrint((DBG_ERROR, "RfsdRead: INVALID PARAMETER ... \n"));
                DbgBreak();

                Status = STATUS_INVALID_PARAMETER;
                bCompleteRequest = TRUE;
            }
        }

    } _SEH2_FINALLY {
        if (bCompleteRequest) {
            RfsdCompleteIrpContext(IrpContext, Status);
        }
    } _SEH2_END;
    
    return Status;
}
