//////////////////////////////////////////////////////////////////// // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine // All rights reserved // This file was released under the GPLv2 on June 2015. //////////////////////////////////////////////////////////////////// /************************************************************************* * * File: Read.cpp * * Module: UDF File System Driver (Kernel mode execution only) * * Description: * Contains code to handle the "Read" dispatch entry point. * *************************************************************************/ #include "udffs.h" // define the file specific bug-check id #define UDF_BUG_CHECK_ID UDF_FILE_READ #ifdef _M_IX86 #if DBG #define OVERFLOW_READ_THRESHHOLD (0xE00) #else #define OVERFLOW_READ_THRESHHOLD (0xA00) #endif // UDF_DBG #else // defined(_M_IX86) #define OVERFLOW_READ_THRESHHOLD (0x1000) #endif // defined(_M_IX86) //#define POST_LOCK_PAGES /************************************************************************* * * Function: UDFRead() * * 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 NTAPI UDFRead( PDEVICE_OBJECT DeviceObject, // the logical volume device object PIRP Irp) // I/O Request Packet { NTSTATUS RC = STATUS_SUCCESS; PtrUDFIrpContext PtrIrpContext = NULL; BOOLEAN AreWeTopLevel = FALSE; TmPrint(("UDFRead: \n")); FsRtlEnterFileSystem(); ASSERT(DeviceObject); ASSERT(Irp); // set the top level context AreWeTopLevel = UDFIsIrpTopLevel(Irp); ASSERT(!UDFIsFSDevObj(DeviceObject)); _SEH2_TRY { // get an IRP context structure and issue the request PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject); if(PtrIrpContext) { RC = UDFCommonRead(PtrIrpContext, Irp); } else { RC = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; // complete the IRP IoCompleteRequest(Irp, IO_DISK_INCREMENT); } } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) { RC = UDFExceptionHandler(PtrIrpContext, Irp); UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC); } _SEH2_END; if (AreWeTopLevel) { IoSetTopLevelIrp(NULL); } FsRtlExitFileSystem(); return(RC); } // end UDFRead() /************************************************************************* * * Function: UDFPostStackOverflowRead() * * Description: * Post a read request that could not be processed by * the fsp thread because of stack overflow potential. * * Arguments: * Irp - Supplies the request to process. * Fcb - Supplies the file. * * Return Value: STATUS_PENDING. * *************************************************************************/ NTSTATUS UDFPostStackOverflowRead( IN PtrUDFIrpContext PtrIrpContext, IN PIRP Irp, IN PtrUDFFCB Fcb ) { PKEVENT Event; PERESOURCE Resource; UDFPrint(("Getting too close to stack limit pass request to Fsp\n")); // Allocate an event and get shared on the resource we will // be later using the common read. Event = (PKEVENT)MyAllocatePool__(NonPagedPool, sizeof(KEVENT)); if(!Event) return STATUS_INSUFFICIENT_RESOURCES; KeInitializeEvent( Event, NotificationEvent, FALSE ); if ((Irp->Flags & IRP_PAGING_IO) && (Fcb->NTRequiredFCB->CommonFCBHeader.PagingIoResource)) { Resource = Fcb->NTRequiredFCB->CommonFCBHeader.PagingIoResource; } else { Resource = Fcb->NTRequiredFCB->CommonFCBHeader.Resource; } UDFAcquireResourceShared( Resource, TRUE ); _SEH2_TRY { // If this read is the result of a verify, we have to // tell the overflow read routne to temporarily // hijack the Vcb->VerifyThread field so that reads // can go through. FsRtlPostStackOverflow(PtrIrpContext, Event, UDFStackOverflowRead); // And wait for the worker thread to complete the item DbgWaitForSingleObject(Event, NULL); } _SEH2_FINALLY { UDFReleaseResource( Resource ); MyFreePool__( Event ); } _SEH2_END; return STATUS_PENDING; } // end UDFPostStackOverflowRead() /************************************************************************* * * Function: UDFStackOverflowRead() * * Description: * Process 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. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: None. * *************************************************************************/ VOID NTAPI UDFStackOverflowRead( IN PVOID Context, IN PKEVENT Event ) { PtrUDFIrpContext PtrIrpContext = (PtrUDFIrpContext)Context; NTSTATUS RC; UDFPrint(("UDFStackOverflowRead: \n")); // Make it now look like we can wait for I/O to complete PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK; // Do the read operation protected by a try-except clause _SEH2_TRY { UDFCommonRead(PtrIrpContext, PtrIrpContext->Irp); } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) { RC = UDFExceptionHandler(PtrIrpContext, PtrIrpContext->Irp); UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC); } _SEH2_END; // Set the stack overflow item's event to tell the original // thread that we're done. KeSetEvent( Event, 0, FALSE ); } // end UDFStackOverflowRead() /************************************************************************* * * Function: UDFCommonRead() * * 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 UDFCommonRead( PtrUDFIrpContext PtrIrpContext, PIRP Irp ) { NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp = NULL; LARGE_INTEGER ByteOffset; ULONG ReadLength = 0, TruncatedLength = 0; ULONG NumberBytesRead = 0; PFILE_OBJECT FileObject = NULL; PtrUDFFCB Fcb = NULL; PtrUDFCCB Ccb = NULL; PVCB Vcb = NULL; PtrUDFNTRequiredFCB NtReqFcb = NULL; PERESOURCE PtrResourceAcquired = NULL; PERESOURCE PtrResourceAcquired2 = NULL; PVOID SystemBuffer = NULL; PIRP TopIrp; // uint32 KeyValue = 0; ULONG Res1Acq = 0; ULONG Res2Acq = 0; BOOLEAN CacheLocked = FALSE; BOOLEAN CanWait = FALSE; BOOLEAN PagingIo = FALSE; BOOLEAN NonBufferedIo = FALSE; BOOLEAN SynchronousIo = FALSE; TmPrint(("UDFCommonRead: irp %x\n", Irp)); _SEH2_TRY { TopIrp = IoGetTopLevelIrp(); switch((ULONG)TopIrp) { case FSRTL_FSP_TOP_LEVEL_IRP: UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n")); break; case FSRTL_CACHE_TOP_LEVEL_IRP: UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n")); break; case FSRTL_MOD_WRITE_TOP_LEVEL_IRP: UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n")); // BrutePoint() break; case FSRTL_FAST_IO_TOP_LEVEL_IRP: UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n")); // BrutePoint() break; case NULL: UDFPrint((" NULL TOP_LEVEL_IRP\n")); break; default: if(TopIrp == Irp) { UDFPrint((" TOP_LEVEL_IRP\n")); } else { UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp)); } break; } // First, get a pointer to the current I/O stack location IrpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(IrpSp); MmPrint((" Enter Irp, MDL=%x\n", Irp->MdlAddress)); if(Irp->MdlAddress) { UDFTouch(Irp->MdlAddress); } // If this happens to be a MDL read complete request, then // there is not much processing that the FSD has to do. if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { // Caller wants to tell the Cache Manager that a previously // allocated MDL can be freed. UDFMdlComplete(PtrIrpContext, Irp, IrpSp, TRUE); // The IRP has been completed. 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 (IrpSp->MinorFunction & IRP_MN_DPC) { try_return(RC = STATUS_PENDING); } FileObject = IrpSp->FileObject; ASSERT(FileObject); // Get the FCB and CCB pointers Ccb = (PtrUDFCCB)(FileObject->FsContext2); ASSERT(Ccb); Fcb = Ccb->Fcb; ASSERT(Fcb); Vcb = Fcb->Vcb; if(Fcb->FCBFlags & UDF_FCB_DELETED) { ASSERT(FALSE); try_return(RC = STATUS_ACCESS_DENIED); } // check for stack overflow if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) { RC = UDFPostStackOverflowRead( PtrIrpContext, Irp, Fcb ); try_return(RC); } // Disk based file systems might decide to verify the logical volume // (if required and only if removable media are supported) at this time // As soon as Tray is locked, we needn't call UDFVerifyVcb() ByteOffset = IrpSp->Parameters.Read.ByteOffset; CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; PagingIo = (Irp->Flags & IRP_PAGING_IO) ? TRUE : FALSE; NonBufferedIo = (Irp->Flags & IRP_NOCACHE) ? TRUE : FALSE; SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO) ? TRUE : FALSE; UDFPrint((" Flags: %s %s %s %s\n", CanWait ? "W" : "w", PagingIo ? "Pg" : "pg", NonBufferedIo ? "NBuf" : "buff", SynchronousIo ? "Snc" : "Asc")); if(!NonBufferedIo && (Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB)) { if(UDFIsAStream(Fcb->FileInfo)) { UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, FILE_NOTIFY_CHANGE_LAST_ACCESS, FILE_ACTION_MODIFIED_STREAM); } else { UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, FILE_NOTIFY_CHANGE_LAST_ACCESS, FILE_ACTION_MODIFIED); } } // Get some of the parameters supplied to us ReadLength = IrpSp->Parameters.Read.Length; if (ReadLength == 0) { // a 0 byte read can be immediately succeeded try_return(RC); } UDFPrint((" ByteOffset = %I64x, ReadLength = %x\n", ByteOffset.QuadPart, ReadLength)); // Is this a read of the volume itself ? if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) { // Yup, we need to send this on to the disk driver after // validation of the offset and length. Vcb = (PVCB)Fcb; Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; if(!CanWait) try_return(RC = STATUS_PENDING); if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) { UDFPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n")); PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED; if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) { UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); } #ifdef UDF_DELAYED_CLOSE UDFCloseAllDelayed(Vcb); #endif //UDF_DELAYED_CLOSE } if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH_REQUIRED) { UDFPrint((" UDF_IRP_CONTEXT_FLUSH_REQUIRED\n")); PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH_REQUIRED; // Acquire the volume resource exclusive UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); PtrResourceAcquired = &(Vcb->VCBResource); UDFFlushLogicalVolume(NULL, NULL, Vcb, 0); UDFReleaseResource(PtrResourceAcquired); PtrResourceAcquired = NULL; } // Acquire the volume resource shared ... UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); PtrResourceAcquired = &(Vcb->VCBResource); #if 0 if(PagingIo) { CollectStatistics(Vcb, MetaDataReads); CollectStatisticsEx(Vcb, MetaDataReadBytes, NumberBytesRead); } #endif // Forward the request to the lower level driver // Lock the callers buffer if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, ReadLength))) { try_return(RC); } SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); if(!SystemBuffer) { try_return(RC = STATUS_INVALID_USER_BUFFER); } if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) { RC = UDFReadData(Vcb, TRUE, ByteOffset.QuadPart, ReadLength, FALSE, (PCHAR)SystemBuffer, &NumberBytesRead); } else { RC = UDFTRead(Vcb, SystemBuffer, ReadLength, (ULONG)(ByteOffset.QuadPart >> Vcb->BlockSizeBits), &NumberBytesRead); } UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); try_return(RC); } Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; // 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(Fcb->FCBFlags & UDF_FCB_PAGE_FILE) { NonBufferedIo = TRUE; } if(ByteOffset.HighPart == -1) { if(ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) { ByteOffset = FileObject->CurrentByteOffset; } } // If this read is directed to a directory, it is not allowed // by the UDF FSD. if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) { RC = STATUS_INVALID_DEVICE_REQUEST; try_return(RC); } NtReqFcb = Fcb->NTRequiredFCB; Res1Acq = UDFIsResourceAcquired(&(NtReqFcb->MainResource)); if(!Res1Acq) { Res1Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES1_ACQ; } Res2Acq = UDFIsResourceAcquired(&(NtReqFcb->PagingIoResource)); if(!Res2Acq) { Res2Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES2_ACQ; } #if 0 if(PagingIo) { CollectStatistics(Vcb, UserFileReads); CollectStatisticsEx(Vcb, UserFileReadBytes, NumberBytesRead); } #endif // This is a good place for oplock related processing. // If this is the normal file we have to check for // write access according to the current state of the file locks. if (!PagingIo && !FsRtlCheckLockForReadAccess( &(NtReqFcb->FileLock), Irp )) { try_return( RC = STATUS_FILE_LOCK_CONFLICT ); } // 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, // the contents of the FCB and associated data structures // can safely be examined. // Also note that we are 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). TruncatedLength = ReadLength; if (ByteOffset.QuadPart >= NtReqFcb->CommonFCBHeader.FileSize.QuadPart) { // 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 if( NtReqFcb->CommonFCBHeader.FileSize.QuadPart < (ByteOffset.QuadPart + ReadLength) ) { TruncatedLength = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart); // we can't get ZERO here } UDFPrint((" TruncatedLength = %x\n", TruncatedLength)); // 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. // 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 UDF_REQ_NOT_VIA_CACHE_MGR(ptr) (!MmIsRecursiveIoFault() && ((ptr)->ImageSectionObject != NULL)) if(NonBufferedIo && (NtReqFcb->SectionObject.DataSectionObject != NULL)) { if(!PagingIo) { /* // We hold the main resource exclusive here because the flush // may generate a recursive write in this thread. The PagingIo // resource is held shared so the drop-and-release serialization // below will work. if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) { try_return(RC = STATUS_PENDING); } PtrResourceAcquired = &(NtReqFcb->MainResource); // We hold PagingIo shared around the flush to fix a // cache coherency problem. UDFAcquireResourceShared(&(NtReqFcb->PagingIoResource), TRUE );*/ MmPrint((" CcFlushCache()\n")); CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, TruncatedLength, &(Irp->IoStatus)); /* UDFReleaseResource(&(NtReqFcb->PagingIoResource)); UDFReleaseResource(PtrResourceAcquired); PtrResourceAcquired = NULL; // If the flush failed, return error to the caller if(!NT_SUCCESS(RC = Irp->IoStatus.Status)) { try_return(RC); } // Acquiring and immediately dropping the resource serializes // us behind any other writes taking place (either from the // lazy writer or modified page writer).*/ if(!Res2Acq) { UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE ); UDFReleaseResource(&(NtReqFcb->PagingIoResource)); } } } // Acquire the appropriate FCB resource shared if (PagingIo) { // Try to acquire the FCB PagingIoResource shared if(!Res2Acq) { if (!UDFAcquireResourceShared(&(NtReqFcb->PagingIoResource), CanWait)) { try_return(RC = STATUS_PENDING); } // Remember the resource that was acquired PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); } } else { // Try to acquire the FCB MainResource shared if(NonBufferedIo) { if(!Res2Acq) { if(!UDFAcquireSharedWaitForExclusive(&(NtReqFcb->PagingIoResource), CanWait)) { try_return(RC = STATUS_PENDING); } PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); } } else { if(!Res1Acq) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) { try_return(RC = STATUS_PENDING); } // Remember the resource that was acquired PtrResourceAcquired = &(NtReqFcb->MainResource); } } } // 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. NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb); /* if(NtReqFcb->CommonFCBHeader.IsFastIoPossible == FastIoIsPossible) NtReqFcb->CommonFCBHeader.IsFastIoPossible = FastIoIsQuestionable;*/ #ifdef UDF_DISABLE_SYSTEM_CACHE_MANAGER NonBufferedIo = TRUE; #endif if(Fcb && Fcb->FileInfo && Fcb->FileInfo->Dloc) { AdPrint(("UDFCommonRead: DataLoc %x, Mapping %x\n", &Fcb->FileInfo->Dloc->DataLoc, Fcb->FileInfo->Dloc->DataLoc.Mapping)); } // Branch here for cached vs non-cached I/O if (!NonBufferedIo) { if(FileObject->Flags & FO_WRITE_THROUGH) { CanWait = TRUE; } // The caller wishes to perform cached I/O. Initiate caching if // this is the first cached I/O operation using this file object if (!(FileObject->PrivateCacheMap)) { // This is the first cached I/O operation. You must ensure // that the FCB Common FCB Header contains valid sizes at this time MmPrint((" CcInitializeCacheMap()\n")); CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&(NtReqFcb->CommonFCBHeader.AllocationSize)), FALSE, // We will not utilize pin access for this file &(UDFGlobalData.CacheMgrCallBacks), // callbacks NtReqFcb); // The context used in callbacks MmPrint((" CcSetReadAheadGranularity()\n")); CcSetReadAheadGranularity(FileObject, Vcb->SystemCacheGran); } // Check and see if this request requires a MDL returned to the caller if (IrpSp->MinorFunction & IRP_MN_MDL) { // Caller does want a MDL returned. Note that this mode // implies that the caller is prepared to block MmPrint((" CcMdlRead()\n")); // CcMdlRead(FileObject, &ByteOffset, TruncatedLength, &(Irp->MdlAddress), &(Irp->IoStatus)); // NumberBytesRead = Irp->IoStatus.Information; // RC = Irp->IoStatus.Status; NumberBytesRead = 0; RC = STATUS_INVALID_PARAMETER; 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 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); if(!SystemBuffer) try_return(RC = STATUS_INVALID_USER_BUFFER); ASSERT(SystemBuffer); MmPrint((" CcCopyRead()\n")); if (!CcCopyRead(FileObject, &(ByteOffset), TruncatedLength, CanWait, SystemBuffer, &(Irp->IoStatus))) { // The caller was not prepared to block and data is not immediately // available in the system cache try_return(RC = STATUS_PENDING); } UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); // We have the data RC = Irp->IoStatus.Status; NumberBytesRead = Irp->IoStatus.Information; try_return(RC); } else { MmPrint((" Read NonBufferedIo\n")); #if 1 if((ULONG)TopIrp == FSRTL_MOD_WRITE_TOP_LEVEL_IRP) { UDFPrint(("FSRTL_MOD_WRITE_TOP_LEVEL_IRP => CanWait\n")); CanWait = TRUE; } else if((ULONG)TopIrp == FSRTL_CACHE_TOP_LEVEL_IRP) { UDFPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n")); CanWait = TRUE; } if(NtReqFcb->AcqSectionCount || NtReqFcb->AcqFlushCount) { MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb->AcqSectionCount, NtReqFcb->AcqFlushCount)); CanWait = TRUE; } else {} /* if((TopIrp != Irp)) { UDFPrint(("(TopIrp != Irp) => CanWait\n")); CanWait = TRUE; } else*/ #endif if(KeGetCurrentIrql() > PASSIVE_LEVEL) { MmPrint((" !PASSIVE_LEVEL\n")); CanWait = FALSE; PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FORCED_POST; } if(!CanWait && UDFIsFileCached__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, FALSE)) { MmPrint((" Locked => CanWait\n")); CacheLocked = TRUE; CanWait = TRUE; } // Send the request to lower level drivers if(!CanWait) { try_return(RC = STATUS_PENDING); } // ASSERT(NT_SUCCESS(RC)); if(!Res2Acq) { if(UDFAcquireResourceSharedWithCheck(&(NtReqFcb->PagingIoResource))) PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); } RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, TruncatedLength); if(!NT_SUCCESS(RC)) { try_return(RC); } SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); if(!SystemBuffer) { try_return(RC = STATUS_INVALID_USER_BUFFER); } RC = UDFReadFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, CacheLocked, (PCHAR)SystemBuffer, &NumberBytesRead); /* // AFAIU, CacheManager wants this: if(!NT_SUCCESS(RC)) { NumberBytesRead = 0; }*/ UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); #if 0 if(PagingIo) { CollectStatistics(Vcb, UserDiskReads); } else { CollectStatistics2(Vcb, NonCachedDiskReads); } #endif try_return(RC); // 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, the 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. } try_exit: NOTHING; } _SEH2_FINALLY { if(CacheLocked) { WCacheEODirect__(&(Vcb->FastCache), Vcb); } // Release any resources acquired here ... if(PtrResourceAcquired2) { UDFReleaseResource(PtrResourceAcquired2); } if(PtrResourceAcquired) { if(NtReqFcb && (PtrResourceAcquired == &(NtReqFcb->MainResource))) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); } UDFReleaseResource(PtrResourceAcquired); } // Post IRP if required if(RC == STATUS_PENDING) { // Lock the callers buffer here. Then invoke a common routine to // perform the post operation. if (!(IrpSp->MinorFunction & IRP_MN_MDL)) { RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, ReadLength); ASSERT(NT_SUCCESS(RC)); } if(PagingIo) { if(Res1Acq) { PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES1_ACQ; } if(Res2Acq) { PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES2_ACQ; } } // Perform the post operation which will mark the IRP pending // and will return STATUS_PENDING back to us RC = UDFPostRequest(PtrIrpContext, Irp); } else { // 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)) { FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + 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) { FileObject->Flags |= FO_FILE_FAST_IO_READ; Ccb->CCBFlags |= UDF_CCB_ACCESSED; } if(!_SEH2_AbnormalTermination()) { Irp->IoStatus.Status = RC; Irp->IoStatus.Information = NumberBytesRead; UDFPrint((" NumberBytesRead = %x\n", NumberBytesRead)); // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); // complete the IRP MmPrint((" Complete Irp, MDL=%x\n", Irp->MdlAddress)); if(Irp->MdlAddress) { UDFTouch(Irp->MdlAddress); } IoCompleteRequest(Irp, IO_DISK_INCREMENT); } } // can we complete the IRP ? } _SEH2_END; // end of "__finally" processing return(RC); } // end UDFCommonRead() #ifdef UDF_DBG ULONG LockBufferCounter = 0; ULONG BuildMdlCounter = 0; #endif //UDF_DBG /************************************************************************* * * Function: UDFGetCallersBuffer() * * Description: * Obtain a pointer to the caller's buffer. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ PVOID UDFGetCallersBuffer( PtrUDFIrpContext PtrIrpContext, PIRP Irp ) { VOID *ReturnedBuffer = NULL; UDFPrint(("UDFGetCallersBuffer: \n")); // If an MDL is supplied, use it. if(Irp->MdlAddress) { MmPrint((" UDFGetCallersBuffer: MmGetSystemAddressForMdl(Irp->MdlAddress) MDL=%x\n", Irp->MdlAddress)); // ReturnedBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); ReturnedBuffer = MmGetSystemAddressForMdlSafer(Irp->MdlAddress); } else if (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_BUFFER_LOCKED) { // Free buffer #ifndef POST_LOCK_PAGES MmPrint((" UDFGetCallersBuffer: MmGetSystemAddressForMdl(PtrIrpContext->PtrMdl) MDL=%x\n", PtrIrpContext->PtrMdl)); ReturnedBuffer = MmGetSystemAddressForMdlSafe(PtrIrpContext->PtrMdl, NormalPagePriority); #else //POST_LOCK_PAGES if(PtrIrpContext->TransitionBuffer) { MmPrint((" UDFGetCallersBuffer: TransitionBuffer\n")); return PtrIrpContext->TransitionBuffer; } _SEH2_TRY { MmPrint((" MmProbeAndLockPages()\n")); MmProbeAndLockPages(PtrIrpContext->PtrMdl, Irp->RequestorMode, ((PtrIrpContext->MajorFunction == IRP_MJ_READ) ? IoWriteAccess:IoReadAccess)); #ifdef UDF_DBG LockBufferCounter++; #endif //UDF_DBG } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { //RC = STATUS_INVALID_USER_BUFFER; BrutePoint(); return NULL; } _SEH2_END; MmPrint((" MmGetSystemAddressForMdlSafer()\n")); ReturnedBuffer = MmGetSystemAddressForMdlSafer(PtrIrpContext->PtrMdl); #endif //POST_LOCK_PAGES } else { MmPrint((" UDFGetCallersBuffer: Irp->UserBuffer\n")); ReturnedBuffer = Irp->UserBuffer; } return(ReturnedBuffer); } // end UDFGetCallersBuffer() /************************************************************************* * * Function: UDFLockCallersBuffer() * * 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 UDFLockCallersBuffer( PtrUDFIrpContext PtrIrpContext, PIRP Irp, BOOLEAN IsReadOperation, uint32 Length ) { NTSTATUS RC = STATUS_SUCCESS; PMDL PtrMdl = NULL; UDFPrint(("UDFLockCallersBuffer: \n")); ASSERT(Irp); _SEH2_TRY { // Is a MDL already present in the IRP if (!(Irp->MdlAddress)) { // Allocate a MDL /* if(!IsReadOperation) { MmPrint((" Allocate TransitionBuffer\n")); PtrIrpContext->TransitionBuffer = (PCHAR)DbgAllocatePool(NonPagedPool, Length); if(!PtrIrpContext->TransitionBuffer) { RC = STATUS_INSUFFICIENT_RESOURCES; try_return(RC); } _SEH2_TRY { RtlCopyMemory(PtrIrpContext->TransitionBuffer, Irp->UserBuffer, Length); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { RC = STATUS_INVALID_USER_BUFFER; } _SEH2_END; } else*/ { MmPrint((" IoAllocateMdl()\n")); // if (!(PtrMdl = IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, NULL))) { // This will place allocated Mdl to Irp if (!(PtrMdl = IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, Irp))) { RC = STATUS_INSUFFICIENT_RESOURCES; try_return(RC); } MmPrint((" Alloc MDL=%x\n", PtrMdl)); #ifdef UDF_DBG BuildMdlCounter++; #endif //UDF_DBG } // 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 #ifndef POST_LOCK_PAGES _SEH2_TRY { MmPrint((" MmProbeAndLockPages()\n")); MmProbeAndLockPages(PtrMdl, Irp->RequestorMode, (IsReadOperation ? IoWriteAccess:IoReadAccess)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { MmPrint((" MmProbeAndLockPages() failed\n")); Irp->MdlAddress = NULL; RC = STATUS_INVALID_USER_BUFFER; } _SEH2_END; #endif //POST_LOCK_PAGES if(NT_SUCCESS(RC)) { PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_BUFFER_LOCKED; PtrIrpContext->PtrMdl = PtrMdl; } } else { MmPrint((" UDFLockCallersBuffer: do nothing, MDL=%x\n", Irp->MdlAddress)); UDFTouch(Irp->MdlAddress); } try_exit: NOTHING; } _SEH2_FINALLY { if (!NT_SUCCESS(RC) && PtrMdl) { MmPrint((" Free MDL=%x\n", PtrMdl)); IoFreeMdl(PtrMdl); } } _SEH2_END; return(RC); } // end UDFLockCallersBuffer() /************************************************************************* * * Function: UDFUnlockCallersBuffer() * * 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 UDFUnlockCallersBuffer( PtrUDFIrpContext PtrIrpContext, PIRP Irp, PVOID SystemBuffer ) { NTSTATUS RC = STATUS_SUCCESS; UDFPrint(("UDFUnlockCallersBuffer: \n")); ASSERT(Irp); _SEH2_TRY { // Is a nonPaged buffer already present in the IRP if (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_BUFFER_LOCKED) { UDFPrint((" UDF_IRP_CONTEXT_BUFFER_LOCKED MDL=%x, Irp MDL=%x\n", PtrIrpContext->PtrMdl, Irp->MdlAddress)); if(PtrIrpContext->TransitionBuffer) { MmPrint((" UDFUnlockCallersBuffer: free TransitionBuffer\n")); DbgFreePool(PtrIrpContext->TransitionBuffer); PtrIrpContext->TransitionBuffer = NULL; PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_BUFFER_LOCKED; try_return(RC); } // Free buffer KeFlushIoBuffers( PtrIrpContext->PtrMdl, TRUE, FALSE ); // MmPrint((" IrpCtx->Mdl, MmUnmapLockedPages()\n")); // MmUnmapLockedPages(SystemBuffer, PtrIrpContext->PtrMdl); // This will be done in IoCompleteIrp !!! //MmPrint((" MmUnlockPages()\n")); //MmUnlockPages(PtrIrpContext->PtrMdl); #ifdef UDF_DBG LockBufferCounter--; #endif //UDF_DBG // This will be done in IoCompleteIrp !!! //IoFreeMdl(PtrIrpContext->PtrMdl); #ifdef UDF_DBG BuildMdlCounter--; #endif //UDF_DBG UDFTouch(PtrIrpContext->PtrMdl); PtrIrpContext->PtrMdl = NULL; PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_BUFFER_LOCKED; } else if(Irp->MdlAddress) { // MmPrint((" Irp->Mdl, MmUnmapLockedPages()\n")); // MmUnmapLockedPages(SystemBuffer, Irp->MdlAddress); UDFPrint((" UDF_IRP_CONTEXT_BUFFER_LOCKED MDL=%x, Irp MDL=%x\n", PtrIrpContext->PtrMdl, Irp->MdlAddress)); UDFTouch(Irp->MdlAddress); KeFlushIoBuffers( Irp->MdlAddress, ((IoGetCurrentIrpStackLocation(Irp))->MajorFunction) == IRP_MJ_READ, FALSE ); } else { ; } try_exit: NOTHING; } _SEH2_FINALLY { NOTHING; } _SEH2_END; return(RC); } // end UDFUnlockCallersBuffer() /************************************************************************* * * Function: UDFMdlComplete() * * Description: * Tell Cache Manager to release MDL (and possibly flush). * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: None. * *************************************************************************/ VOID UDFMdlComplete( PtrUDFIrpContext PtrIrpContext, PIRP Irp, PIO_STACK_LOCATION IrpSp, BOOLEAN ReadCompletion) { NTSTATUS RC = STATUS_SUCCESS; PFILE_OBJECT FileObject = NULL; UDFPrint(("UDFMdlComplete: \n")); FileObject = IrpSp->FileObject; ASSERT(FileObject); UDFTouch(Irp->MdlAddress); // Not much to do here. if (ReadCompletion) { MmPrint((" CcMdlReadComplete() MDL=%x\n", Irp->MdlAddress)); CcMdlReadComplete(FileObject, Irp->MdlAddress); } else { // The Cache Manager needs the byte offset in the I/O stack location. MmPrint((" CcMdlWriteComplete() MDL=%x\n", Irp->MdlAddress)); CcMdlWriteComplete(FileObject, &(IrpSp->Parameters.Write.ByteOffset), Irp->MdlAddress); } // Clear the MDL address field in the IRP so the IoCompleteRequest() // does not __try to play around with the MDL. Irp->MdlAddress = NULL; // Free up the Irp Context. UDFReleaseIrpContext(PtrIrpContext); // Complete the IRP. Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return; }