www.pudn.com > filesys.rar > read.c
/*************************************************************************
*
* File: read.c
*
* Module: Sample File System Driver (Kernel mode execution only)
*
* Description:
* Contains code to handle the "Read" dispatch entry point.
*
* Author: R. Nagar
*
* (c) 1996-97 Rajeev Nagar, All Rights Reserved
*
*************************************************************************/
#include "sfsd.h"
// define the file specific bug-check id
#define SFSD_BUG_CHECK_ID SFSD_FILE_READ
/*************************************************************************
*
* Function: SFsdRead()
*
* Description:
* The I/O Manager will invoke this routine to handle a read
* request
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
* to be deferred to a worker thread context)
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdRead(
PDEVICE_OBJECT DeviceObject, // the logical volume device object
PIRP Irp) // I/O Request Packet
{
NTSTATUS RC = STATUS_SUCCESS;
PtrSFsdIrpContext PtrIrpContext = NULL;
BOOLEAN AreWeTopLevel = FALSE;
FsRtlEnterFileSystem();
ASSERT(DeviceObject);
ASSERT(Irp);
// set the top level context
AreWeTopLevel = SFsdIsIrpTopLevel(Irp);
try {
// get an IRP context structure and issue the request
PtrIrpContext = SFsdAllocateIrpContext(Irp, DeviceObject);
ASSERT(PtrIrpContext);
RC = SFsdCommonRead(PtrIrpContext, Irp);
} except (SFsdExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
RC = SFsdExceptionHandler(PtrIrpContext, Irp);
SFsdLogEvent(SFSD_ERROR_INTERNAL_ERROR, RC);
}
if (AreWeTopLevel) {
IoSetTopLevelIrp(NULL);
}
FsRtlExitFileSystem();
return(RC);
}
/*************************************************************************
*
* Function: SFsdCommonRead()
*
* Description:
* The actual work is performed here. This routine may be invoked in one
* of the two possible contexts:
* (a) in the context of a system worker thread
* (b) in the context of the original caller
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdCommonRead(
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp)
{
NTSTATUS RC = STATUS_SUCCESS;
PIO_STACK_LOCATION PtrIoStackLocation = NULL;
LARGE_INTEGER ByteOffset;
uint32 ReadLength = 0, TruncatedReadLength = 0;
uint32 NumberBytesRead = 0;
PFILE_OBJECT PtrFileObject = NULL;
PtrSFsdFCB PtrFCB = NULL;
PtrSFsdCCB PtrCCB = NULL;
PtrSFsdVCB PtrVCB = NULL;
PtrSFsdNTRequiredFCB PtrReqdFCB = NULL;
PERESOURCE PtrResourceAcquired = NULL;
IO_STATUS_BLOCK LocalIoStatus;
void *PtrSystemBuffer = NULL;
uint32 KeyValue = 0;
BOOLEAN CompleteIrp = TRUE;
BOOLEAN PostRequest = FALSE;
BOOLEAN CanWait = FALSE;
BOOLEAN PagingIo = FALSE;
BOOLEAN NonBufferedIo = FALSE;
BOOLEAN SynchronousIo = FALSE;
try {
// First, get a pointer to the current I/O stack location
PtrIoStackLocation = IoGetCurrentIrpStackLocation(PtrIrp);
ASSERT(PtrIoStackLocation);
// If this happens to be a MDL read complete request, then
// there is not much processing that the FSD has to do.
if (PtrIoStackLocation->MinorFunction & IRP_MN_COMPLETE) {
// Caller wants to tell the Cache Manager that a previously
// allocated MDL can be freed.
SFsdMdlComplete(PtrIrpContext, PtrIrp, PtrIoStackLocation, TRUE);
// The IRP has been completed.
CompleteIrp = FALSE;
try_return(RC = STATUS_SUCCESS);
}
// If this is a request at IRQL DISPATCH_LEVEL, then post
// the request (your FSD may choose to process it synchronously
// if you implement the support correctly; obviously you will be
// quite constrained in what you can do at such IRQL).
if (PtrIoStackLocation->MinorFunction & IRP_MN_DPC) {
CompleteIrp = FALSE;
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
PtrFileObject = PtrIoStackLocation->FileObject;
ASSERT(PtrFileObject);
// Get the FCB and CCB pointers
PtrCCB = (PtrSFsdCCB)(PtrFileObject->FsContext2);
ASSERT(PtrCCB);
PtrFCB = PtrCCB->PtrFCB;
ASSERT(PtrFCB);
// Get some of the parameters supplied to us
ByteOffset = PtrIoStackLocation->Parameters.Read.ByteOffset;
ReadLength = PtrIoStackLocation->Parameters.Read.Length;
CanWait = ((PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);
PagingIo = ((PtrIrp->Flags & IRP_PAGING_IO) ? TRUE : FALSE);
NonBufferedIo = ((PtrIrp->Flags & IRP_NOCACHE) ? TRUE : FALSE);
SynchronousIo = ((PtrFileObject->Flags & FO_SYNCHRONOUS_IO) ? TRUE : FALSE);
if (ReadLength == 0) {
// a 0 byte read can be immediately succeeded
try_return(RC);
}
// NOTE: if your FSD does not support file sizes > 2 GB, you
// could validate the start offset here and return end-of-file
// if the offset begins beyond the maximum supported length.
// Is this a read of the volume itself ?
if (PtrFCB->NodeIdentifier.NodeType == SFSD_NODE_TYPE_VCB) {
// Yup, we need to send this on to the disk driver after
// validation of the offset and length.
PtrVCB = (PtrSFsdVCB)(PtrFCB);
// Acquire the volume resource shared ...
if (!ExAcquireResourceSharedLite(&(PtrVCB->VCBResource), CanWait)) {
// post the request to be processed in the context of a worker thread
CompleteIrp = FALSE;
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
PtrResourceAcquired = &(PtrVCB->VCBResource);
// Insert code to validate the caller supplied offset here
// Lock the callers buffer
if (!NT_SUCCESS(RC = SFsdLockCallersBuffer(PtrIrp, TRUE, ReadLength))) {
try_return(RC);
}
// Forward the request to the lower level driver
// For synchronous I/O wait here, else return STATUS_PENDING
// For asynchronous I/O support, read the discussion in Chapter 10
try_return(RC);
}
// If the read request is directed to a page file (if your FSD
// supports paging files), send the request directly to the disk
// driver. For requests directed to a page file, you have to trust
// that the offsets will be set correctly by the VMM. You should not
// attempt to acquire any FSD resources either.
if (PtrFCB->FCBFlags & SFSD_FCB_PAGE_FILE) {
IoMarkIrpPending(PtrIrp);
// You will need to set a completion routine before invoking
// a lower level driver
// forward request directly to disk driver
// SFsdPageFileIo(PtrIrpContext, PtrIrp);
CompleteIrp = FALSE;
try_return(RC = STATUS_PENDING);
}
// If this read is directed to a directory, it is not allowed
// by the sample FSD. Note that you may choose to create a stream
// file for FSD (internal) directory read/write operations in which
// case you should modify the check below to allow reading (directly
// from disk) directories as long as the read originated from within your FSD
if (PtrFCB->FCBFlags & SFSD_FCB_DIRECTORY) {
RC = STATUS_INVALID_DEVICE_REQUEST;
try_return(RC);
}
PtrReqdFCB = &(PtrFCB->NTRequiredFCB);
// This is a good place for oplock related processing.
// Chapter 11 expands upon this topic in greater detail.
// Check whether the desired read can be allowed depending
// on any byte range locks that might exist. Note that for
// paging-io, no such checks should be performed.
if (!PagingIo) {
// Insert code to perform the check here ...
// if (!SFsdCheckForByteLock(PtrFCB, PtrCCB, PtrIrp, PtrCurrentIoStackLocation)) {
// try_return(RC = STATUS_FILE_LOCK_CONFLICT);
// }
}
// There are certain complications that arise when the same file stream
// has been opened for cached and non-cached access. The FSD is then
// responsible for maintaining a consistent view of the data seen by
// the caller.
// Also, it is possible for file streams to be mapped in both as data files
// and as an executable. This could also lead to consistency problems since
// there now exist two separate sections (and pages) containing file
// information.
// Read Chapter 10 for more information on the issues involved in
// maintaining data consistency.
// The test below flushes the data cached in system memory if the current
// request madates non-cached access (file stream must be cached) and
// (a) the current request is not paging-io which indicates it is not
// a recursive I/O operation OR originating in the Cache Manager
// (b) OR the current request is paging-io BUT it did not originate via
// the Cache Manager (or is a recursive I/O operation) and we do
// have an image section that has been initialized.
#define SFSD_REQ_NOT_VIA_CACHE_MGR(ptr) (!MmIsRecursiveIoFault() && ((ptr)->ImageSectionObject != NULL))
if (NonBufferedIo && (PtrReqdFCB->SectionObject.DataSectionObject != NULL)) {
if (!PagingIo || (SFSD_REQ_NOT_VIA_CACHE_MGR(&(PtrReqdFCB->SectionObject)))) {
CcFlushCache(&(PtrReqdFCB->SectionObject), &ByteOffset, ReadLength, &(PtrIrp->IoStatus));
// If the flush failed, return error to the caller
if (!NT_SUCCESS(RC = PtrIrp->IoStatus.Status)) {
try_return(RC);
}
}
}
// Acquire the appropriate FCB resource shared
if (PagingIo) {
// Try to acquire the FCB PagingIoResource shared
if (!ExAcquireResourceSharedLite(&(PtrReqdFCB->PagingIoResource), CanWait)) {
CompleteIrp = FALSE;
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
// Remember the resource that was acquired
PtrResourceAcquired = &(PtrReqdFCB->PagingIoResource);
} else {
// Try to acquire the FCB MainResource shared
if (!ExAcquireResourceSharedLite(&(PtrReqdFCB->MainResource), CanWait)) {
CompleteIrp = FALSE;
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
// Remember the resource that was acquired
PtrResourceAcquired = &(PtrReqdFCB->MainResource);
}
// Validate start offset and length supplied.
// If start offset is > end-of-file, return an appropriate error. Note
// that since a FCB resource has already been acquired, and since all
// file size changes require acquisition of both FCB resources (see
// Chapter 10), the contents of the FCB and associated data structures
// can safely be examined.
// Also note that I am using the file size in the "Common FCB Header"
// to perform the check. However, your FSD might decide to keep a
// separate copy in the FCB (or some other representation of the
// file associated with the FCB).
if (RtlLargeIntegerGreaterThan(ByteOffset, PtrReqdFCB->CommonFCBHeader.FileSize)) {
// Starting offset is > file size
try_return(RC = STATUS_END_OF_FILE);
}
// We can also go ahead and truncate the read length here
// such that it is contained within the file size
// This is also a good place to set whether fast-io can be performed
// on this particular file or not. Your FSD must make it's own
// determination on whether or not to allow fast-io operations.
// Commonly, fast-io is not allowed if any byte range locks exist
// on the file or if oplocks prevent fast-io. Practically any reason
// choosen by your FSD could result in your setting FastIoIsNotPossible
// OR FastIoIsQuestionable instead of FastIoIsPossible.
//
// PtrReqdFCB->CommonFCBHeader.IsFastIoPossible = FastIoIsPossible;
// Branch here for cached vs non-cached I/O
if (!NonBufferedIo) {
// The caller wishes to perform cached I/O. Initiate caching if
// this is the first cached I/O operation using this file object
if (PtrFileObject->PrivateCacheMap == NULL) {
// This is the first cached I/O operation. You must ensure
// that the FCB Common FCB Header contains valid sizes at this time
CcInitializeCacheMap(PtrFileObject, (PCC_FILE_SIZES)(&(PtrReqdFCB->CommonFCBHeader.AllocationSize)),
FALSE, // We will not utilize pin access for this file
&(SFsdGlobalData.CacheMgrCallBacks), // callbacks
PtrCCB); // The context used in callbacks
}
// Check and see if this request requires a MDL returned to the caller
if (PtrIoStackLocation->MinorFunction & IRP_MN_MDL) {
// Caller does want a MDL returned. Note that this mode
// implies that the caller is prepared to block
CcMdlRead(PtrFileObject, &ByteOffset, TruncatedReadLength, &(PtrIrp->MdlAddress), &(PtrIrp->IoStatus));
NumberBytesRead = PtrIrp->IoStatus.Information;
RC = PtrIrp->IoStatus.Status;
try_return(RC);
}
// This is a regular run-of-the-mill cached I/O request. Let the
// Cache Manager worry about it!
// First though, we need a buffer pointer (address) that is valid
// More on this in Chapter 10
PtrSystemBuffer = SFsdGetCallersBuffer(PtrIrp);
ASSERT(PtrSystemBuffer);
if (!CcCopyRead(PtrFileObject, &(ByteOffset), ReadLength, CanWait, PtrSystemBuffer, &(PtrIrp->IoStatus))) {
// The caller was not prepared to block and data is not immediately
// available in the system cache
CompleteIrp = FALSE;
PostRequest = TRUE;
// Mark Irp Pending ...
try_return(RC = STATUS_PENDING);
}
// We have the data
RC = PtrIrp->IoStatus.Status;
NumberBytesRead = PtrIrp->IoStatus.Information;
try_return(RC);
} else {
// Send the request to lower level drivers
// For paging-io, the FSD has to trust the VMM to do the right thing
// Here is a common method used by Windows NT native file systems
// that are in the process of sending a request to the disk driver.
// First, mark the IRP as pending, then invoke the lower level driver
// after setting a completion routine.
// Meanwhile, this particular thread can immediately return a
// STATUS_PENDING return code.
// The completion routine is then responsible for completing the IRP
// and unlocking appropriate resources
// Also, at this point, your FSD might choose to utilize the
// information contained in the ValidDataLength field to simply
// return zeroes to the caller for reads extending beyond current
// valid data length.
IoMarkIrpPending(PtrIrp);
// Invoke a routine to read disk information at this time
// You will need to set a completion routine before invoking
// a lower level driver
CompleteIrp = FALSE;
try_return(RC = STATUS_PENDING);
}
try_exit: NOTHING;
} finally {
// Post IRP if required
if (PostRequest) {
// Release any resources acquired here ...
if (PtrResourceAcquired) {
SFsdReleaseResource(PtrResourceAcquired);
}
// Implement a routine that will queue up the request to be executed
// later (asynchronously) in the context of a system worker thread.
// See Chapter 10 for details.
// Lock the callers buffer here. Then invoke a common routine to
// perform the post operation.
if (!(PtrIoStackLocation->MinorFunction & IRP_MN_MDL)) {
RC = SFsdLockCallersBuffer(PtrIrp, TRUE, ReadLength);
ASSERT(NT_SUCCESS(RC));
}
// Perform the post operation which will mark the IRP pending
// and will return STATUS_PENDING back to us
RC = SFsdPostRequest(PtrIrpContext, PtrIrp);
} else if (CompleteIrp && !(RC == STATUS_PENDING)) {
// For synchronous I/O, the FSD must maintain the current byte offset
// Do not do this however, if I/O is marked as paging-io
if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) {
PtrFileObject->CurrentByteOffset = RtlLargeIntegerAdd(ByteOffset, RtlConvertUlongToLargeInteger((unsigned long)NumberBytesRead));
}
// If the read completed successfully and this was not a paging-io
// operation, set a flag in the CCB that indicates that a read was
// performed and that the file time should be updated at cleanup
if (NT_SUCCESS(RC) && !PagingIo) {
SFsdSetFlag(PtrCCB->CCBFlags, SFSD_CCB_ACCESSED);
}
if (PtrResourceAcquired) {
SFsdReleaseResource(PtrResourceAcquired);
}
// Can complete the IRP here if no exception was encountered
if (!(PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_EXCEPTION)) {
PtrIrp->IoStatus.Status = RC;
PtrIrp->IoStatus.Information = NumberBytesRead;
// Free up the Irp Context
SFsdReleaseIrpContext(PtrIrpContext);
// complete the IRP
IoCompleteRequest(PtrIrp, IO_DISK_INCREMENT);
}
} // can we complete the IRP ?
} // end of "finally" processing
return(RC);
}
/*************************************************************************
*
* Function: SFsdGetCallersBuffer()
*
* Description:
* Obtain a pointer to the caller's buffer.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
void *SFsdGetCallersBuffer(
PIRP PtrIrp)
{
void *ReturnedBuffer = NULL;
// If an MDL is supplied, use it.
if (PtrIrp->MdlAddress) {
ReturnedBuffer = MmGetSystemAddressForMdl(PtrIrp->MdlAddress);
} else {
ReturnedBuffer = PtrIrp->UserBuffer;
}
return(ReturnedBuffer);
}
/*************************************************************************
*
* Function: SFsdLockCallersBuffer()
*
* Description:
* Obtain a MDL that describes the buffer. Lock pages for I/O
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdLockCallersBuffer(
PIRP PtrIrp,
BOOLEAN IsReadOperation,
uint32 Length)
{
NTSTATUS RC = STATUS_SUCCESS;
PMDL PtrMdl = NULL;
ASSERT(PtrIrp);
try {
// Is a MDL already present in the IRP
if (!(PtrIrp->MdlAddress)) {
// Allocate a MDL
if (!(PtrMdl = IoAllocateMdl(PtrIrp->UserBuffer, Length, FALSE, FALSE, PtrIrp))) {
RC = STATUS_INSUFFICIENT_RESOURCES;
try_return(RC);
}
// Probe and lock the pages described by the MDL
// We could encounter an exception doing so, swallow the exception
// NOTE: The exception could be due to an unexpected (from our
// perspective), invalidation of the virtual addresses that comprise
// the passed in buffer
try {
MmProbeAndLockPages(PtrMdl, PtrIrp->RequestorMode, (IsReadOperation ? IoWriteAccess:IoReadAccess));
} except(EXCEPTION_EXECUTE_HANDLER) {
RC = STATUS_INVALID_USER_BUFFER;
}
}
try_exit: NOTHING;
} finally {
if (!NT_SUCCESS(RC) && PtrMdl) {
IoFreeMdl(PtrMdl);
// You MUST NULL out the MdlAddress field in the IRP after freeing
// the MDL, else the I/O Manager will also attempt to free the MDL
// pointed to by that field during I/O completion. Obviously, the
// pointer becomes invalid once you free the allocated MDL and hence
// you will encounter a system crash during IRP completion.
PtrIrp->MdlAddress = NULL;
}
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdMdlComplete()
*
* Description:
* Tell Cache Manager to release MDL (and possibly flush).
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None.
*
*************************************************************************/
void SFsdMdlComplete(
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp,
PIO_STACK_LOCATION PtrIoStackLocation,
BOOLEAN ReadCompletion)
{
NTSTATUS RC = STATUS_SUCCESS;
PFILE_OBJECT PtrFileObject = NULL;
PtrFileObject = PtrIoStackLocation->FileObject;
ASSERT(PtrFileObject);
// Not much to do here.
if (ReadCompletion) {
CcMdlReadComplete(PtrFileObject, PtrIrp->MdlAddress);
} else {
// The Cache Manager needs the byte offset in the I/O stack location.
CcMdlWriteComplete(PtrFileObject, &(PtrIoStackLocation->Parameters.Write.ByteOffset), PtrIrp->MdlAddress);
}
// Clear the MDL address field in the IRP so the IoCompleteRequest()
// does not try to play around with the MDL.
PtrIrp->MdlAddress = NULL;
// Free up the Irp Context.
SFsdReleaseIrpContext(PtrIrpContext);
// Complete the IRP.
PtrIrp->IoStatus.Status = RC;
PtrIrp->IoStatus.Information = 0;
IoCompleteRequest(PtrIrp, IO_NO_INCREMENT);
return;
}