/*++ Copyright (c) 1998-2001 Microsoft Corporation Module Name: filecache.cxx Abstract: This module implements the open file handle cache. Author: Keith Moore (keithmo) 21-Aug-1998 Revision History: --*/ #include "precomp.h" // // Private constants. // // // Private types. // #ifdef __cplusplus extern "C" { #endif // __cplusplus // // Private prototypes. // VOID UlpDestroyFileCacheEntry( IN PUL_WORK_ITEM pWorkItem ); BOOLEAN UlpFailMdlReadDev( IN struct _FILE_OBJECT *FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN ULONG LockKey, OUT PMDL *MdlChain, OUT PIO_STATUS_BLOCK IoStatus, IN struct _DEVICE_OBJECT *DeviceObject ); BOOLEAN UlpFailMdlReadCompleteDev( IN struct _FILE_OBJECT *FileObject, IN PMDL MdlChain, IN struct _DEVICE_OBJECT *DeviceObject ); NTSTATUS UlpRestartReadFileEntry( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID pContext ); #ifdef __cplusplus }; // extern "C" #endif // __cplusplus // // Private globals. // #ifdef ALLOC_PRAGMA #pragma alloc_text( INIT, InitializeFileCache ) #pragma alloc_text( PAGE, TerminateFileCache ) #pragma alloc_text( PAGE, UlCreateFileEntry ) #pragma alloc_text( PAGE, UlpFailMdlReadDev ) #pragma alloc_text( PAGE, UlpFailMdlReadCompleteDev ) #pragma alloc_text( PAGE, UlReadFileEntry ) #pragma alloc_text( PAGE, UlReadFileEntryFast ) #pragma alloc_text( PAGE, UlReadCompleteFileEntry ) #pragma alloc_text( PAGE, UlReadCompleteFileEntryFast ) #endif // ALLOC_PRAGMA #if 0 NOT PAGEABLE -- ReferenceCachedFile NOT PAGEABLE -- DereferenceCachedFile NOT PAGEABLE -- UlpRestartReadFileEntry #endif // // Public functions. // /***************************************************************************++ Routine Description: Performs global initialization of the open file cache. Return Value: NTSTATUS - Completion status. --***************************************************************************/ NTSTATUS InitializeFileCache( VOID ) { return STATUS_SUCCESS; // NYI } // InitializeFileCache /***************************************************************************++ Routine Description: Performs global termination of the open file cache. --***************************************************************************/ VOID TerminateFileCache( VOID ) { } // TerminateFileCache /***************************************************************************++ Routine Description: References the specified file cache entry. Arguments: pFileCacheEntry - Supplies the file cache entry to reference. --***************************************************************************/ VOID ReferenceCachedFile( IN PUL_FILE_CACHE_ENTRY pFileCacheEntry ) { LONG result; result = InterlockedIncrement( &pFileCacheEntry->ReferenceCount ); ASSERT( result > 1 ); IF_DEBUG( FILE_CACHE ) { KdPrint(( "ReferenceCachedFile: entry %p, ref %ld\n", pFileCacheEntry, result )); } } // ReferenceCachedFile /***************************************************************************++ Routine Description: Dereferences the specified file cache entry. Arguments: pFileCacheEntry - Supplies the file cache entry to dereference. --***************************************************************************/ VOID DereferenceCachedFile( IN PUL_FILE_CACHE_ENTRY pFileCacheEntry ) { LONG result; result = InterlockedDecrement( &pFileCacheEntry->ReferenceCount ); ASSERT( result >= 0 ); IF_DEBUG( FILE_CACHE ) { KdPrint(( "DereferenceCachedFile: entry %p, ref %ld\n", pFileCacheEntry, result )); } if (result == 0) { UL_CALL_PASSIVE( &pFileCacheEntry->WorkItem, &UlpDestroyFileCacheEntry ); } } // DereferenceCachedFile /***************************************************************************++ Routine Description: Creates a new file entry for the specified file. Arguments: pFileName - Supplies the name of the file to open. FileHandle - the optional file handle. ONLY 1 can be provided. name OR handle. pFileCacheEntry - Receives the newly created file cache entry if successful. Return Value: NTSTATUS - Completion status. --***************************************************************************/ NTSTATUS UlCreateFileEntry( IN PUNICODE_STRING pFileName OPTIONAL, IN HANDLE FileHandle OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PUL_FILE_CACHE_ENTRY *pFileCacheEntry ) { NTSTATUS status; PUL_FILE_CACHE_ENTRY pNewEntry; HANDLE fileHandle; PFILE_OBJECT pFileObject; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; PFAST_IO_DISPATCH pFastIoDispatch; BOOLEAN AttachedToSysProc; // // Sanity check. // PAGED_CODE(); // // Setup locals so we know how to cleanup on exit. // pNewEntry = NULL; fileHandle = NULL; pFileObject = NULL; AttachedToSysProc = FALSE; status = STATUS_SUCCESS; // // Only 1 can be passed in // if (pFileName != NULL && FileHandle != NULL) { status = STATUS_INVALID_PARAMETER; goto end; } IF_DEBUG( FILE_CACHE ) { if (pFileName != NULL) { KdPrint(( "UlCreateFileEntry: file %wZ\n", pFileName )); } else { KdPrint(( "UlCreateFileEntry: handle %p\n", (PVOID)FileHandle )); } } // // Allocate the entry. // pNewEntry = UL_ALLOCATE_STRUCT_WITH_SPACE( NonPagedPool, UL_FILE_CACHE_ENTRY, (pFileName == NULL) ? 0 : pFileName->MaximumLength, UL_FILE_CACHE_ENTRY_POOL_TAG ); if (pNewEntry == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto end; } RtlZeroMemory( pNewEntry, sizeof(*pNewEntry) ); pNewEntry->Signature = UL_FILE_CACHE_ENTRY_SIGNATURE; if (pFileName != NULL) { // // Open the file. // InitializeObjectAttributes( &objectAttributes, // ObjectAttributes pFileName, // ObjectName OBJ_CASE_INSENSITIVE | // Attributes UL_KERNEL_HANDLE, NULL, // RootDirectory NULL // SecurityDescriptor ); UlAttachToSystemProcess(); AttachedToSysProc = TRUE; status = IoCreateFile( &fileHandle, // FileHandle FILE_GENERIC_READ, // DesiredAccess &objectAttributes, // ObjectAttributes &ioStatusBlock, // IoStatusBlock NULL, // AllocationSize 0, // FileAttributes FILE_SHARE_READ | // ShareAccess FILE_SHARE_WRITE, FILE_OPEN, // CreateDisposition 0, // CreateOptions NULL, // EaBuffer 0, // EaLength CreateFileTypeNone, // CreateFileType NULL, // ExtraCreateParameters IO_NO_PARAMETER_CHECKING // Options ); if (NT_SUCCESS(status) == FALSE) goto end; AccessMode = KernelMode; } else { // // use the passed in handle // fileHandle = FileHandle; } // // Get a referenced pointer to the file object. // status = ObReferenceObjectByHandle( fileHandle, // Handle 0, // DesiredAccess *IoFileObjectType, // ObjectType AccessMode, // AccessMode (void**)&pFileObject, // Object NULL // HandleInformation ); if (NT_SUCCESS(status) == FALSE) goto end; // // Get the file size, etc from the file. Note that, since we *may* // be running in the context of a user-mode thread, we need to // use the Zw form of the API rather than the Nt form. // status = ZwQueryInformationFile( fileHandle, // FileHandle &ioStatusBlock, // IoStatusBlock, &pNewEntry->FileInfo, // FileInformation, sizeof(pNewEntry->FileInfo), // Length FileStandardInformation // FileInformationClass ); if (NT_SUCCESS(status) == FALSE) goto end; if (AttachedToSysProc) { // // detach from the sys process // UlDetachFromSystemProcess(); AttachedToSysProc = FALSE; } // // Snag the device object from the file object, then fill in the // fast I/O routines. The code here was shamelessly stolen from // the NT SMB server. // pNewEntry->pDeviceObject = IoGetRelatedDeviceObject( pFileObject ); pFastIoDispatch = pNewEntry->pDeviceObject->DriverObject->FastIoDispatch; if (pFastIoDispatch != NULL) { // // Fill in Mdl calls. If the file system's vector is large // enough, we still need to check if one of the routines is // specified. If one is specified, they all must be. // if ((pFastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlReadComplete)) && (pFastIoDispatch->MdlRead != NULL)) { pNewEntry->pMdlRead = pFastIoDispatch->MdlRead; pNewEntry->pMdlReadComplete = pFastIoDispatch->MdlReadComplete; } else if (IoGetBaseFileSystemDeviceObject( pFileObject ) == pNewEntry->pDeviceObject) { // // Otherwise default to the original FsRtl routines if we // are right atop a filesystem. // pNewEntry->pMdlRead = &FsRtlMdlReadDev; pNewEntry->pMdlReadComplete = &FsRtlMdlReadCompleteDev; } else { // // Otherwise, make them fail. // pNewEntry->pMdlRead = &UlpFailMdlReadDev; pNewEntry->pMdlReadComplete = &UlpFailMdlReadCompleteDev; } } else { // // No fast dispatch, so make the fast routines fail. // pNewEntry->pMdlRead = &UlpFailMdlReadDev; pNewEntry->pMdlReadComplete = &UlpFailMdlReadCompleteDev; } // // Initialize the new entry. // pNewEntry->ReferenceCount = 1; if (pFileName != NULL) { pNewEntry->FileName.Length = pFileName->Length; pNewEntry->FileName.MaximumLength = pFileName->MaximumLength; pNewEntry->FileName.Buffer = (PWSTR)( pNewEntry + 1 ); RtlCopyMemory( pNewEntry->FileName.Buffer, pFileName->Buffer, pNewEntry->FileName.MaximumLength ); // // only set the handle if it's one we opened, destroy will close it // pNewEntry->FileHandle = fileHandle; } pNewEntry->pFileObject = pFileObject; // // Success! // IF_DEBUG( FILE_CACHE ) { KdPrint(( "UlCreateFileEntry: entry %p, file %wZ, handle %lx [%p]\n", pNewEntry, pFileName, fileHandle, pFileObject )); } *pFileCacheEntry = pNewEntry; end: if (NT_SUCCESS(status) == FALSE) { // // If we made it to this point, then the open has failed. // IF_DEBUG( FILE_CACHE ) { if (pFileName != NULL) { KdPrint(( "UlCreateFileEntry: file %wZ, failure %08lx\n", pFileName, status )); } else { KdPrint(( "UlCreateFileEntry: handle %p, failure %08lx\n", FileHandle, status )); } } if (pNewEntry != NULL) { UlpDestroyFileCacheEntry(&pNewEntry->WorkItem); pNewEntry = NULL; } } RETURN(status); } // UlCreateFileEntry /***************************************************************************++ Routine Description: Reads data from a file. Does a MDL read for filesystems that support MDL reads. If the fs doesn't support MDL reads, this function allocates a buffer to hold the data. Arguments: pFileBuffer - Contains all the info about the read, and the data once that's been read. pIrp - This IRP is used to issue the read. --***************************************************************************/ NTSTATUS UlReadFileEntry( IN OUT PUL_FILE_BUFFER pFileBuffer, IN PIRP pIrp ) { NTSTATUS Status; PIO_STACK_LOCATION pIrpSp; PUL_FILE_CACHE_ENTRY pFile; // // Sanity check. // PAGED_CODE(); ASSERT(pFileBuffer); ASSERT(IS_FILE_BUFFER_IN_USE(pFileBuffer)); ASSERT(IS_VALID_FILE_CACHE_ENTRY(pFileBuffer->pFileCacheEntry)); ASSERT(IS_VALID_IRP(pIrp)); pFile = pFileBuffer->pFileCacheEntry; if (pFile->pFileObject->Flags & FO_CACHE_SUPPORTED) { UlTrace(FILE_CACHE, ( "http!UlReadFileEntry(Buffer = %p, pFile = %p, pIrp = %p) MDL Read\n", pFileBuffer, pFile, pIrp )); // // Caching file system. Do a MDL read. // pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->MajorFunction = IRP_MJ_READ; pIrpSp->MinorFunction = IRP_MN_MDL; pIrpSp->FileObject = pFile->pFileObject; pIrpSp->DeviceObject = pFile->pDeviceObject; // // Initialize the IRP. // pIrp->MdlAddress = NULL; pIrp->Tail.Overlay.Thread = UlQueryIrpThread(); // // Indicate to the file system that this operation can be handled // synchronously. Basically, this means that the file system can // use our thread to fault pages in, etc. This avoids // having to context switch to a file system thread. // pIrp->Flags = IRP_SYNCHRONOUS_API; // // Set the number of bytes to read and the offset. // pIrpSp->Parameters.Read.Length = pFileBuffer->Length; pIrpSp->Parameters.Read.ByteOffset = pFileBuffer->FileOffset; ASSERT(pIrpSp->Parameters.Read.Key == 0); // // Set up the completion routine. // IoSetCompletionRoutine( pIrp, // Irp UlpRestartReadFileEntry, // CompletionRoutine pFileBuffer, // Context TRUE, // InvokeOnSuccess TRUE, // InvokeOnError TRUE // InvokeOnCancel ); // // Call the driver. Note that we always set status to // STATUS_PENDING, since we set the IRP completion routine // to *always* be called. // UlCallDriver( pFile->pDeviceObject, pIrp ); Status = STATUS_PENDING; } else { PUCHAR pFileData; PMDL pMdl; UlTrace(FILE_CACHE, ( "http!UlReadFileEntry(Buffer = %p, pFile = %p, pIrp = %p) Normal Read\n", pFileBuffer, pFile, pIrp )); // // Non-caching file system. Allocate a buffer and issue a // normal read. // pFileData = (PUCHAR)UL_ALLOCATE_POOL( NonPagedPool, pFileBuffer->Length, UL_NONCACHED_FILE_DATA_POOL_TAG ); if (!pFileData) { Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } // // Get a MDL for our buffer. // pMdl = IoAllocateMdl( pFileData, pFileBuffer->Length, FALSE, FALSE, NULL ); if (!pMdl) { UL_FREE_POOL( pFileData, UL_NONCACHED_FILE_DATA_POOL_TAG ); Status = STATUS_INSUFFICIENT_RESOURCES; goto end; } MmBuildMdlForNonPagedPool(pMdl); pFileBuffer->pMdl = pMdl; // // Remember where the data is. // pFileBuffer->pFileData = pFileData; // // Set up the read information. // pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->MajorFunction = IRP_MJ_READ; pIrpSp->MinorFunction = IRP_MN_NORMAL; pIrpSp->FileObject = pFile->pFileObject; pIrpSp->DeviceObject = pFile->pDeviceObject; // // Initialize the IRP. // pIrp->MdlAddress = NULL; pIrp->Tail.Overlay.Thread = UlQueryIrpThread(); // // Indicate to the file system that this operation can be handled // synchronously. Basically, this means that the file system can // use the server's thread to fault pages in, etc. This avoids // having to context switch to a file system thread. // pIrp->Flags = IRP_SYNCHRONOUS_API; // // Set the number of bytes to read and the offset. // pIrpSp->Parameters.Read.Length = pFileBuffer->Length; pIrpSp->Parameters.Read.ByteOffset = pFileBuffer->FileOffset; ASSERT(pIrpSp->Parameters.Read.Key == 0); // // If the target device does buffered I/O, load the address of the // caller's buffer as the "system buffered I/O buffer". If the // target device does direct I/O, load the MDL address. If it does // neither, load both the user buffer address and the MDL address. // (This is necessary to support file systems, such as HPFS, that // sometimes treat the I/O as buffered and sometimes treat it as // direct.) // if (pFileBuffer->pFileCacheEntry->pDeviceObject->Flags & DO_BUFFERED_IO) { pIrp->AssociatedIrp.SystemBuffer = pFileData; pIrp->Flags |= IRP_BUFFERED_IO | IRP_INPUT_OPERATION; } else if (pFileBuffer->pFileCacheEntry->pDeviceObject->Flags & DO_DIRECT_IO) { pIrp->MdlAddress = pMdl; } else { pIrp->UserBuffer = pFileData; pIrp->MdlAddress = pMdl; } // // Set up the completion routine. // IoSetCompletionRoutine( pIrp, // Irp UlpRestartReadFileEntry, // CompletionRoutine pFileBuffer, // Context TRUE, // InvokeOnSuccess TRUE, // InvokeOnError TRUE // InvokeOnCancel ); // // Call the driver. Note that we always set status to // STATUS_PENDING, since we set the IRP completion routine // to *always* be called. // UlCallDriver( pFile->pDeviceObject, pIrp ); Status = STATUS_PENDING; } end: return Status; } /***************************************************************************++ Routine Description: Reads data from a file. Does a MDL read for filesystems that support MDL reads and Fast I/O. If the FS doesn't support fast i/o and MDL reads, the function returns with a failure status. Arguments: pFileBuffer - Contains all the info about the read, and the data once that's been read. --***************************************************************************/ NTSTATUS UlReadFileEntryFast( IN OUT PUL_FILE_BUFFER pFileBuffer ) { NTSTATUS Status; IO_STATUS_BLOCK IoStatus; PUL_FILE_CACHE_ENTRY pFile; // // Sanity check. // PAGED_CODE(); ASSERT(pFileBuffer); ASSERT(IS_FILE_BUFFER_IN_USE(pFileBuffer)); ASSERT(IS_VALID_FILE_CACHE_ENTRY(pFileBuffer->pFileCacheEntry)); pFile = pFileBuffer->pFileCacheEntry; if (pFile->pFileObject->Flags & FO_CACHE_SUPPORTED) { UlTrace(FILE_CACHE, ( "http!UlReadFileEntryFast(Buffer = %p, pFile = %p) MDL Read\n", pFileBuffer, pFile )); // // Cached filesystem. Try to use the fast path for the MDL read // complete. // if (pFileBuffer->pFileCacheEntry->pMdlRead( pFileBuffer->pFileCacheEntry->pFileObject, &pFileBuffer->FileOffset, pFileBuffer->Length, 0, &pFileBuffer->pMdl, &IoStatus, pFileBuffer->pFileCacheEntry->pDeviceObject )) { Status = STATUS_SUCCESS; } else { // // It didn't work. The caller must now use the IRP path // by calling UlReadFileEntry. // Status = STATUS_UNSUCCESSFUL; } } else { UlTrace(FILE_CACHE, ( "http!UlReadFileEntryFast(Buffer = %p, pFile = %p) Normal Read\n", pFileBuffer, pFile )); // // Non-caching file system. No fast i/o. The caller should // use the IRP path by calling UlReadFileEntry. // Status = STATUS_UNSUCCESSFUL; } return Status; } /***************************************************************************++ Routine Description: Frees up resources allocated by UlReadFileEntry (or UlReadFileEntryFast). Should be called when the file data read is no longer in use. Arguments: pFileBuffer - Contains all the info about the read, and the data that was read. pIrp - This IRP is used to issue the read completion. --***************************************************************************/ NTSTATUS UlReadCompleteFileEntry( IN PUL_FILE_BUFFER pFileBuffer, IN PIRP pIrp ) { NTSTATUS Status; PIO_STACK_LOCATION pIrpSp; PUL_FILE_CACHE_ENTRY pFile; // // Sanity check. // PAGED_CODE(); ASSERT(pFileBuffer); ASSERT(IS_FILE_BUFFER_IN_USE(pFileBuffer)); ASSERT(IS_VALID_FILE_CACHE_ENTRY(pFileBuffer->pFileCacheEntry)); ASSERT(IS_VALID_IRP(pIrp)); pFile = pFileBuffer->pFileCacheEntry; if (pFile->pFileObject->Flags & FO_CACHE_SUPPORTED) { UlTrace(FILE_CACHE, ( "http!UlReadCompleteFileEntry(Buffer = %p, pFile = %p, pIrp = %p) MDL Read\n", pFileBuffer, pFile, pIrp )); // // Caching file system. Do a MDL read completion. // pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->MajorFunction = IRP_MJ_READ; pIrpSp->MinorFunction = IRP_MN_MDL | IRP_MN_COMPLETE; pIrpSp->FileObject = pFile->pFileObject; pIrpSp->DeviceObject = pFile->pDeviceObject; // // Initialize the IRP. // pIrp->MdlAddress = pFileBuffer->pMdl; pIrp->Tail.Overlay.Thread = UlQueryIrpThread(); // // MDL functions are inherently synchronous. // pIrp->Flags = IRP_SYNCHRONOUS_API; // // Set the number of bytes to read and the offset. // pIrpSp->Parameters.Read.Length = pFileBuffer->Length; pIrpSp->Parameters.Read.ByteOffset = pFileBuffer->FileOffset; ASSERT(pIrpSp->Parameters.Read.Key == 0); // // Set up the completion routine. We don't need to do anything // on the completion, so we'll just have the I/O manager call // our callers routine directly. // IoSetCompletionRoutine( pIrp, // Irp pFileBuffer->pCompletionRoutine, // CompletionRoutine pFileBuffer->pContext, // Context TRUE, // InvokeOnSuccess TRUE, // InvokeOnError TRUE // InvokeOnCancel ); // // Call the driver. Note that we always set status to // STATUS_PENDING, since we set the IRP completion routine // to *always* be called. // UlCallDriver( pFile->pDeviceObject, pIrp ); Status = STATUS_PENDING; } else { UlTrace(FILE_CACHE, ( "http!UlReadCompleteFileEntry(Buffer = %p, pFile = %p) Normal Read\n", pFileBuffer, pFile )); // // Non-caching file system. We allocated this buffer. Just // free it and call the completion routine. // ASSERT(pFileBuffer->pMdl); IoFreeMdl(pFileBuffer->pMdl); pFileBuffer->pMdl = NULL; ASSERT(pFileBuffer->pFileData); UL_FREE_POOL( pFileBuffer->pFileData, UL_NONCACHED_FILE_DATA_POOL_TAG ); pFileBuffer->pFileData = NULL; // // Fake the completion here. // pFileBuffer->pCompletionRoutine( pFileBuffer->pFileCacheEntry->pDeviceObject, pIrp, pFileBuffer->pContext ); // // Return pending, since we called their completion routine. // Status = STATUS_PENDING; } if (!NT_SUCCESS(Status)) { UlTrace(FILE_CACHE, ( "http!UlReadCompleteFileEntry(Buffer = %p, pFile = %p) FAILED! %x\n", pFileBuffer, pFile, Status )); } return Status; } /***************************************************************************++ Routine Description: Frees up resources allocated by UlReadFileEntry (or UlReadFileEntryFast). Should be called when the file data read is no longer in use. Arguments: pFileBuffer - Contains all the info about the read, and the data that was read. --***************************************************************************/ NTSTATUS UlReadCompleteFileEntryFast( IN PUL_FILE_BUFFER pFileBuffer ) { NTSTATUS Status; PUL_FILE_CACHE_ENTRY pFile; // // Sanity check. // PAGED_CODE(); ASSERT(pFileBuffer); ASSERT(IS_FILE_BUFFER_IN_USE(pFileBuffer)); ASSERT(IS_VALID_FILE_CACHE_ENTRY(pFileBuffer->pFileCacheEntry)); pFile = pFileBuffer->pFileCacheEntry; if (pFile->pFileObject->Flags & FO_CACHE_SUPPORTED) { UlTrace(FILE_CACHE, ( "http!UlReadCompleteFileEntryFast(Buffer = %p, pFile = %p) MDL Read\n", pFileBuffer, pFile )); // // Cached filesystem. Try to use the fast path for the MDL read // complete. // if (pFileBuffer->pFileCacheEntry->pMdlReadComplete( pFileBuffer->pFileCacheEntry->pFileObject, pFileBuffer->pMdl, pFileBuffer->pFileCacheEntry->pDeviceObject )) { pFileBuffer->pMdl = NULL; Status = STATUS_SUCCESS; } else { // // It didn't work. The caller must now use the IRP path // by calling UlReadCompleteFileEntry. // Status = STATUS_UNSUCCESSFUL; } } else { UlTrace(FILE_CACHE, ( "http!UlReadCompleteFileEntryFast(Buffer = %p, pFile = %p) Normal Read\n", pFileBuffer, pFile )); // // Non-caching file system. We allocated this buffer. Just // free it. // ASSERT(pFileBuffer->pMdl); IoFreeMdl(pFileBuffer->pMdl); pFileBuffer->pMdl = NULL; ASSERT(pFileBuffer->pFileData); UL_FREE_POOL( pFileBuffer->pFileData, UL_NONCACHED_FILE_DATA_POOL_TAG ); Status = STATUS_SUCCESS; } return Status; } // // Private functions. // /***************************************************************************++ Routine Description: Helper function to destroy a file cache entry. Arguments: pWorkItem - Supplies a pointer to the work item queued. This should point to the WORK_ITEM structure embedded in a UL_FILE_CACHE_ENTRY. --***************************************************************************/ VOID UlpDestroyFileCacheEntry( IN PUL_WORK_ITEM pWorkItem ) { PUL_FILE_CACHE_ENTRY pFileCacheEntry; // // Sanity check. // PAGED_CODE(); pFileCacheEntry = CONTAINING_RECORD( pWorkItem, UL_FILE_CACHE_ENTRY, WorkItem ); IF_DEBUG( FILE_CACHE ) { KdPrint(( "UlpDestroyFileCacheEntry: entry %p\n", pFileCacheEntry )); } ASSERT( IS_VALID_FILE_CACHE_ENTRY( pFileCacheEntry ) ); // // Cleanup the file system stuff. // if (pFileCacheEntry->pFileObject != NULL) { ObDereferenceObject( pFileCacheEntry->pFileObject ); } if (pFileCacheEntry->FileHandle != NULL) { UlCloseSystemHandle( pFileCacheEntry->FileHandle ); } // // Now release the entry's resources. // pFileCacheEntry->Signature = UL_FILE_CACHE_ENTRY_SIGNATURE_X; UL_FREE_POOL( pFileCacheEntry, UL_FILE_CACHE_ENTRY_POOL_TAG ); } // UlpDestroyFileCacheEntry /***************************************************************************++ Routine Description: Dummy function to fail MDL reads. Arguments: Same as FsRtlMdlReadDev(). Return Value: BOOLEAN - Always FALSE (failure). --***************************************************************************/ BOOLEAN UlpFailMdlReadDev( IN struct _FILE_OBJECT *FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN ULONG LockKey, OUT PMDL *MdlChain, OUT PIO_STATUS_BLOCK IoStatus, IN struct _DEVICE_OBJECT *DeviceObject ) { PAGED_CODE(); return FALSE; } // UlpFailMdlReadDev /***************************************************************************++ Routine Description: Dummy function to fail MDL read completes. Arguments: Same as FsRtlMdlReadCompleteDev(). Return Value: BOOLEAN - Always FALSE (failure). --***************************************************************************/ BOOLEAN UlpFailMdlReadCompleteDev( IN struct _FILE_OBJECT *FileObject, IN PMDL MdlChain, IN struct _DEVICE_OBJECT *DeviceObject ) { PAGED_CODE(); return FALSE; } // UlpFailMdlReadCompleteDev /***************************************************************************++ Routine Description: Completion routine for UlReadFileEntry. Sets the data fields in the UL_FILE_BUFFER and calls the completion routine passed to UlReadFileEntry. Arguments: pDeviceObject - the file system device object (not used) pIrp - the IRP used to do the read pContext - pointer to the UL_FILE_BUFFER --***************************************************************************/ NTSTATUS UlpRestartReadFileEntry( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID pContext ) { NTSTATUS Status; PUL_FILE_BUFFER pFileBuffer = (PUL_FILE_BUFFER)pContext; PUL_FILE_CACHE_ENTRY pFile; // // Sanity check. // ASSERT(pFileBuffer); ASSERT(IS_FILE_BUFFER_IN_USE(pFileBuffer)); ASSERT(IS_VALID_FILE_CACHE_ENTRY(pFileBuffer->pFileCacheEntry)); pFile = pFileBuffer->pFileCacheEntry; if (pFile->pFileObject->Flags & FO_CACHE_SUPPORTED) { // // This was a MDL read. // if (NT_SUCCESS(pIrp->IoStatus.Status)) { pFileBuffer->pMdl = pIrp->MdlAddress; } } else { // // This was a Normal Read. pFileBuffer->pMdl // was already set by UlReadFileEntry. // ASSERT(pFileBuffer->pMdl); } if (pFileBuffer->pCompletionRoutine) { Status = (pFileBuffer->pCompletionRoutine)( pDeviceObject, pIrp, pFileBuffer->pContext ); } else { Status = STATUS_MORE_PROCESSING_REQUIRED; } return Status; }