|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
fscontrl.c
Abstract:
This module implements the forwarding of all broadcast requests to UNC providers.
Author:
Manny Weiser (mannyw) 6-Jan-1992
Revision History:
--*/
#include "mup.h"
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_FORWARD)
//
// local procedure prototypes
//
NTSTATUS BuildAndSubmitIrp ( IN PIRP OriginalIrp, IN PCCB Ccb, IN PMASTER_FORWARDED_IO_CONTEXT MasterContext );
NTSTATUS ForwardedIoCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
VOID DeferredForwardedIoCompletionRoutine( PVOID Context);
NTSTATUS CommonForwardedIoCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, BuildAndSubmitIrp )
#pragma alloc_text( PAGE, MupForwardIoRequest )
#endif
NTSTATUS MupForwardIoRequest ( IN PMUP_DEVICE_OBJECT MupDeviceObject, IN PIRP Irp )
/*++
Routine Description:
This routine forwards an I/O Request Packet to all redirectors for a broadcast request.
Arguments:
MupDeviceObject - Supplies the device object to use.
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The status for the IRP
--*/
{ NTSTATUS status; PIO_STACK_LOCATION irpSp; PFCB fcb; PVOID fscontext2; PLIST_ENTRY listEntry; PCCB ccb; PMASTER_FORWARDED_IO_CONTEXT masterContext; BOOLEAN ownLock = FALSE;
MupDeviceObject;
PAGED_CODE(); DebugTrace(+1, Dbg, "MupForwardIrp\n", 0);
if (MupEnableDfs && MupDeviceObject->DeviceObject.DeviceType == FILE_DEVICE_DFS) { status = DfsVolumePassThrough((PDEVICE_OBJECT)MupDeviceObject, Irp); return( status ); }
irpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Find the FCB for this file object
//
FsRtlEnterFileSystem();
MupDecodeFileObject( irpSp->FileObject, (PVOID *)&fcb, &fscontext2 );
if ( fcb == NULL || BlockType( fcb ) != BlockTypeFcb ) {
//
// This is not an FCB.
//
DebugTrace(0, Dbg, "The fail is closing\n", 0);
FsRtlExitFileSystem();
MupCompleteRequest( Irp, STATUS_INVALID_DEVICE_REQUEST ); status = STATUS_INVALID_DEVICE_REQUEST;
DebugTrace(-1, Dbg, "MupForwardRequest -> %08lx\n", status ); return status; }
//
// Allocate a context structure
//
masterContext = MupAllocateMasterIoContext();
if (masterContext == NULL) {
//
// We ran out of resources. Clean up and return the error.
//
DebugTrace(0, Dbg, "Couldn't allc masterContect\n", 0);
FsRtlExitFileSystem();
MupCompleteRequest( Irp, STATUS_INSUFFICIENT_RESOURCES ); status = STATUS_INSUFFICIENT_RESOURCES;
DebugTrace(-1, Dbg, "MupForwardRequest -> %08lx\n", status ); return status; }
DebugTrace( 0, Dbg, "Allocated MasterContext 0x%08lx\n", masterContext );
IoMarkIrpPending(Irp);
//
// At this point, we're committed to returning STATUS_PENDING
//
masterContext->OriginalIrp = Irp;
//
// set status for MupDereferenceMasterIoContext. If this is still
// an error when the context is freed then masterContext->ErrorStatus
// will be used to complete the request.
//
masterContext->SuccessStatus = STATUS_UNSUCCESSFUL; masterContext->ErrorStatus = STATUS_BAD_NETWORK_PATH;
//
// Copy the referenced pointer to the FCB.
//
masterContext->Fcb = fcb;
try {
//
// Submit the forwarded IRPs. Note that we can not hold the lock
// across calls to BuildAndSubmitIrp as it calls IoCallDriver().
//
ACQUIRE_LOCK( &MupCcbListLock ); ownLock = TRUE;
listEntry = fcb->CcbList.Flink;
while ( listEntry != &fcb->CcbList ) {
RELEASE_LOCK( &MupCcbListLock ); ownLock = FALSE;
ccb = CONTAINING_RECORD( listEntry, CCB, ListEntry );
MupAcquireGlobalLock(); MupReferenceBlock( ccb ); MupReleaseGlobalLock();
BuildAndSubmitIrp( Irp, ccb, masterContext );
ACQUIRE_LOCK( &MupCcbListLock ); ownLock = TRUE;
listEntry = listEntry->Flink;
}
RELEASE_LOCK( &MupCcbListLock ); ownLock = FALSE;
} except ( EXCEPTION_EXECUTE_HANDLER ) {
masterContext->ErrorStatus = GetExceptionCode();
}
//
// If BuildAndSubmitIrp threw an exception, the lock might still be
// held. Drop it if so.
//
if (ownLock == TRUE) {
RELEASE_LOCK( &MupCcbListLock );
}
//
// Release our reference to the master IO context block.
//
MupDereferenceMasterIoContext( masterContext, NULL );
//
// Return to the caller.
//
FsRtlExitFileSystem();
DebugTrace(-1, Dbg, "MupForwardIrp -> %08lx\n", status); return STATUS_PENDING; }
NTSTATUS BuildAndSubmitIrp ( IN PIRP OriginalIrp, IN PCCB Ccb, IN PMASTER_FORWARDED_IO_CONTEXT MasterContext )
/*++
Routine Description:
This routine takes the original IRP and forwards it to the the UNC provider described by the CCB.
Arguments:
OriginalIrp - Supplies the Irp being processed
Ccb - A pointer the the ccb.
MasterContext - A pointer to the master context block for this forwarded request.
Return Value:
NTSTATUS - The status for the Irp
--*/
{ PIRP irp = NULL; PIO_STACK_LOCATION irpSp; PFORWARDED_IO_CONTEXT forwardedIoContext = NULL; NTSTATUS status = STATUS_SUCCESS; PDEVICE_OBJECT deviceObject; ULONG bufferLength; KPROCESSOR_MODE requestorMode; PMDL mdl = NULL;
PAGED_CODE(); DebugTrace(+1, Dbg, "BuildAndSubmitIrp\n", 0);
try { // make this NonPagedPool, since we could free this up in the
// io completion routine.
forwardedIoContext = ExAllocatePoolWithTag( NonPagedPool, sizeof( FORWARDED_IO_CONTEXT ), ' puM');
if (forwardedIoContext == NULL) {
try_return(status = STATUS_INSUFFICIENT_RESOURCES);
}
forwardedIoContext->pIrp = NULL; forwardedIoContext->DeviceObject = NULL;
DebugTrace( 0, Dbg, "Allocated work context 0x%08lx\n", forwardedIoContext );
//
// Get the address of the target device object. Note that this was
// already done for the no intermediate buffering case, but is done
// here again to speed up the turbo write path.
//
deviceObject = IoGetRelatedDeviceObject( Ccb->FileObject );
//
// Allocate and initialize the I/O Request Packet (IRP) for this
// operation. The allocation is performed with an exception handler
// in case the caller does not have enough quota to allocate the
// packet.
//
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
if (irp == NULL) {
//
// An IRP could not be allocated. Return an appropriate
// error status code.
//
try_return(status = STATUS_INSUFFICIENT_RESOURCES); }
irp->Tail.Overlay.OriginalFileObject = Ccb->FileObject; irp->Tail.Overlay.Thread = OriginalIrp->Tail.Overlay.Thread; irp->RequestorMode = KernelMode;
//
// Get a pointer to the stack location for the first driver. This will be
// used to pass the original function codes and parameters.
//
irpSp = IoGetNextIrpStackLocation( irp );
//
// Copy the parameters from the original request.
//
RtlMoveMemory( irpSp, IoGetCurrentIrpStackLocation( OriginalIrp ), sizeof( *irpSp ) );
bufferLength = irpSp->Parameters.Write.Length;
irpSp->FileObject = Ccb->FileObject;
//
// Even though this is probably meaningless to a remote mailslot
// write, pass it though obediently.
//
if (Ccb->FileObject->Flags & FO_WRITE_THROUGH) { irpSp->Flags = SL_WRITE_THROUGH; }
requestorMode = OriginalIrp->RequestorMode;
//
// Now determine whether this device expects to have data buffered
// to it or whether it performs direct I/O. This is based on the
// DO_BUFFERED_IO flag in the device object. If the flag is set,
// then a system buffer is allocated and the caller's data is copied
// into it. Otherwise, a Memory Descriptor List (MDL) is allocated
// and the caller's buffer is locked down using it.
//
if (deviceObject->Flags & DO_BUFFERED_IO) {
//
// The device does not support direct I/O. Allocate a system
// buffer, and copy the caller's data into it. This is done
// using an exception handler that will perform cleanup if the
// operation fails. Note that this is only done if the operation
// has a non-zero length.
//
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
if ( bufferLength != 0 ) {
//
// If the request was made from a mode other than kernel,
// presumably user, probe the entire buffer to determine
// whether or not the caller has write access to it.
//
if (requestorMode != KernelMode) { ProbeForRead( OriginalIrp->UserBuffer, bufferLength, sizeof( UCHAR ) ); }
//
// Allocate the intermediary system buffer from paged
// pool and charge quota for it.
//
irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuotaTag( PagedPoolCacheAligned, bufferLength, ' puM');
if (irp->AssociatedIrp.SystemBuffer == NULL) { try_return(status = STATUS_INSUFFICIENT_RESOURCES); }
RtlMoveMemory( irp->AssociatedIrp.SystemBuffer, OriginalIrp->UserBuffer, bufferLength);
//
// Set the IRP_BUFFERED_IO flag in the IRP so that I/O
// completion will know that this is not a direct I/O
// operation. Also set the IRP_DEALLOCATE_BUFFER flag
// so it will deallocate the buffer.
//
irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
} else {
//
// This is a zero-length write. Simply indicate that this is
// buffered I/O, and pass along the request. The buffer will
// not be set to deallocate so the completion path does not
// have to special-case the length.
//
irp->Flags = IRP_BUFFERED_IO; }
} else if (deviceObject->Flags & DO_DIRECT_IO) {
//
// This is a direct I/O operation. Allocate an MDL and invoke the
// memory management routine to lock the buffer into memory.
// Note that no MDL is allocated, nor is any memory probed or
// locked if the length of the request was zero.
//
if ( bufferLength != 0 ) {
//
// Allocate an MDL, charging quota for it, and hang it
// off of the IRP. Probe and lock the pages associated
// with the caller's buffer for read access and fill in
// the MDL with the PFNs of those pages.
//
mdl = IoAllocateMdl( OriginalIrp->UserBuffer, bufferLength, FALSE, TRUE, irp );
if (mdl == NULL) { try_return(status = STATUS_INSUFFICIENT_RESOURCES); }
MmProbeAndLockPages( mdl, requestorMode, IoReadAccess );
}
} else {
//
// Pass the address of the caller's buffer to the device driver.
// It is now up to the driver to do everything.
//
irp->UserBuffer = OriginalIrp->UserBuffer;
}
//
// If this write operation is to be performed without any caching,
// set the appropriate flag in the IRP so no caching is performed.
//
if (Ccb->FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) { irp->Flags |= IRP_NOCACHE | IRP_WRITE_OPERATION; } else { irp->Flags |= IRP_WRITE_OPERATION; }
//
// Setup the context block
//
forwardedIoContext->Ccb = Ccb; forwardedIoContext->MasterContext = MasterContext;
MupAcquireGlobalLock(); MupReferenceBlock( MasterContext ); MupReleaseGlobalLock();
//
// Set up the completion routine.
//
IoSetCompletionRoutine( irp, (PIO_COMPLETION_ROUTINE)ForwardedIoCompletionRoutine, forwardedIoContext, TRUE, TRUE, TRUE );
//
// Pass the request to the provider.
//
IoCallDriver( Ccb->DeviceObject, irp );
//
// At this point it is up to the completion routine to free things
//
irp = NULL; forwardedIoContext = NULL; mdl = NULL;
try_exit: NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); }
//
// Clean up everything if we are returning an error
//
if (!NT_SUCCESS(status)) { if ( forwardedIoContext != NULL ) ExFreePool( forwardedIoContext ); if ( irp != NULL ) { if (irp->AssociatedIrp.SystemBuffer != NULL) ExFreePool(irp->AssociatedIrp.SystemBuffer); IoFreeIrp( irp ); } if ( mdl != NULL ) IoFreeMdl( mdl ); }
DebugTrace(-1, Dbg, "BuildAndSubmitIrp -> 0x%08lx\n", status);
return status;
}
NTSTATUS ForwardedIoCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
/*++
Routine Description:
This routines cleans up after a forwarded IRP has completed.
Arguments:
DeviceObject - A pointer to the MUP device object.
IRP - A pointer to the IRP being processed.
Context - A pointer to a block containing the context of a forward IRP.
Return Value:
None.
--*/
{ PFORWARDED_IO_CONTEXT ioContext = Context;
NTSTATUS status = Irp->IoStatus.Status;
DebugTrace( +1, Dbg, "ForwardedIoCompletionRoutine\n", 0 ); DebugTrace( 0, Dbg, "Irp = 0x%08lx\n", Irp ); DebugTrace( 0, Dbg, "Context = 0x%08lx\n", Context ); DebugTrace( 0, Dbg, "status = 0x%08lx\n", status );
//
// Give this to a worker thread if we are at too high an Irq level
//
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { ioContext->DeviceObject = DeviceObject; ioContext->pIrp = Irp; ExInitializeWorkItem( &ioContext->WorkQueueItem, DeferredForwardedIoCompletionRoutine, Context); ExQueueWorkItem(&ioContext->WorkQueueItem, CriticalWorkQueue); } else { CommonForwardedIoCompletionRoutine( DeviceObject, Irp, Context); }
//
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
// will stop working on the IRP.
//
DebugTrace( -1, Dbg, "ForwardedIoCompletionRoutine exit\n", 0 );
return STATUS_MORE_PROCESSING_REQUIRED; }
VOID DeferredForwardedIoCompletionRoutine( PVOID Context) { PFORWARDED_IO_CONTEXT ioContext = Context;
CommonForwardedIoCompletionRoutine( ioContext->DeviceObject, ioContext->pIrp, Context); }
NTSTATUS CommonForwardedIoCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) {
PFORWARDED_IO_CONTEXT ioContext = Context;
NTSTATUS status = Irp->IoStatus.Status;
DeviceObject;
//
// Free the Irp, and any additional structures we may have allocated.
//
if ( Irp->MdlAddress ) { MmUnlockPages( Irp->MdlAddress ); IoFreeMdl( Irp->MdlAddress ); }
if ( Irp->Flags & IRP_DEALLOCATE_BUFFER ) { ExFreePool( Irp->AssociatedIrp.SystemBuffer ); }
IoFreeIrp( Irp );
//
// Release the our referenced blocks.
//
MupDereferenceCcb( ioContext->Ccb ); MupDereferenceMasterIoContext( ioContext->MasterContext, &status );
//
// Free the slave forwarded IO context block
//
ExFreePool( ioContext );
return STATUS_MORE_PROCESSING_REQUIRED;
}
|