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.
1076 lines
36 KiB
1076 lines
36 KiB
/*++
|
|
|
|
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);
|
|
}
|
|
|