708 lines
17 KiB
C
708 lines
17 KiB
C
/*++
|
||
|
||
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;
|
||
|
||
}
|