/*++ Copyright (c) 1989 Microsoft Corporation Module Name: LockCtrl.c Abstract: This module implements the Lock Control routines for Rx called by the dispatch driver. Author: Joe Linn [JoeLinn] 9-Nov-1994 Revision History: --*/ #include "precomp.h" #pragma hdrstop // // The local debug trace level // #define Dbg (DEBUG_TRACE_LOCKCTRL) NTSTATUS RxLowIoLockControlShellCompletion ( IN PRX_CONTEXT RxContext ); NTSTATUS RxLockOperationCompletionWithAcquire ( IN PRX_CONTEXT RxContext, IN PIRP Irp ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, RxCommonLockControl) #pragma alloc_text(PAGE, RxLockOperationCompletion) #pragma alloc_text(PAGE, RxLockOperationCompletionWithAcquire) #pragma alloc_text(PAGE, RxUnlockOperation) #pragma alloc_text(PAGE, RxLowIoLockControlShellCompletion) #pragma alloc_text(PAGE, RxFinalizeLockList) #pragma alloc_text(PAGE, RxLowIoLockControlShell) #endif NTSTATUS RxCommonLockControl ( IN PRX_CONTEXT RxContext, IN PIRP Irp ) /*++ Routine Description: This is the common routine for doing Lock control operations called by both the fsd and fsp threads Arguments: Irp - Supplies the Irp to process Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); PFCB Fcb; PFOBX Fobx; NODE_TYPE_CODE TypeOfOpen; PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; PAGED_CODE(); TypeOfOpen = RxDecodeFileObject( IrpSp->FileObject, &Fcb, &Fobx ); RxDbgTrace( +1, Dbg, ("RxCommonLockControl...IrpC %08lx, Fobx %08lx, Fcb %08lx\n", RxContext, Fobx, Fcb) ); RxDbgTrace( 0, Dbg, ("MinorFunction = %08lx\n", IrpSp->MinorFunction) ); RxLog(( "Lock %lx %lx %lx %lx\n", RxContext, Fobx, Fcb, IrpSp->MinorFunction )); RxWmiLog( LOG, RxCommonLockControl_1, LOGPTR( RxContext ) LOGPTR( Fobx ) LOGPTR( Fcb ) LOGUCHAR( IrpSp->MinorFunction )); // // If the file is not a user file open then we reject the request // as an invalid parameter // if (TypeOfOpen != RDBSS_NTC_STORAGE_TYPE_FILE) { RxDbgTrace(-1, Dbg, ("RxCommonLockControl -> RxStatus(INVALID_PARAMETER\n)", 0)); return STATUS_INVALID_PARAMETER; } // // Acquire shared access to the Fcb and enqueue the Irp if we didn't // get access. // Status = RxAcquireSharedFcb( RxContext, Fcb ); if (Status == STATUS_LOCK_NOT_GRANTED) { Status = RxFsdPostRequest( RxContext ); RxDbgTrace(-1, Dbg, ("RxCommonLockControl -> %08lx\n", Status)); return Status; } else if (Status != STATUS_SUCCESS) { RxDbgTrace(-1, Dbg, ("RxCommonLockControl -> error accquiring Fcb (%lx) %08lx\n", Fcb, Status)); return Status; } SetFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD ); try { // // tell the buffering change guys that locks are outstanding // InterlockedIncrement( &Fcb->OutstandingLockOperationsCount ); // // setup the bit telling the unlock routine whether to pitch or save the unlocks passed down // from fsrtl. // switch (IrpSp->MinorFunction) { case IRP_MN_LOCK: // // find out if this lock is realizable......if not, don't proceed........ // if ((Fcb->MRxDispatch != NULL) && (Fcb->MRxDispatch->MRxIsLockRealizable != NULL)) { Status = Fcb->MRxDispatch->MRxIsLockRealizable( (PMRX_FCB)Fcb, &IrpSp->Parameters.LockControl.ByteOffset, IrpSp->Parameters.LockControl.Length, IrpSp->Flags ); } if (Status != STATUS_SUCCESS) { try_return( Status ); } if (!FlagOn( IrpSp->Flags, SL_FAIL_IMMEDIATELY )) { // // we cannot handout in the lock queue with the resource held // RxReleaseFcb( RxContext, Fcb ); ClearFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD ); } break; case IRP_MN_UNLOCK_SINGLE: break; case IRP_MN_UNLOCK_ALL: case IRP_MN_UNLOCK_ALL_BY_KEY: SetFlag( LowIoContext->Flags, LOWIO_CONTEXT_FLAG_SAVEUNLOCKS ); break; } // // Now call the FsRtl routine to do the actual processing of the // Lock request; take a reference before we go in that will be removed // by the LockOperationComplete guy. // RxLog(( "Inc RxC %lx L %ld %lx\n", RxContext, __LINE__, RxContext->ReferenceCount )); RxWmiLog( LOG, RxCommonLockControl_2, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount )); InterlockedIncrement( &RxContext->ReferenceCount ); // // Store the current thread id. in the lock manager context to // distinguisgh between the case when the request was pended in the // lock manager and the case when it was immediately satisfied. // InterlockedExchangePointer( &RxContext->LockManagerContext, PsGetCurrentThread() ); try { Status = FsRtlProcessFileLock( &Fcb->FileLock, Irp, RxContext ); } except(EXCEPTION_EXECUTE_HANDLER) { return RxProcessException( RxContext, GetExceptionCode() ); } // // see bug: 514303 // if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_OPERATION_COMPLETED )) { // // reset the status in order to force a completion // Status = STATUS_SUCCESS; } // // call the completion wrapper that reacquires the resource // if ((Status == STATUS_SUCCESS) && !FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD )) { // // if we get queued then we have to keep the refcount up to prevent early finalization // from later in this routine. so take a reference here and set up to remove it // if we call down to lowio // RxLog(( "Inc RxC %lx L %ld %lx\n", RxContext, __LINE__, RxContext->ReferenceCount )); RxWmiLog( LOG, RxCommonLockControl_3, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount ) ); InterlockedIncrement( &RxContext->ReferenceCount ); SetFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_WAS_QUEUED_IN_LOCKMANAGER ); Status = RxLockOperationCompletionWithAcquire( RxContext, Irp ); if (Status != STATUS_PENDING) { // // take back the reference....didn't need it. this cannot // be the last one, so we just decrement // RxLog(( "Dec RxC %lx L %ld %lx\n", RxContext,__LINE__,RxContext->ReferenceCount )); RxWmiLog( LOG, RxCommonLockControl_4, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount ) ); InterlockedDecrement( &RxContext->ReferenceCount); } } else if (Status == STATUS_PENDING) { InterlockedExchangePointer( &RxContext->LockManagerContext, NULL ); } try_exit: NOTHING; } finally { DebugUnwind( RxCommonLockControl ); // // If resources have been acquired, release them under the right conditions. // the right conditions are these: // 1) if we have abnormal termination. here we obviously release the since no one else will. // 2) if the underlying call did not succeed: Status==Pending. // 3) if we posted the request // We also take away a opcount since the context is about to be completed. // if (AbnormalTermination() || (Status != STATUS_PENDING)) { if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD )) { InterlockedDecrement( &Fcb->OutstandingLockOperationsCount ); // // If we have set the resource owner to the RxContext, then we have to call // ReleaseForThread. if( RESOURCE_OWNER_SET( LowIoContext->ResourceThreadId ) ) { RxReleaseFcbForThread( RxContext, Fcb, LowIoContext->ResourceThreadId ); } else { RxReleaseFcb( RxContext, Fcb ); } } } else { // // here the guy below is going to handle the completion....but, we // don't know the finish order....in all likelihood the deletecontext // call below just reduces the refcount but the guy may already have // finished in which case this will really delete the context. // RxLog(( "Dec RxC %lx L %ld %lx\n", RxContext, __LINE__, RxContext->ReferenceCount )); RxWmiLog( LOG, RxCommonLockControl_5, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount )); RxDereferenceAndDeleteRxContext( RxContext ); } RxDbgTrace( -1, Dbg, ("RxCommonLockControl -> %08lx\n", Status) ); } // finally return Status; } #define RDBSS_LOCK_MANAGER_REQUEST_RESUMED (IntToPtr(0xaaaaaaaa)) // Sundown: sign-extended. NTSTATUS RxLockOperationCompletion ( IN PVOID Context, IN PIRP Irp ) /*++ Routine Description: This routine is called after the FSRTL lock package has processed a lock operation. If locks are not being held, we call down to the correct minirdr routine. BTW, we do not actually complete the packet here. that is either done above (fsd or fsp) or it will be done asynchronously. The logic for locks is greatly complicated by locks that do not fail immediately but wait until they can be completed. If the request is not of this type then we will go into the normal lowio stuff from here. If the request is !failimmediately, then there are two cases depending on whether or not the lock was enqueued. In both cases, we have to reacquire the resource before we can proceed. In the case where the lock was NOT enqueued, we back up into CommonLock where there is code to call back into this routine with the resource held. Then things proceed as normal. However, if we did wait in the lock queue then we will have to post to a worker thread to get our work. Here, PENDING is returned to CommonLock so he removes one refcount leaving only one for this routine. However, the normal entrystate for this routine is 2 refcounts....one that is taken away here and one that belongs to whoever completes the request. So, we take an extra one before we post. Posting leaves us still with two cases: locks-buffered vs locks-not-buffered. In the locks-buffered case, we come back in here with 2 refcounts; we take awayone here and the fspdispatch takes away the other when it completes. Finally, if locks are not buffered then we go to the wire: since it is async, pending could be returned. no-pending is just as before. With pending, lowio takes an extra reference that belongs to the completion routine and that leaves one reference to take away on this path. Unlike commonlock, fspdispatch does not have the clause that takes away a reference on pending-returned. SOOOOOO, we take one away here if lowio returns pending AND we were enqueued. got that??? A minirdr should not go for an indefinite stay at the wire with the resource held; if necessary, it should drop the resource and reacquire it. Arguments: IN PVOID Context - Provides the context supplied to FsRtlProcessFileLock. In this case, it is the original RxContext. IN PIRP Irp - Supplies an IRP describing the lock operation. Return Value: RXSTATUS - Final status of operation.. --*/ { PRX_CONTEXT RxContext = Context; NTSTATUS Status = Irp->IoStatus.Status; PVOID LockManagerContext; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); PFCB Fcb = (PFCB)RxContext->pFcb; PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxLockOperationCompletion -> RxContext = %08lx, 1stStatus= %08lx\n", RxContext, Status) ); RxLog(( "LockCompEntry %lx %lx\n", RxContext, Status )); RxWmiLog( LOG, RxLockOperationCompletion_1, LOGPTR( RxContext ) LOGULONG( Status ) ); ASSERT( Context ); ASSERT( Irp == RxContext->CurrentIrp ); // // The LockManagerContext field in the RxContext supplied to the lock // manager routine is initialized to the thread id. of the thread submitting // the request and set to RDBSS_LOCK_MANAGER_REQUEST_PENDING on return if // STATUS_PENDING is returned. Thus if this routine is called with the // value in this field is not equal to either the current thread id. or // RDBSS_LOCK_MANAGER_REQUEST_RESUMED, we are guaranteed that this request // was pended in the the lock manager. // LockManagerContext = InterlockedExchangePointer( &RxContext->LockManagerContext, RDBSS_LOCK_MANAGER_REQUEST_RESUMED ); if ((LockManagerContext != PsGetCurrentThread()) && (LockManagerContext != RDBSS_LOCK_MANAGER_REQUEST_RESUMED)) { // // here we were hung out in the lock queue........turn the operation to // async and post to get the resource back. read the notes above to see // why we inc the refcount // RxLog(( "Inc RxC %lx L %ld %lx\n", RxContext, __LINE__, RxContext->ReferenceCount )); RxWmiLog( LOG, RxLockOperationCompletion_3, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount )); InterlockedIncrement( &RxContext->ReferenceCount ); SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); SetFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_WAS_QUEUED_IN_LOCKMANAGER ); RxDbgTrace( -1, Dbg, ("Posting Queued LockReq = %08lx\n", RxContext) ); if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD )) { RxReleaseFcb( RxContext, Fcb ); ClearFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD ); } Status = RxFsdPostRequestWithResume( RxContext, RxLockOperationCompletionWithAcquire ); return Status; } // // if we dropped the resource before we came in, then we reacquire it now. // the guy above me must be the commonlock routine and he come back down // thru the reacquire wrapper...sort of a post without posting // if (!FlagOn( RxContext->FlagsForLowIo,RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD )) { // // see bug: 514303 // force completion regardless of return status. fsrtlprocessfilelock can return status_pending // and we will loss this completion, so mark it // SetFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_OPERATION_COMPLETED ); Status = STATUS_SUCCESS; RxLog(( "ResDropUp! %lx %lx\n", RxContext, Fcb->FcbState )); RxWmiLog( LOG, RxLockOperationCompletion_4, LOGPTR( RxContext ) LOGULONG( Fcb->FcbState ) ); RxDbgTrace( -1, Dbg, ("RxLockOperationCompletion Resdropup-> Status = %08lx\n", Status) ); return Status; } // // this is the normal case. remove the extra reference. this cannot // be the last one, so we just decrement // RxLog(( "Dec RxC %lx L %ld %lx\n", RxContext, __LINE__, RxContext->ReferenceCount )); RxWmiLog( LOG, RxLockOperationCompletion_5, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount ) ); InterlockedDecrement( &RxContext->ReferenceCount ); // // if we have nonsuccess, just get out! // if (!NT_SUCCESS( Status )) { RxLog(( "NONSUCCESS %lx %lx\n", RxContext, Status )); RxWmiLog( LOG, RxLockOperationCompletion_6, LOGPTR( RxContext ) LOGULONG( Status ) ); RxDbgTrace( -1, Dbg, ("RxLockOperationCompletion NONSUCCESS-> Rxc,Status =%08lx %08lx\n", RxContext, Status )); return Status; } // // if locks are buffered, just get out // if (FlagOn( Fcb->FcbState, FCB_STATE_LOCK_BUFFERING_ENABLED )) { Status = STATUS_SUCCESS; RxLog(("LocksBuffered! %lx %lx %lx\n", RxContext, Fcb->FcbState, RxContext->ReferenceCount )); RxWmiLog( LOG, RxLockOperationCompletion_7, LOGPTR( RxContext ) LOGULONG( Fcb->FcbState ) LOGULONG( RxContext->ReferenceCount )); RxDbgTrace( -1, Dbg, ("RxLockOperationCompletion LockOpBuffered-> Status = %08lx\n", Status) ); return Status; } // // otherwise, let's go to the mini // RxInitializeLowIoContext( RxContext, LOWIO_OP_UNLOCK, LowIoContext ); LowIoContext->ParamsFor.Locks.ByteOffset = IrpSp->Parameters.LockControl.ByteOffset.QuadPart; LowIoContext->ParamsFor.Locks.Key = IrpSp->Parameters.LockControl.Key; LowIoContext->ParamsFor.Locks.Flags = 0; // no flags switch (IrpSp->MinorFunction) { case IRP_MN_LOCK: if (FlagOn( IrpSp->Flags, SL_EXCLUSIVE_LOCK )) { LowIoContext->Operation = LOWIO_OP_EXCLUSIVELOCK; } else { LowIoContext->Operation = LOWIO_OP_SHAREDLOCK; } LowIoContext->ParamsFor.Locks.Flags = IrpSp->Flags; LowIoContext->ParamsFor.Locks.Length = (*IrpSp->Parameters.LockControl.Length).QuadPart; break; case IRP_MN_UNLOCK_SINGLE: LowIoContext->ParamsFor.Locks.Length = (*IrpSp->Parameters.LockControl.Length).QuadPart; break; case IRP_MN_UNLOCK_ALL: case IRP_MN_UNLOCK_ALL_BY_KEY: LowIoContext->ParamsFor.Locks.Length = 0; if (LowIoContext->ParamsFor.Locks.LockList == NULL) { RxDbgTrace( -1, Dbg, ("RxLockOperationCompletion -> Nothing to unlock\n") ); return STATUS_SUCCESS; } LowIoContext->Operation = LOWIO_OP_UNLOCK_MULTIPLE; break; } RxDbgTrace( 0, Dbg, ("--->Operation = %08lx\n", LowIoContext->Operation) ); Status = RxLowIoLockControlShell( RxContext, Irp, Fcb ); if ((Status == STATUS_PENDING) && FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_WAS_QUEUED_IN_LOCKMANAGER )) { // // the fsp dispatch routine doesn't have a clause to take away a // reference on pended operations. so, if we were queued AND we are // returning pending back thru the fsp, then take away a reference here. // RxLog(( "Dec RxC %lx L %ld %lx\n", RxContext,__LINE__,RxContext->ReferenceCount )); RxWmiLog( LOG, RxLockOperationCompletion_8, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount ) ); RxDereferenceAndDeleteRxContext( RxContext ); } RxDbgTrace( -1, Dbg, ("RxLockOperationCompletion -> Status = %08lx\n", Status) ); return Status; } NTSTATUS RxLockOperationCompletionWithAcquire ( IN PRX_CONTEXT RxContext, IN PIRP Irp ) /*++ Routine Description: This routine is responsible to get the resource back in case we were held up in the lock queue. Then it calls the LockOperationComplete. Of course, it has to mess around with giving the resource back for abnormal termination and the like. Two things are invariant ... first, when we get here there are two references on the rxcontext. also, unless we return pending, the fsp guy above will try to complete this request. here's what we do. this routine is refcount-neutral: it always takes away as many as it places. it has to place refcount on the context if it acquires the resource in order to maintain the invariant that a context always!!!!! has the same number of releases as acquires. if it takes this refcount, then it releases it EVEN IF THE FCB IS NOT RELEASED HERE. (it might be relased on the async path instead.) Last, we can also be called from the original commonlock routine. in this case, we take care of releasing the fcb and clear the flag so that it will not be released above. Arguments: Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); PFCB Fcb = (PFCB)RxContext->pFcb; PFOBX Fobx = (PFOBX)RxContext->pFobx; PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; BOOLEAN ReleaseFcb = FALSE; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxLockOperationCompletionWithAcquire...IrpC %08lx, Fobx %08lx, Fcb %08lx\n", RxContext, Fobx, Fcb )); RxDbgTrace( 0, Dbg, ("MinorFunction = %08lx\n", IrpSp->MinorFunction) ); RxLog(( "LockAcq %lx %lx %lx %lx\n", RxContext, Fobx, Fcb, IrpSp->MinorFunction )); RxWmiLog( LOG, RxLockOperationCompletionWithAcquire_1, LOGPTR( RxContext ) LOGPTR( Fobx ) LOGPTR( Fcb ) LOGUCHAR( IrpSp->MinorFunction ) ); // // Acquire shared access to the Fcb // Status = RxAcquireSharedFcb( RxContext, Fcb ); if (Status == STATUS_LOCK_NOT_GRANTED) { Status = RxFsdPostRequestWithResume( RxContext, RxLockOperationCompletionWithAcquire ); RxDbgTrace( -1, Dbg, ("RxLockOperationCompletionWithAcquire -> %08lx\n", Status) ); return Status; } else if (Status != STATUS_SUCCESS) { RxLog(( "Dec RxC %lx L %ld %lx\n", RxContext,__LINE__,RxContext->ReferenceCount )); InterlockedDecrement( &RxContext->ReferenceCount); return Status; } SetFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD ); RxLog(( "Inc RxC %lx L %ld %lx\n", RxContext, __LINE__, RxContext->ReferenceCount )); RxWmiLog( LOG, RxLockOperationCompletionWithAcquire_2, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount ) ); InterlockedIncrement( &RxContext->ReferenceCount ); // we MUST!!! deref try { // // Call the guy to complete the lock request....with the resrouce held // Status = RxLockOperationCompletion( RxContext, Irp ); } finally { DebugUnwind( RxLockOperationCompletionWithAcquire ); // // If resources have been acquired, release them under the right conditions. // the right conditions are these: // 1) if we have abnormal termination. here we obviously release the // resource since no one else will. // 2) if the underlying call did not succeed: Status==Pending. // We also take away a opcount since the context is about to be completed. // if (AbnormalTermination() || (Status != STATUS_PENDING)) { ReleaseFcb = TRUE; } else { // // here the guy below is going to handle the completion // ASSERT( FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ) ); } if (ReleaseFcb) { InterlockedDecrement( &Fcb->OutstandingLockOperationsCount ); if(RESOURCE_OWNER_SET(LowIoContext->ResourceThreadId ) ) { RxReleaseFcbForThread( RxContext, Fcb, LowIoContext->ResourceThreadId ); } else { RxReleaseFcb( RxContext, Fcb ); } ClearFlag( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_LOCK_FCB_RESOURCE_HELD ); } // // if we took a refcount we MUST deref no matter what! // RxLog(( "Dec RxC %lx L %ld %lx\n", RxContext, __LINE__, RxContext->ReferenceCount )); RxWmiLog( LOG, RxLockOperationCompletionWithAcquire_3, LOGPTR( RxContext ) LOGULONG( RxContext->ReferenceCount ) ); RxDereferenceAndDeleteRxContext( RxContext ); RxDbgTrace( -1, Dbg, ("RxLockOperationCompletionWithAcquire -> %08lx\n", Status) ); } // finally return Status; } VOID RxUnlockOperation ( IN PVOID Context, IN PFILE_LOCK_INFO LockInfo ) /*++ Routine Description: This routine is called after the FSRTL lock package has determined that a locked region is to be unlocked. We do one of two things as determined by a bit in the lowio_flags field. If the bit is clear, we just ignore the call. This happens on unlock single calls that are passed down to the minirdr exactly as a single lock. For a unlock_all or unlock_all_by_key, we use these calls to get an enumeration of the lock set. then, these go thru lowIO but using the list method. Arguments: IN PVOID Context - Provides the context supplied to FsRtlProcessFileLock. In this case, it is the RxContext of the Ongoing unlock operation OR of a cleanup operation IN PFILE_LOCK_INFO LockInfo - Describes the region being unlock Return Value: RXSTATUS - Status of unlock operation (it can't really fail, though). --*/ { PRX_CONTEXT RxContext = Context; PFCB Fcb; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxUnlockOperation -> RxContext/LowByte = %08lx/%08lx\n", RxContext,LockInfo->StartingByte.LowPart) ); RxLog(( "Unlck %x %x",RxContext,LockInfo->StartingByte.LowPart )); RxWmiLog(LOG, RxUnlockOperation, LOGPTR( RxContext ) LOGULONG( LockInfo->StartingByte.LowPart ) ); // // If there is a NULL context, this means that this routine was called // on behalf of a failed lock request, so we return immediately. // if (Context != NULL) { PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation( RxContext->CurrentIrp)->FileObject; Fcb = (PFCB) RxContext->pFcb; ASSERT( FileObject == LockInfo->FileObject ); // ok4->FileObj if (FlagOn( LowIoContext->Flags, LOWIO_CONTEXT_FLAG_SAVEUNLOCKS ) && !FlagOn( Fcb->FcbState, FCB_STATE_LOCK_BUFFERING_ENABLED )) { PLOWIO_LOCK_LIST LockList,ThisElement; LockList = LowIoContext->ParamsFor.Locks.LockList; ThisElement = RxAllocatePoolWithTag( PagedPool, sizeof( LOWIO_LOCK_LIST ), 'LLxR' ); if (ThisElement==NULL) { RxDbgTrace( -1, Dbg, ("RxUnlockOperation FAILED ALLOCATION!\n") ); return; } if (LockList == NULL) { ThisElement->LockNumber = 1; } else { ThisElement->LockNumber = LockList->LockNumber + 1; } ThisElement->Next = LockList; ThisElement->ByteOffset = LockInfo->StartingByte.QuadPart; ThisElement->Length = LockInfo->Length.QuadPart; LowIoContext->ParamsFor.Locks.LockList = ThisElement; } } RxDbgTrace(-1, Dbg, ("RxUnlockOperation -> status=%08lx\n", Status)); return; } // // Internal support routine // NTSTATUS RxLowIoLockControlShellCompletion ( IN PRX_CONTEXT RxContext ) /*++ Routine Description: This routine postprocesses a read request after it comes back from the minirdr. It does callouts to handle compression, buffering and shadowing. It is the opposite number of LowIoLockControlShell. This will be called from LowIo; for async, originally in the completion routine. If RxStatus(MORE_PROCESSING_REQUIRED) is returned, LowIo will call again in a thread. If this was syncIo, you'll be back in the user's thread; if async, lowIo will requeue to a thread. Currrently, we always get to a thread before anything; this is a bit slower than completing at DPC time, but it's aheckuva lot safer and we may often have stuff to do (like decompressing, shadowing, etc) that we don't want to do at DPC time. Arguments: RxContext - the usual Return Value: whatever value supplied by the caller or RxStatus(MORE_PROCESSING_REQUIRED). --*/ { NTSTATUS Status; PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; PIRP Irp = RxContext->CurrentIrp; PFCB Fcb = (PFCB) RxContext->pFcb; BOOLEAN SynchronousIo = !BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); PAGED_CODE(); Status = RxContext->StoredStatus; RxDbgTrace( +1, Dbg, ("RxLowIoLockControlShellCompletion entry Status = %08lx\n", Status) ); RxLog(( "LkShlComp" )); RxWmiLog( LOG, RxLowIoLockControlShellCompletion_1, LOGPTR( RxContext ) ); switch (Status) { case STATUS_SUCCESS: break; case STATUS_FILE_LOCK_CONFLICT: break; case STATUS_CONNECTION_INVALID: // // NOT YET IMPLEMENTED here is where the failover will happen // first we give the local guy current minirdr another chance...then we go // to fullscale retry // return(RxStatus(DISCONNECTED)); //special....let LowIo get us back // break; } // // for a unlock_multiple, get rid of the lock_list // if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) { RxFinalizeLockList( RxContext ); } if (FlagOn( LowIoContext->Flags, LOWIO_CONTEXT_FLAG_SYNCCALL )){ // // if we're being called from lowioubmit then just get out // RxDbgTrace( -1, Dbg, ("RxLowIoLockControlShellCompletion syncexit Status = %08lx\n", Status) ); return Status; } // // so we're doing an asynchronous completion. well, the only reason why we would be // trying a lock at the server would be if the lock manager already completed it // successfully! but if it didn't complete successfully at the server then we have // to remove it. // if ((Status != STATUS_SUCCESS) && (RxContext->MajorFunction == IRP_MJ_LOCK_CONTROL) && (RxContext->MinorFunction == IRP_MN_LOCK)) { PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; NTSTATUS LocalStatus; LocalStatus = FsRtlFastUnlockSingle( &Fcb->FileLock, FileObject, (PLARGE_INTEGER)&LowIoContext->ParamsFor.Locks.ByteOffset, (PLARGE_INTEGER)&LowIoContext->ParamsFor.Locks.Length, IoGetRequestorProcess( Irp ), LowIoContext->ParamsFor.Locks.Key, NULL, TRUE ); RxLog(( "RetractLck %lx %lx %lx",RxContext,Status,LocalStatus )); RxWmiLog( LOG, RxLowIoLockControlShellCompletion_2, LOGPTR( RxContext ) LOGULONG( Status ) LOGULONG( LocalStatus ) ); } // // otherwise we have to do the end of the lock from here // InterlockedDecrement( &Fcb->OutstandingLockOperationsCount ); RxReleaseFcbForThread( RxContext, Fcb, LowIoContext->ResourceThreadId ); ASSERT( Status != STATUS_RETRY ); if (Status != STATUS_RETRY) { ASSERT( RxContext->MajorFunction == IRP_MJ_LOCK_CONTROL ); } RxDbgTrace( -1, Dbg, ("RxLowIoLockControlShellCompletion exit Status = %08lx\n", Status) ); return Status; } VOID RxFinalizeLockList ( PRX_CONTEXT RxContext ) /*++ Routine Description: This routine runs down a lock lis and frees each member Arguments: RxContext - the usual Return Value: n/a --*/ { PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; PLOWIO_LOCK_LIST LockList = LowIoContext->ParamsFor.Locks.LockList; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxFinalizeLockList entry rxcontext=%08lx\n", RxContext) ); RxWmiLog( LOG, RxFinalizeLockList, LOGPTR( RxContext ) ); for(;;){ PLOWIO_LOCK_LIST NextLockList; if (LockList == NULL) break; NextLockList = LockList->Next; RxFreePool( LockList ); LockList = NextLockList; } RxDbgTrace(-1, Dbg, ("RxFinalizeLockList exit \n")); return; } NTSTATUS RxLowIoLockControlShell ( IN PRX_CONTEXT RxContext, IN PIRP Irp, IN PFCB Fcb ) /*++ Routine Description: This routine preprocesses a read request before it goes down to the minirdr. It does callouts to handle compression, buffering and shadowing. It is the opposite number of LowIoLockControlShellCompletion. By the time we get here, we are going to the wire. Lock buffering is handled in the lockcompletionroutine (not lowio) Arguments: RxContext - the usual Return Value: whatever value is returned by a callout....or by LowIo. --*/ { NTSTATUS Status; PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; PAGED_CODE(); RxDbgTrace( +1, Dbg, ("RxLowIoLockControlShell entry %08lx\n", 0) ); RxLog(( "%s\n", "skL" )); RxWmiLog( LOG, RxLowIoLockControlShell, LOGPTR( RxContext ) ); // // If we are in FSP, we are not guaranteed the thread will stay around till the // operation is completed. So we set the resource owner to the RX_CONTEXT // so that the resource package does not touch it. // if( BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ) && BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP )) { LowIoContext->ResourceThreadId = MAKE_RESOURCE_OWNER(RxContext); ExSetResourceOwnerPointer(Fcb->Header.Resource, (PVOID)LowIoContext->ResourceThreadId); } Status = RxLowIoSubmit( RxContext, Irp, Fcb, RxLowIoLockControlShellCompletion ); RxDbgTrace(-1, Dbg, ("RxLowIoLockControlShell exit Status = %08lx\n", Status)); return(Status); }