/*++ Copyright (c) 1989 Microsoft Corporation Module Name: RxContx.c Abstract: This module implements routine to allocate/initialize and to delete an Irp Context. These structures are very important because they link Irps with the RDBSS. They encapsulate all the context required to process an IRP. Author: Joe Linn [JoeLinn] 21-aug-1994 Revision History: Balan Sethu Raman [SethuR] 07-June-1995 Included support for cancelling requests --*/ #include "precomp.h" #pragma hdrstop #include #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, RxInitializeContext) #pragma alloc_text(PAGE, RxReinitializeContext) #pragma alloc_text(PAGE, RxPrepareContextForReuse) #pragma alloc_text(PAGE, RxCompleteRequest) #pragma alloc_text(PAGE, __RxSynchronizeBlockingOperations) #pragma alloc_text(PAGE, RxResumeBlockedOperations_Serially) #pragma alloc_text(PAGE, RxResumeBlockedOperations_ALL) #pragma alloc_text(PAGE, RxCancelBlockingOperation) #pragma alloc_text(PAGE, RxRemoveOperationFromBlockingQueue) #endif BOOLEAN RxSmallContextLogEntry = FALSE; FAST_MUTEX RxContextPerFileSerializationMutex; // // The debug trace level // #define Dbg (DEBUG_TRACE_RXCONTX) ULONG RxContextSerialNumberCounter = 0; #ifdef RDBSSLOG // // this stuff must be in nonpaged memory // //// 1 2 3 4 5 6 7 8 9 char RxInitContext_SurrogateFormat[] = "%S%S%N%N%N%N%N%N%N"; //// 2 3 4 5 6 7 8 9 char RxInitContext_ActualFormat[] = "Irp++ %s/%lx %08lx irp %lx thrd %lx %lx:%lx #%lx"; #endif // ifdef RDBSSLOG VOID ValidateBlockingIoQ ( PLIST_ENTRY BlockingIoQ ); VOID RxInitializeContext ( IN PIRP Irp, IN PRDBSS_DEVICE_OBJECT RxDeviceObject, IN ULONG InitialContextFlags, IN OUT PRX_CONTEXT RxContext ) { PDEVICE_OBJECT TopLevelDeviceObject; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxInitializeContext\n")); // // some asserts that we need. This ensures that the two values that are // packaged together as an IoStatusBlock can be manipulated independently // as well as together. // ASSERT( FIELD_OFFSET( RX_CONTEXT, StoredStatus ) == FIELD_OFFSET( RX_CONTEXT, IoStatusBlock.Status ) ); ASSERT( FIELD_OFFSET( RX_CONTEXT, InformationToReturn ) == FIELD_OFFSET( RX_CONTEXT, IoStatusBlock.Information ) ); // // Set the proper node type code, node byte size and the flags // RxContext->NodeTypeCode = RDBSS_NTC_RX_CONTEXT; RxContext->NodeByteSize = sizeof( RX_CONTEXT ); RxContext->ReferenceCount = 1; RxContext->SerialNumber = InterlockedIncrement( &RxContextSerialNumberCounter ); RxContext->RxDeviceObject = RxDeviceObject; // // Initialize the Sync Event. // KeInitializeEvent( &RxContext->SyncEvent, SynchronizationEvent, FALSE ); // // Initialize the associated scavenger entry // RxInitializeScavengerEntry( &RxContext->ScavengerEntry ); // // Initialize the list entry of blocked operations // InitializeListHead( &RxContext->BlockedOperations ); RxContext->MRxCancelRoutine = NULL; RxContext->ResumeRoutine = NULL; SetFlag( RxContext->Flags, InitialContextFlags ); // // Set the Irp fields....for cacheing and hiding // RxContext->CurrentIrp = Irp; RxContext->OriginalThread = RxContext->LastExecutionThread = PsGetCurrentThread(); if (Irp != NULL) { PIO_STACK_LOCATION IrpSp; IrpSp = IoGetCurrentIrpStackLocation( Irp ); // ok4ioget // // There are certain operations that are open ended in the redirector. // The change notification mechanism is one of them. On a synchronous // operation if the wait is in the redirector then we will not be able // to cancel because FsRtlEnterFileSystem disables APC's. Therefore // we convert the synchronous operation into an asynchronous one and // let the I/O system do the waiting. // if (IrpSp->FileObject != NULL) { if (!IoIsOperationSynchronous( Irp )) { SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); } else { PFCB Fcb; Fcb = (PFCB)IrpSp->FileObject->FsContext; if ((Fcb != NULL) && NodeTypeIsFcb( Fcb )) { if (((IrpSp->MajorFunction == IRP_MJ_READ) || (IrpSp->MajorFunction == IRP_MJ_WRITE) || (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL)) && (Fcb->NetRoot != NULL) && (Fcb->NetRoot->Type == NET_ROOT_PIPE)) { SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); } } } } if ((IrpSp->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) && (IrpSp->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)) { SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); } // // JOYC: make all device io control async // if (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) { SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); } // // Set the recursive file system call parameter. We set it true if // the TopLevelIrp field in the thread local storage is not the current // irp, otherwise we leave it as FALSE. // if (!RxIsThisTheTopLevelIrp( Irp )) { SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_RECURSIVE_CALL ); } if (RxGetTopDeviceObjectIfRdbssIrp() == RxDeviceObject) { SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_THIS_DEVICE_TOP_LEVEL ); } // // Major/Minor Function codes // RxContext->MajorFunction = IrpSp->MajorFunction; RxContext->MinorFunction = IrpSp->MinorFunction; ASSERT( RxContext->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION ); RxContext->CurrentIrpSp = IrpSp; if (IrpSp->FileObject) { PFOBX Fobx; PFCB Fcb; Fcb = (PFCB)IrpSp->FileObject->FsContext; Fobx = (PFOBX)IrpSp->FileObject->FsContext2; RxContext->pFcb = (PMRX_FCB)Fcb; if (Fcb && NodeTypeIsFcb( Fcb )) { RxContext->NonPagedFcb = Fcb->NonPaged; } if (Fobx && (Fobx != (PFOBX)UIntToPtr( DFS_OPEN_CONTEXT )) && (Fobx != (PFOBX)UIntToPtr( DFS_DOWNLEVEL_OPEN_CONTEXT ))) { RxContext->pFobx = (PMRX_FOBX)Fobx; RxContext->pRelevantSrvOpen = Fobx->pSrvOpen; if (NodeType( Fobx ) == RDBSS_NTC_FOBX) { RxContext->FobxSerialNumber = InterlockedIncrement( &Fobx->FobxSerialNumber ); } } else { RxContext->pFobx = NULL; } // // Copy IRP specific parameters. // if ((RxContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) && (RxContext->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)) { if (Fobx != NULL) { if (NodeType( Fobx ) == RDBSS_NTC_FOBX) { RxContext->NotifyChangeDirectory.pVNetRoot = (PMRX_V_NET_ROOT)Fcb->VNetRoot; } else if (NodeType( Fobx ) == RDBSS_NTC_V_NETROOT) { RxContext->NotifyChangeDirectory.pVNetRoot = (PMRX_V_NET_ROOT)Fobx; } } } // // Copy RealDevice for workque algorithms, // RxContext->RealDevice = IrpSp->FileObject->DeviceObject; if (FlagOn( IrpSp->FileObject->Flags,FO_WRITE_THROUGH )) { SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_WRITE_THROUGH ); } } } else { RxContext->CurrentIrpSp = NULL; // // Major/Minor Function codes // RxContext->MajorFunction = IRP_MJ_MAXIMUM_FUNCTION + 1; RxContext->MinorFunction = 0; } if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL) { PETHREAD Thread = PsGetCurrentThread(); UCHAR Pad = 0; RxLog(( RxInitContext_SurrogateFormat, RxInitContext_ActualFormat, RXCONTX_OPERATION_NAME( RxContext->MajorFunction, BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_WAIT )), RxContext->MinorFunction, RxContext, Irp, Thread, RxContext->pFcb, RxContext->pFobx, RxContext->SerialNumber )); RxWmiLog( LOG, RxInitializeContext, LOGPTR( RxContext ) LOGPTR( Irp ) LOGPTR( Thread ) LOGPTR( RxContext->pFcb ) LOGPTR( RxContext->pFobx ) LOGULONG( RxContext->SerialNumber ) LOGUCHAR( RxContext->MinorFunction ) LOGARSTR( RXCONTX_OPERATION_NAME( RxContext->MajorFunction, BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_WAIT ) )) ); } RxDbgTrace( -1, Dbg, ("RxInitializeContext -> %08lx %08lx %08lx\n", RxContext, RxContext->pFcb, RxContext->pFobx )); } PRX_CONTEXT RxCreateRxContext ( IN PIRP Irp, IN PRDBSS_DEVICE_OBJECT RxDeviceObject, IN ULONG InitialContextFlags ) /*++ Routine Description: This routine creates a new RX_CONTEXT record Arguments: Irp - Supplies the originating Irp. RxDeviceObject - the deviceobject that applies InitialContextFlags - Supplies the wait value to store in the context; also, the must_succeed value Return Value: PRX_CONTEXT - returns a pointer to the newly allocate RX_CONTEXT Record --*/ { KIRQL SavedIrql; PRX_CONTEXT RxContext = NULL; ULONG RxContextFlags = 0; UCHAR MustSucceedDescriptorNumber = 0; #if DBG InterlockedIncrement( &RxFsdEntryCount ); #endif ASSERT( RxDeviceObject != NULL ); InterlockedIncrement( &RxDeviceObject->NumberOfActiveContexts ); if (RxContext == NULL) { RxContext = ExAllocateFromNPagedLookasideList( &RxContextLookasideList ); if (RxContext != NULL) { SetFlag( RxContextFlags, RX_CONTEXT_FLAG_FROM_POOL ); } } if (RxContext == NULL) { return NULL; } RtlZeroMemory( RxContext, sizeof( RX_CONTEXT ) ); RxContext->Flags = RxContextFlags; RxContext->MustSucceedDescriptorNumber = MustSucceedDescriptorNumber; RxInitializeContext( Irp, RxDeviceObject, InitialContextFlags, RxContext ); ASSERT( (RxContext->MajorFunction!=IRP_MJ_CREATE) || !FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED ) ); KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql ); InsertTailList(&RxActiveContexts,&RxContext->ContextListEntry); KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql ); return RxContext; } VOID RxReinitializeContext( IN OUT PRX_CONTEXT RxContext ) { PIRP Irp = RxContext->CurrentIrp; PRDBSS_DEVICE_OBJECT RxDeviceObject = RxContext->RxDeviceObject; ULONG PreservedContextFlags = FlagOn( RxContext->Flags, RX_CONTEXT_PRESERVED_FLAGS ); ULONG InitialContextFlags = FlagOn( RxContext->Flags, RX_CONTEXT_INITIALIZATION_FLAGS ); PAGED_CODE(); RxPrepareContextForReuse( RxContext ); RtlZeroMemory( (PCHAR)(&RxContext->ContextListEntry + 1), sizeof( RX_CONTEXT ) - FIELD_OFFSET( RX_CONTEXT, MajorFunction ) ); RxContext->Flags = PreservedContextFlags; RxInitializeContext( Irp, RxDeviceObject, InitialContextFlags, RxContext ); } VOID RxPrepareContextForReuse ( IN OUT PRX_CONTEXT RxContext ) /*++ Routine Description: This routine prepares a context for reuse by resetting all operation specific allocations/acquistions that have been made. The parameters that have been obtained from the IRP are not modified. Arguments: RxContext - Supplies the RX_CONTEXT to remove Return Value: None --*/ { PAGED_CODE(); // // Clean up the operation specific stuff // switch (RxContext->MajorFunction) { case IRP_MJ_CREATE: ASSERT( RxContext->Create.CanonicalNameBuffer == NULL ); break; case IRP_MJ_READ : case IRP_MJ_WRITE : ASSERT( RxContext->RxContextSerializationQLinks.Flink == NULL ); ASSERT( RxContext->RxContextSerializationQLinks.Blink == NULL ); break; default: NOTHING; } RxContext->ReferenceCount = 0; } VOID RxDereferenceAndDeleteRxContext_Real ( IN PRX_CONTEXT RxContext ) /*++ Routine Description: This routine dereferences an RxContexts and if the refcount goes to zero then it deallocates and removes the specified RX_CONTEXT record from the Rx in-memory data structures. IT is called by routines other than RxCompleteRequest async requests touch the RxContext "last" in either the initiating thread or in some other thread. Thus, we refcount the structure and finalize on the last dereference. Arguments: RxContext - Supplies the RX_CONTEXT to remove Return Value: None --*/ { KIRQL SavedIrql; BOOLEAN RxContextIsFromPool; PRDBSS_DEVICE_OBJECT RxDeviceObject; PRX_CONTEXT StopContext = NULL; LONG FinalRefCount; RxDbgTraceLV( +1, Dbg, 1500, ("RxDereferenceAndDeleteRxContext, RxContext = %08lx (%lu)\n", RxContext,RxContext->SerialNumber) ); KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql ); ASSERT( RxContext->NodeTypeCode == RDBSS_NTC_RX_CONTEXT ); FinalRefCount = InterlockedDecrement( &RxContext->ReferenceCount ); if (FinalRefCount == 0) { RxDeviceObject = RxContext->RxDeviceObject; RxContextIsFromPool = BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_FROM_POOL ); if (RxContext == RxDeviceObject->StartStopContext.pStopContext) { RxDeviceObject->StartStopContext.pStopContext = NULL; } else { ASSERT((RxContext->ContextListEntry.Flink->Blink == &RxContext->ContextListEntry) && (RxContext->ContextListEntry.Blink->Flink == &RxContext->ContextListEntry)); RemoveEntryList( &RxContext->ContextListEntry ); if ((InterlockedDecrement( &RxDeviceObject->NumberOfActiveContexts) == 0) && (RxDeviceObject->StartStopContext.pStopContext != NULL)) { StopContext = RxDeviceObject->StartStopContext.pStopContext; } } } KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql ); if (FinalRefCount > 0) { RxDbgTraceLV( -1, Dbg, 1500, ("RxDereferenceAndDeleteRxContext, RxContext not final!!! = %08lx (%lu)\n", RxContext,RxContext->SerialNumber) ); return; } ASSERT( RxContext->ReferenceCount == 0 ); // // Clean up the operation specific stuff // RxPrepareContextForReuse( RxContext ); ASSERT( RxContext->AcquireReleaseFcbTrackerX == 0 ); if (StopContext != NULL) { // // Signal the event. // RxSignalSynchronousWaiter( StopContext ); } #if DBG if (RxContext->ShadowCritOwner) { DbgPrint( "RxDereferenceAndDeleteRxContext:shdowcrit still owned by %x\n", RxContext->ShadowCritOwner ); ASSERT( FALSE ); } #endif if (RxContextIsFromPool) { ExFreeToNPagedLookasideList( &RxContextLookasideList, RxContext ); } RxDbgTraceLV( -1, Dbg, 1500, ("RxDereferenceAndDeleteRxContext -> VOID\n", 0) ); return; } ULONG RxStopOnLoudCompletion = TRUE; NTSTATUS RxCompleteRequest ( IN PRX_CONTEXT RxContext, IN NTSTATUS Status ) /*++ Routine Description: This routine complete the request associated with the RX_CONTEXT Arguments: RxContext - Contains an irp to be completed Status - Status to complete request with Return Value: --*/ { PIRP Irp = RxContext->CurrentIrp; PAGED_CODE(); ASSERT( RxContext ); ASSERT( RxContext->CurrentIrp ); if ((RxContext->LoudCompletionString)) { DbgPrint( "LoudCompletion %08lx/%08lx on %wZ\n", Status, Irp->IoStatus.Information, RxContext->LoudCompletionString ); if ((Status != STATUS_SUCCESS) && RxStopOnLoudCompletion) { DbgPrint( "FAILURE!!!!! %08lx/%08lx on %wZ\n", Status, Irp->IoStatus.Information, RxContext->LoudCompletionString ); // DbgBreakPoint(); } } RxCompleteRequest_Real( RxContext, Irp, Status ); return Status; } #ifdef RDBSSLOG //this stuff must be in nonpaged memory //// 1 2 3 4 5 6 7 8 9 char RxCompleteContext_SurrogateFormat[] = "%S%S%S%N%N%N%N%N%N"; //// 2 3 4 5 6 7 8 9 char RxCompleteContext_ActualFormat[] = "Irp-- %s%s/%lx %lx irp %lx iosb %lx,%lx #%lx"; #endif //ifdef RDBSSLOG VOID RxCompleteRequest_Real ( IN OPTIONAL PRX_CONTEXT RxContext, IN PIRP Irp OPTIONAL, IN NTSTATUS Status ) /*++ Routine Description: This routine completes a Irp Arguments: Irp - Supplies the Irp being processed Status - Supplies the status to complete the Irp with --*/ { // // If we have an Irp then complete the irp. // if (Irp != NULL) { CCHAR PriorityBoost; PIO_STACK_LOCATION IrpSp; IrpSp = IoGetCurrentIrpStackLocation( Irp ); RxSetCancelRoutine( Irp, NULL ); // // For an error, zero out the information field before // completing the request if this was an input operation. // Otherwise IopCompleteRequest will try to copy to the user's buffer. // Also, no boost for an error. // if (NT_ERROR( Status ) && FlagOn( Irp->Flags, IRP_INPUT_OPERATION )) { Irp->IoStatus.Information = 0; PriorityBoost = IO_NO_INCREMENT; } else { PriorityBoost = IO_DISK_INCREMENT; } Irp->IoStatus.Status = Status; RxDbgTrace( 0, (DEBUG_TRACE_DISPATCH), ("RxCompleteRequest_real ---------- Irp(code) = %08lx(%02lx) %08lx %08lx\n", Irp, IoGetCurrentIrpStackLocation( Irp )->MajorFunction, Status, Irp->IoStatus.Information)); if (RxContext != NULL) { ASSERT( RxContext->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION ); if (RxContext->MajorFunction != IRP_MJ_DEVICE_CONTROL) { RxLog(( RxCompleteContext_SurrogateFormat, RxCompleteContext_ActualFormat, (RxContext->OriginalThread == PsGetCurrentThread())?"":"*", RXCONTX_OPERATION_NAME( RxContext->MajorFunction ,TRUE) , RxContext->MinorFunction, RxContext, Irp, Status, Irp->IoStatus.Information, RxContext->SerialNumber )); RxWmiLog( LOG, RxCompleteRequest, LOGPTR( RxContext ) LOGPTR( Irp ) LOGULONG( Status ) LOGPTR( Irp->IoStatus.Information ) LOGULONG( RxContext->SerialNumber ) LOGUCHAR( RxContext->MinorFunction ) LOGARSTR( RXCONTX_OPERATION_NAME( RxContext->MajorFunction, TRUE )) ); } } if ((IrpSp->MajorFunction == IRP_MJ_CREATE) && (Status != STATUS_PENDING) && (RxContext != NULL)) { if (FlagOn( RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH )) { IrpSp->FileObject->FileName.Length += sizeof( WCHAR ); } RxpPrepareCreateContextForReuse( RxContext ); ASSERT ( RxContext->Create.CanonicalNameBuffer == NULL ); } // // Check information returned on successfull writes is no more than requested // ASSERT( (IrpSp->MajorFunction != IRP_MJ_WRITE) || (Irp->IoStatus.Status != STATUS_SUCCESS) || (Irp->IoStatus.Information <= IrpSp->Parameters.Write.Length) ); // // Check that pending returned is in sync with the irp itself // ASSERT( (RxContext == NULL) || (!RxContext->PendingReturned) || FlagOn( IrpSp->Control, SL_PENDING_RETURNED ) ); if( RxContext != NULL ) RxContext->CurrentIrp = NULL; IoCompleteRequest( Irp, PriorityBoost ); } else { // // a call with a null irp.......... // RxLog(( "Irp00 %lx\n", RxContext )); RxWmiLog( LOG, RxCompleteRequest_NI, LOGPTR( RxContext ) ); } // // Delete the Irp context. // if (RxContext != NULL) { RxDereferenceAndDeleteRxContext( RxContext ); } return; } NTSTATUS __RxSynchronizeBlockingOperations ( IN OUT PRX_CONTEXT RxContext, IN PFCB Fcb, IN OUT PLIST_ENTRY BlockingIoQ, IN BOOLEAN DropFcbLock ) /*++ Routine Description: This routine is used to synchronize among blocking IOs to the same Q. Currently, the routine is only used to synchronize block pipe operations and the Q is the one in the file object extension (Fobx). What happens is that the operation joins the queue. If it is now the front of the queue, the operation continues; otherwise it waits on the sync event in the RxContext or just returns pending (if async). We may have been cancelled while we slept, check for that and return an error if it happens. The event must have been reset before the call. The fcb lock must be held; it is dropped after we get on the Q. Arguments: RxContext The context of the operation being synchronized BlockingIoQ The queue to get on. --*/ { NTSTATUS Status; PIRP Irp = RxContext->CurrentIrp; BOOLEAN FcbLockDropped = FALSE; BOOLEAN SerializationMutexReleased = FALSE; PRX_CONTEXT FrontRxContext; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock, rxc=%08lx, fobx=%08lx\n", RxContext, RxContext->pFobx) ); // // do this early since a cleanup could come through and change it // RxContext->StoredStatus = STATUS_SUCCESS; ExAcquireFastMutex( &RxContextPerFileSerializationMutex ); if (!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED )) { SetFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION ); InsertTailList( BlockingIoQ, &RxContext->RxContextSerializationQLinks ); FrontRxContext = CONTAINING_RECORD( BlockingIoQ->Flink, RX_CONTEXT, RxContextSerializationQLinks ); if (RxContext != FrontRxContext) { if (!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION )) { if (!SerializationMutexReleased) { SerializationMutexReleased = TRUE; ExReleaseFastMutex( &RxContextPerFileSerializationMutex ); } if (DropFcbLock && !FcbLockDropped) { RxContext->FcbResourceAcquired = FALSE; FcbLockDropped = TRUE; RxReleaseFcb( RxContext, Fcb ); } RxDbgTrace( 0, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock waiting, rxc=%08lx\n", RxContext) ); RxWaitSync( RxContext ); RxDbgTrace( 0, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock ubblocked, rxc=%08lx\n", RxContext) ); } else { RxContext->StoredStatus = STATUS_PENDING; SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_BLOCKED_PIPE_RESUME ); try { RxPrePostIrp( RxContext, Irp ); } finally { if (AbnormalTermination()) { RxLog(( "!!!!! RxContext %lx Status %lx\n", RxContext, RxContext->StoredStatus )); RxWmiLog( LOG, RxSynchronizeBlockingOperationsMaybeDroppingFcbLock, LOGPTR( RxContext ) LOGULONG( Status )); RemoveEntryList(&RxContext->RxContextSerializationQLinks); RxContext->RxContextSerializationQLinks.Flink = NULL; RxContext->RxContextSerializationQLinks.Blink = NULL; ClearFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION ); if (!SerializationMutexReleased) { SerializationMutexReleased = TRUE; ExReleaseFastMutex( &RxContextPerFileSerializationMutex ); } } else { InterlockedIncrement( &RxContext->ReferenceCount ); } } RxDbgTrace( -1, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock asyncreturn, rxc=%08lx\n", RxContext) ); } } if (FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_CANCELLED)) { Status = STATUS_CANCELLED; } else { Status = RxContext->StoredStatus; } } else { Status = STATUS_CANCELLED; } if (!SerializationMutexReleased) { SerializationMutexReleased = TRUE; ExReleaseFastMutex( &RxContextPerFileSerializationMutex ); } if (DropFcbLock && !FcbLockDropped) { RxContext->FcbResourceAcquired = FALSE; FcbLockDropped = TRUE; RxReleaseFcb( RxContext, Fcb ); } RxDbgTrace( -1, Dbg, ("RxSynchronizeBlockingOperationsAndDropFcbLock returning, rxc=%08lx, status=%08lx\n", RxContext, Status) ); return Status; } VOID RxRemoveOperationFromBlockingQueue ( IN OUT PRX_CONTEXT RxContext ) /*++ Routine Description: This routine removes the context from the blocking queue if it is on it Arguments: RxContext The context of the operation being synchronized --*/ { PAGED_CODE(); ExAcquireFastMutex( &RxContextPerFileSerializationMutex ); if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION )) { ClearFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION ); RemoveEntryList( &RxContext->RxContextSerializationQLinks ); RxContext->RxContextSerializationQLinks.Flink = NULL; RxContext->RxContextSerializationQLinks.Blink = NULL; } ExReleaseFastMutex( &RxContextPerFileSerializationMutex ); RxDbgTrace( -1, Dbg, ("RxRemoveOperationFromBlockingQueue, rxc=%08lx\n", RxContext )); return; } VOID RxCancelBlockingOperation ( IN OUT PRX_CONTEXT RxContext, IN PIRP Irp ) /*++ Routine Description: This routine cancels the operation in the blocking queue Arguments: RxContext - The context of the operation being synchronized Return: None --*/ { PIO_STACK_LOCATION IrpSp; PFCB Fcb; PFOBX Fobx; BOOLEAN CompleteRequest = FALSE; PAGED_CODE(); ExAcquireFastMutex( &RxContextPerFileSerializationMutex ); if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION ) && (RxContext->RxContextSerializationQLinks.Flink != NULL)) { // // Now its safe to get the fobx - since we know the irp is still active // we will cancel the request if it isn't on the front of the list // IrpSp = IoGetCurrentIrpStackLocation( Irp ); RxDecodeFileObject( IrpSp->FileObject, &Fcb, &Fobx ); if ((RxContext != CONTAINING_RECORD( Fobx->Specific.NamedPipe.ReadSerializationQueue.Flink, RX_CONTEXT, RxContextSerializationQLinks) ) && (RxContext != CONTAINING_RECORD( Fobx->Specific.NamedPipe.WriteSerializationQueue.Flink, RX_CONTEXT, RxContextSerializationQLinks) )) { ClearFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION ); RemoveEntryList( &RxContext->RxContextSerializationQLinks ); RxContext->RxContextSerializationQLinks.Flink = NULL; RxContext->RxContextSerializationQLinks.Blink = NULL; RxContext->StoredStatus = STATUS_CANCELLED; if (!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION )) { RxSignalSynchronousWaiter( RxContext ); } else { CompleteRequest = TRUE; } } } ExReleaseFastMutex( &RxContextPerFileSerializationMutex ); if (CompleteRequest) { RxFsdPostRequest( RxContext ); } RxDbgTrace( -1, Dbg, ("RxCancelBlockedOperations, rxc=%08lx\n", RxContext )); return; } VOID RxResumeBlockedOperations_Serially ( IN OUT PRX_CONTEXT RxContext, IN OUT PLIST_ENTRY BlockingIoQ ) /*++ Routine Description: This routine wakes up the next guy, if any, on the serialized blockingioQ. We know that the fcb must still be valid because of the reference that is being held by the IO system on the file object thereby preventing a close. Arguments: RxContext The context of the operation being synchronized BlockingIoQ The queue to get on. --*/ { PLIST_ENTRY ListEntry; BOOLEAN FcbLockHeld = FALSE; PRX_CONTEXT FrontRxContext = NULL; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxResumeBlockedOperations_Serially, rxc=%08lx, fobx=%08lx\n", RxContext, RxContext->pFobx )); // // remove myself from the queue and check for someone else // ExAcquireFastMutex( &RxContextPerFileSerializationMutex ); if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION )) { ClearFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION ); // ValidateBlockingIoQ(BlockingIoQ); RemoveEntryList( &RxContext->RxContextSerializationQLinks ); // ValidateBlockingIoQ(BlockingIoQ); RxContext->RxContextSerializationQLinks.Flink = NULL; RxContext->RxContextSerializationQLinks.Blink = NULL; ListEntry = BlockingIoQ->Flink; if (BlockingIoQ != ListEntry) { FrontRxContext = CONTAINING_RECORD( ListEntry, RX_CONTEXT, RxContextSerializationQLinks ); RxDbgTrace( -1, Dbg, ("RxResumeBlockedOperations unwaiting the next guy and returning, rxc=%08lx\n", RxContext )); } else { FrontRxContext = NULL; } if (FrontRxContext != NULL) { if (!FlagOn( FrontRxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION )) { RxSignalSynchronousWaiter( FrontRxContext ); } else { // // The reference taken in the synchronization routine is derefernced // by the post completion routine, // RxFsdPostRequest( FrontRxContext ); } } } ExReleaseFastMutex( &RxContextPerFileSerializationMutex ); RxDbgTrace( -1, Dbg, ("RxResumeBlockedOperations_Serially returning, rxc=%08lx\n", RxContext ) ); return; } VOID RxResumeBlockedOperations_ALL ( IN OUT PRX_CONTEXT RxContext ) /*++ Routine Description: This routine wakes up all of the guys on the blocked operations queue. The controlling mutex is also stored in the RxContext block. the current implementation is that all of the guys must be waiting on the sync events. Arguments: RxContext The context of the operation being synchronized --*/ { LIST_ENTRY CopyOfQueue; PLIST_ENTRY ListEntry; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxResumeBlockedOperations_ALL, rxc=%08lx\n", RxContext) ); RxTransferListWithMutex( &CopyOfQueue, &RxContext->BlockedOperations, RxContext->BlockedOpsMutex ); for (ListEntry = CopyOfQueue.Flink; ListEntry != &CopyOfQueue;) { PRX_CONTEXT FrontRxContext = CONTAINING_RECORD( ListEntry, RX_CONTEXT, RxContextSerializationQLinks ); RxSignalSynchronousWaiter( FrontRxContext ); IF_DEBUG { PLIST_ENTRY PrevListEntry = ListEntry; ListEntry = ListEntry->Flink; PrevListEntry->Flink = PrevListEntry->Blink = NULL; } else { ListEntry = ListEntry->Flink; } } RxDbgTrace( -1, Dbg, ("RxResumeBlockedOperations_ALL returning, rxc=%08lx\n", RxContext) ); return; } VOID __RxItsTheSameContext ( PRX_CONTEXT RxContext, ULONG CapturedRxContextSerialNumber, ULONG Line, PSZ File ) { if ((NodeType( RxContext ) != RDBSS_NTC_RX_CONTEXT) || (RxContext->SerialNumber != CapturedRxContextSerialNumber)) { RxLog(( "NotSame!!!! %lx", RxContext )); RxWmiLog( LOG, RxItsTheSameContext, LOGPTR( RxContext ) ); DbgPrint( "NOT THE SAME CONTEXT %08lx at Line %d in %s\n", RxContext, Line, File ); } } #if 0 VOID ValidateBlockingIoQ( PLIST_ENTRY BlockingIoQ ) { PLIST_ENTRY ListEntry; ULONG cntFlink, cntBlink; cntFlink = cntBlink = 0; ListEntry = BlockingIoQ->Flink; while (ListEntry != BlockingIoQ) { PRX_CONTEXT RxContext; RxContext = (PRX_CONTEXT)CONTAINING_RECORD( ListEntry, RX_CONTEXT, RxContextSerializationQLinks ); if (!RxContext || (NodeType( RxContext ) != RDBSS_NTC_RX_CONTEXT)) { DbgPrint("ValidateBlockingIO:Invalid RxContext %x on Q %x\n", RxContext, BlockingIoQ ); //DbgBreakPoint(); } cntFlink += 1; ListEntry = ListEntry->Flink; } // // check backward list validity // ListEntry = BlockingIoQ->Blink; while (ListEntry != BlockingIoQ) { PRX_CONTEXT RxContext; RxContext = (PRX_CONTEXT)CONTAINING_RECORD( ListEntry, RX_CONTEXT, RxContextSerializationQLinks ); if (!RxContext || (NodeType( RxContext ) != RDBSS_NTC_RX_CONTEXT)) { DbgPrint("ValidateBlockingIO:Invalid RxContext %x on Q %x\n", RxContext, BlockingIoQ); //DbgBreakPoint(); } cntBlink += 1; ListEntry = ListEntry->Blink; } // // both counts should be the same // if(cntFlink != cntBlink) { DbgPrint( "ValidateBlockingIO: cntFlink %d cntBlink %d\n", cntFlink, cntBlink ); //DbgBreakPoint(); } } #endif #ifndef RX_NO_DBGFIELD_HLPRS #define DECLARE_FIELD_HLPR(x) ULONG RxContextField_##x = FIELD_OFFSET(RX_CONTEXT,x); #define DECLARE_FIELD_HLPR2(x,y) ULONG RxContextField_##x##y = FIELD_OFFSET(RX_CONTEXT,x.y); DECLARE_FIELD_HLPR(MajorFunction); DECLARE_FIELD_HLPR(CurrentIrp); DECLARE_FIELD_HLPR(pFcb); DECLARE_FIELD_HLPR(Flags); DECLARE_FIELD_HLPR(MRxContext); DECLARE_FIELD_HLPR(MRxCancelRoutine); DECLARE_FIELD_HLPR(SyncEvent); DECLARE_FIELD_HLPR(BlockedOperations); DECLARE_FIELD_HLPR(FlagsForLowIo); DECLARE_FIELD_HLPR2(Create,CanonicalNameBuffer); DECLARE_FIELD_HLPR2(Create,pSrvCall); DECLARE_FIELD_HLPR2(Create,pNetRoot); DECLARE_FIELD_HLPR2(Create,pVNetRoot); DECLARE_FIELD_HLPR2(QueryDirectory,FileIndex); DECLARE_FIELD_HLPR2(QueryEa,UserEaList); DECLARE_FIELD_HLPR2(QuerySecurity,SecurityInformation); DECLARE_FIELD_HLPR2(QuerySecurity,Length); #endif