You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
707 lines
18 KiB
707 lines
18 KiB
/*++
|
|
|
|
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;
|
|
|
|
}
|