4128 lines
104 KiB
C
4128 lines
104 KiB
C
/*++
|
||
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Oplock.c
|
||
|
||
Abstract:
|
||
|
||
The OPLOCK routines provide support to filesystems which implement
|
||
opporuntistics locks. The specific actions needed are based on
|
||
the current oplocked state of the file (maintained in the OPLOCK
|
||
structure) and the Irp the Io system provides to the file systems.
|
||
Rather than define separate entry points for each oplock operation
|
||
a single generic entry point is defined.
|
||
|
||
The file systems will maintain a variable of type OPLOCK for
|
||
each open file in the system. This variable is initialized
|
||
when an unopened file is opened. It is uninitialized when the
|
||
last reference to the file is cleared when the Io system calls
|
||
the file system with a close call.
|
||
|
||
The following routines are provided by this package:
|
||
|
||
o FsRtlInitializeOplock - Initialize a new OPLOCK structure. There
|
||
should be one OPLOCK for every opened file. Each OPLOCK structure
|
||
must be initialized before it can be used by the system.
|
||
|
||
o FsRtlUninitializeOplock - Uninitialize an OPLOCK structure. This
|
||
call is used to cleanup any anciallary structures allocated and
|
||
maintained by the OPLOCK. After being uninitialized the OPLOCK
|
||
must again be initialized before it can be used by the system.
|
||
|
||
Author:
|
||
|
||
Brian Andrew [BrianAn] 10-Dec-1990
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "FsRtlP.h"
|
||
|
||
//
|
||
// Trace level for the module
|
||
//
|
||
|
||
#define Dbg (0x08000000)
|
||
|
||
//
|
||
// Define the compatible filter oplock desired access flags. We won't break
|
||
// a filter oplock when these flags are the only flags specified.
|
||
//
|
||
|
||
#define FILTER_OPLOCK_VALID_FLAGS ( \
|
||
FILE_READ_ATTRIBUTES | \
|
||
FILE_WRITE_ATTRIBUTES | \
|
||
FILE_READ_DATA | \
|
||
FILE_READ_EA | \
|
||
SYNCHRONIZE | \
|
||
READ_CONTROL \
|
||
)
|
||
|
||
|
||
//
|
||
// We encode the different bits so we can test without having to enumerate
|
||
// all of the possible states.
|
||
//
|
||
// NOTE - The LEVEL_1, BATCH_OPLOCK and FILTER_OPLOCK must be in this order.
|
||
// We assume later on that they are in this order.
|
||
//
|
||
|
||
#define NO_OPLOCK (0x00000001)
|
||
#define LEVEL_I_OPLOCK (0x00000002)
|
||
#define BATCH_OPLOCK (0x00000004)
|
||
#define FILTER_OPLOCK (0x00000008)
|
||
#define LEVEL_II_OPLOCK (0x00000010)
|
||
#define UNINITIALIZE_OPLOCK (0x00000020)
|
||
|
||
#define OPLOCK_TYPE_MASK (0x0000003f)
|
||
|
||
#define EXCLUSIVE (0x00000040)
|
||
#define PENDING (0x00000080)
|
||
|
||
#define OPLOCK_HELD_MASK (0x000000c0)
|
||
|
||
#define BREAK_TO_II (0x00000100)
|
||
#define BREAK_TO_NONE (0x00000200)
|
||
#define BREAK_TO_II_TO_NONE (0x00000400)
|
||
#define CLOSE_PENDING (0x00000800)
|
||
#define TIMER_STARTED (0x00001000)
|
||
|
||
#define OPLOCK_BREAK_MASK (0x00001f00)
|
||
|
||
//
|
||
// The oplock types consist of the appropriate flags.
|
||
//
|
||
|
||
#define NoOplocksHeld (NO_OPLOCK)
|
||
|
||
#define OplockIGranted (LEVEL_I_OPLOCK | EXCLUSIVE)
|
||
#define OpBatchGranted (BATCH_OPLOCK | EXCLUSIVE)
|
||
#define OpFilterGranted (FILTER_OPLOCK | EXCLUSIVE)
|
||
#define OpFilterReqPending (FILTER_OPLOCK | EXCLUSIVE | PENDING )
|
||
|
||
#define OplockBreakItoII (LEVEL_I_OPLOCK | EXCLUSIVE | BREAK_TO_II)
|
||
#define OpBatchBreaktoII (BATCH_OPLOCK | EXCLUSIVE | BREAK_TO_II)
|
||
#define OpFilterBreaktoII (FILTER_OPLOCK | EXCLUSIVE | BREAK_TO_II)
|
||
|
||
#define OplockBreakItoNone (LEVEL_I_OPLOCK | EXCLUSIVE | BREAK_TO_NONE)
|
||
#define OpBatchBreaktoNone (BATCH_OPLOCK | EXCLUSIVE | BREAK_TO_NONE)
|
||
#define OpFilterBreaktoNone (FILTER_OPLOCK | EXCLUSIVE | BREAK_TO_NONE)
|
||
|
||
#define OplockBreakItoIItoNone (LEVEL_I_OPLOCK | EXCLUSIVE | BREAK_TO_II_NONE)
|
||
#define OpBatchBreaktoIItoNone (BATCH_OPLOCK | EXCLUSIVE | BREAK_TO_II_NONE)
|
||
#define OpFilterBreaktoIItoNone (FILTER_OPLOCK | EXCLUSIVE | BREAK_TO_II_NONE)
|
||
|
||
#define OpBatchClosePending (BATCH_OPLOCK | EXCLUSIVE | CLOSE_PENDING)
|
||
#define OpFilterClosePending (FILTER_OPLOCK | EXCLUSIVE | CLOSE_PENDING)
|
||
|
||
#define OpFilterTimerStarted (FILTER_OPLOCK | EXCLUSIVE | PENDING | TIMER_STARTED)
|
||
|
||
#define OplockIIGranted (LEVEL_II_OPLOCK)
|
||
|
||
#define UninitializeOplock (UNINITIALIZE_OPLOCK)
|
||
|
||
//
|
||
// The oplock state is now just a ULONG.
|
||
//
|
||
|
||
typedef ULONG OPLOCK_STATE;
|
||
|
||
//
|
||
// The non-opaque definition of an OPLOCK is a pointer to a privately
|
||
// defined structure.
|
||
//
|
||
|
||
typedef struct _NONOPAQUE_OPLOCK {
|
||
|
||
//
|
||
// This is the Irp used to successfully request a level I oplock or
|
||
// batch oplock. It is completed to initiate the Oplock I break
|
||
// procedure.
|
||
//
|
||
|
||
PIRP IrpExclusiveOplock;
|
||
|
||
//
|
||
// This is a pointer to the original file object used when granting
|
||
// an Oplock I or batch oplock.
|
||
//
|
||
|
||
PFILE_OBJECT FileObject;
|
||
|
||
//
|
||
// The start of a linked list of Irps used to successfully request
|
||
// a level II oplock.
|
||
//
|
||
|
||
LIST_ENTRY IrpOplocksII;
|
||
|
||
//
|
||
// The following links the Irps waiting to be completed on a queue
|
||
// of Irps.
|
||
//
|
||
|
||
LIST_ENTRY WaitingIrps;
|
||
|
||
//
|
||
// Oplock state. This indicates the current oplock state.
|
||
//
|
||
|
||
OPLOCK_STATE OplockState;
|
||
|
||
//
|
||
// This FastMutex is used to control access to this structure.
|
||
//
|
||
|
||
PFAST_MUTEX FastMutex;
|
||
|
||
//
|
||
// This is the timer package to process an OpFilter break.
|
||
//
|
||
|
||
struct _OPFILTER_TIMER *OpFilter;
|
||
|
||
//
|
||
// Count of outstanding timers.
|
||
//
|
||
|
||
ULONG TimerCount;
|
||
|
||
} NONOPAQUE_OPLOCK, *PNONOPAQUE_OPLOCK;
|
||
|
||
//
|
||
// Each Waiting Irp record corresponds to an Irp that is waiting for an
|
||
// oplock break to be acknowledged and is maintained in a queue off of the
|
||
// Oplock's WaitingIrps list.
|
||
//
|
||
|
||
typedef struct _WAITING_IRP {
|
||
|
||
//
|
||
// The link structures for the list of waiting irps.
|
||
//
|
||
|
||
LIST_ENTRY Links;
|
||
|
||
//
|
||
// This is the Irp attached to this structure.
|
||
//
|
||
|
||
PIRP Irp;
|
||
|
||
//
|
||
// This is the routine to call when we are done with an Irp we
|
||
// held on a waiting queue. (We originally returned STATUS_PENDING).
|
||
//
|
||
|
||
POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine;
|
||
|
||
//
|
||
// The context field to use when we are done with the Irp.
|
||
//
|
||
|
||
PVOID Context;
|
||
|
||
//
|
||
// This points to an event object used when we do not want to
|
||
// give up this thread.
|
||
//
|
||
|
||
PKEVENT Event;
|
||
|
||
//
|
||
// This field contains a copy of the Irp Iosb.Information field.
|
||
// We copy it here so that we can store the Oplock address in the
|
||
// Irp.
|
||
//
|
||
|
||
ULONG Information;
|
||
|
||
} WAITING_IRP, *PWAITING_IRP;
|
||
|
||
//
|
||
// The following structure is used when timing out the OpFilter oplocks.
|
||
//
|
||
|
||
typedef struct _OPFILTER_TIMER {
|
||
|
||
KDPC OpFilterDpc;
|
||
KTIMER OpFilterTimer;
|
||
WORK_QUEUE_ITEM OpFilterItem;
|
||
PNONOPAQUE_OPLOCK Oplock;
|
||
|
||
} OPFILTER_TIMER, *POPFILTER_TIMER;
|
||
|
||
|
||
//
|
||
// Local support routines
|
||
//
|
||
|
||
PNONOPAQUE_OPLOCK
|
||
FsRtlAllocateOplock (
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlRequestExclusiveOplock (
|
||
IN OUT PNONOPAQUE_OPLOCK *Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp OPTIONAL,
|
||
IN OPLOCK_STATE NextOplockState
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlRequestOplockII (
|
||
IN OUT PNONOPAQUE_OPLOCK *Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlAcknowledgeOplockBreak (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp,
|
||
IN BOOLEAN GrantLevelII
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlOpBatchBreakClosePending (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlOplockBreakNotify (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
FsRtlOplockCleanup (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlOplockBreakToII (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp,
|
||
IN PVOID Context,
|
||
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
||
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlOplockBreakToNone (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp,
|
||
IN PVOID Context,
|
||
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
||
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL
|
||
);
|
||
|
||
VOID
|
||
FsRtlRemoveAndCompleteIrp (
|
||
IN PLIST_ENTRY Link
|
||
);
|
||
|
||
NTSTATUS
|
||
FsRtlWaitOnIrp (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIRP Irp,
|
||
IN PVOID Context,
|
||
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
||
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL,
|
||
IN PKEVENT Event
|
||
);
|
||
|
||
VOID
|
||
FsRtlCompletionRoutinePriv (
|
||
IN PVOID Context,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
FsRtlCancelWaitIrp (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
FsRtlCancelOplockIIIrp (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
FsRtlCancelExclusiveIrp (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
FsRtlRemoveAndCompleteWaitIrp (
|
||
IN PWAITING_IRP WaitingIrp
|
||
);
|
||
|
||
BOOLEAN
|
||
FsRtlCheckForMatchingFileObject (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLIST_ENTRY Link,
|
||
IN PLIST_ENTRY EndOfList,
|
||
OUT PLIST_ENTRY *MatchingLink
|
||
);
|
||
|
||
VOID
|
||
FsRtlNotifyCompletion (
|
||
IN PVOID Context,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
FsRtlOplockDpc (
|
||
IN PKDPC Dpc,
|
||
IN PVOID Context,
|
||
IN PVOID SystemArgument1,
|
||
IN PVOID SystemArgument2
|
||
);
|
||
|
||
VOID
|
||
FsRtlOpFilterWorkerRoutine (
|
||
IN PVOID Context
|
||
);
|
||
|
||
VOID
|
||
FsRtlInitializeOpFilter (
|
||
IN PNONOPAQUE_OPLOCK Oplock
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, FsRtlAllocateOplock)
|
||
#pragma alloc_text(PAGE, FsRtlCheckForMatchingFileObject)
|
||
#pragma alloc_text(PAGE, FsRtlCompletionRoutinePriv)
|
||
#pragma alloc_text(PAGE, FsRtlCurrentBatchOplock)
|
||
#pragma alloc_text(PAGE, FsRtlInitializeOpFilter)
|
||
#pragma alloc_text(PAGE, FsRtlInitializeOplock)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyCompletion)
|
||
#pragma alloc_text(PAGE, FsRtlOpBatchBreakClosePending)
|
||
#pragma alloc_text(PAGE, FsRtlOpFilterWorkerRoutine)
|
||
#pragma alloc_text(PAGE, FsRtlOplockBreakNotify)
|
||
#pragma alloc_text(PAGE, FsRtlOplockFsctrl)
|
||
#pragma alloc_text(PAGE, FsRtlOplockIsFastIoPossible)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
FsRtlInitializeOplock (
|
||
IN OUT POPLOCK Oplock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes a new OPLOCK structure. This call must
|
||
precede any other call to this entry point with this OPLOCK
|
||
structure. In addition, this routine will have exclusive access
|
||
to the Oplock structure.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies the address of an opaque OPLOCK structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER( Oplock );
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FsRtlInitializeOplock: Oplock -> %08lx\n", *Oplock );
|
||
|
||
//
|
||
// No action is taken at this time.
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "FsRtlInitializeOplock: Exit\n", 0);
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FsRtlUninitializeOplock (
|
||
IN OUT POPLOCK Oplock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uninitializes an OPLOCK structure. After calling this
|
||
routine, the OPLOCK structure must be reinitialized before being
|
||
used again.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies the address of an opaque OPLOCK structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
PNONOPAQUE_OPLOCK ThisOplock;
|
||
|
||
DebugTrace(+1, Dbg, "FsRtlUninitializeOplock: Oplock -> %08lx\n", *Oplock );
|
||
|
||
//
|
||
// If the Oplock structure has not been allocated, there is no action
|
||
// to take.
|
||
//
|
||
|
||
if (*Oplock != NULL) {
|
||
|
||
//
|
||
// Remove this from the user's structure.
|
||
//
|
||
|
||
ThisOplock = (PNONOPAQUE_OPLOCK) *Oplock;
|
||
|
||
*Oplock = NULL;
|
||
|
||
//
|
||
// Grab the waiting lock queue mutex to exclude anyone from messing
|
||
// with the queue while we're using it
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( ThisOplock->FastMutex );
|
||
|
||
//
|
||
// If there are still timers outstanding then leave this to be
|
||
// done when the last timer times out.
|
||
//
|
||
|
||
if (ThisOplock->TimerCount == 0) {
|
||
|
||
try {
|
||
|
||
PIRP Irp;
|
||
|
||
//
|
||
// Release any waiting Irps held.
|
||
//
|
||
|
||
while (!IsListEmpty( &ThisOplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
PIRP ThisIrp;
|
||
|
||
WaitingIrp = CONTAINING_RECORD( ThisOplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
RemoveHeadList( &ThisOplock->WaitingIrps );
|
||
|
||
ThisIrp = WaitingIrp->Irp;
|
||
|
||
IoAcquireCancelSpinLock( &ThisIrp->CancelIrql );
|
||
|
||
IoSetCancelRoutine( ThisIrp, NULL );
|
||
IoReleaseCancelSpinLock( ThisIrp->CancelIrql );
|
||
|
||
ThisIrp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// Call the completion routine in the Waiting Irp.
|
||
//
|
||
|
||
WaitingIrp->CompletionRoutine( WaitingIrp->Context,
|
||
WaitingIrp->Irp );
|
||
|
||
ExFreePool( WaitingIrp );
|
||
}
|
||
|
||
//
|
||
// Release any oplock II irps held.
|
||
//
|
||
|
||
while (!IsListEmpty( &ThisOplock->IrpOplocksII )) {
|
||
|
||
Irp = CONTAINING_RECORD( ThisOplock->IrpOplocksII.Flink,
|
||
IRP,
|
||
Tail.Overlay.ListEntry );
|
||
|
||
RemoveHeadList( &ThisOplock->IrpOplocksII );
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// Complete the oplock II Irp.
|
||
//
|
||
|
||
ObDereferenceObject( IoGetCurrentIrpStackLocation( Irp )->FileObject );
|
||
|
||
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
FsRtlCompleteRequest( Irp, STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// Release any exclusive oplock held.
|
||
//
|
||
|
||
if (ThisOplock->IrpExclusiveOplock != NULL) {
|
||
|
||
Irp = ThisOplock->IrpExclusiveOplock;
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
FsRtlCompleteRequest( Irp, STATUS_SUCCESS );
|
||
|
||
ThisOplock->IrpExclusiveOplock = NULL;
|
||
|
||
if (ThisOplock->FileObject != NULL) {
|
||
|
||
ObDereferenceObject( ThisOplock->FileObject );
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// No matter how we complete the preceding statements we will
|
||
// now release the waiting lock queue mutex
|
||
//
|
||
|
||
ExReleaseFastMutexUnsafe( ThisOplock->FastMutex );
|
||
}
|
||
|
||
//
|
||
// Deallocate the mutex.
|
||
//
|
||
|
||
ExFreePool( ThisOplock->FastMutex );
|
||
|
||
//
|
||
// Deallocate the Oplock structure.
|
||
//
|
||
|
||
ExFreePool( ThisOplock );
|
||
|
||
} else {
|
||
|
||
ThisOplock->OplockState = UninitializeOplock;
|
||
ExReleaseFastMutexUnsafe( ThisOplock->FastMutex );
|
||
}
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlUninitializeOplock: Exit\n", 0 );
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FsRtlOplockFsctrl (
|
||
IN POPLOCK Oplock,
|
||
IN PIRP Irp,
|
||
IN ULONG OpenCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the interface with the filesystems for Fsctl calls, it handles
|
||
oplock requests, break acknowledgement and break notify.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies the address of the opaque OPLOCK structure.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
OpenCount - This is the number of user handles on the file if we are requsting
|
||
an exclusive oplock. A non-zero value for a level II request indicates
|
||
that there are locks on the file.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Returns the result of this operation. If this is an Oplock
|
||
request which is granted, then STATUS_PENDING is returned.
|
||
If the Oplock isn't granted then STATUS_OPLOCK_NOT_GRANTED
|
||
is returned. If this is an Oplock I break to no oplock,
|
||
then STATUS_SUCCESS. If this is an Oplock I break to
|
||
Oplock II then STATUS_PENDING is returned. Other
|
||
error codes returned depend on the nature of the error.
|
||
|
||
STATUS_CANCELLED is returned if the Irp is cancelled during
|
||
this operation.
|
||
|
||
STATUS_SUCCESS is returned if this is a create asking for
|
||
a filter oplock.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
OPLOCK_STATE OplockState;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the current IRP stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "FsRtlOplockFsctrl: Entered\n", 0);
|
||
DebugTrace( 0, Dbg, "FsRtlOplockFsctrl: Oplock -> %08lx\n", *Oplock );
|
||
DebugTrace( 0, Dbg, "FsRtlOplockFsctrl: Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// Check if this is the create case where the user is requesting a pending
|
||
// filter oplock.
|
||
//
|
||
|
||
if (IrpSp->MajorFunction == IRP_MJ_CREATE) {
|
||
|
||
//
|
||
// Check that all the conditions hold to grant this oplock.
|
||
// The conditions that must hold are:
|
||
//
|
||
// - This is the only opener of the file.
|
||
// - Desired Access must be exactly FILE_READ_ATTRIBUTES.
|
||
// This will insure an asynch open since the SYNCHRONIZE
|
||
// flag can't be set.
|
||
// - Share access is precisely
|
||
// (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
|
||
//
|
||
|
||
if ((OpenCount != 1) ||
|
||
(FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess,
|
||
~(FILE_READ_ATTRIBUTES))) ||
|
||
((IrpSp->Parameters.Create.ShareAccess &
|
||
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)) !=
|
||
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE))) {
|
||
|
||
Status = STATUS_OPLOCK_NOT_GRANTED;
|
||
|
||
} else {
|
||
|
||
Status = FsRtlRequestExclusiveOplock( (PNONOPAQUE_OPLOCK *) Oplock,
|
||
IrpSp,
|
||
NULL,
|
||
OpFilterReqPending );
|
||
}
|
||
|
||
//
|
||
// Case on the FsControlFile code control code.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// Assume this is an OplockLevel I.
|
||
//
|
||
// NOTE - This code depends on the defined bits for these oplock types.
|
||
// FILTER_OPLOCK = 4 * LEVEL_I_OPLOCK
|
||
// BATCH_OPLOCK = 2 * LEVEL_I_OPLOCK
|
||
//
|
||
|
||
OplockState = LEVEL_I_OPLOCK;
|
||
|
||
switch (IrpSp->Parameters.FileSystemControl.FsControlCode) {
|
||
|
||
case FSCTL_REQUEST_FILTER_OPLOCK :
|
||
|
||
OplockState *= 2;
|
||
|
||
case FSCTL_REQUEST_BATCH_OPLOCK :
|
||
|
||
OplockState *= 2;
|
||
|
||
case FSCTL_REQUEST_OPLOCK_LEVEL_1 :
|
||
|
||
//
|
||
// Set the other flags for an exclusive oplock.
|
||
//
|
||
|
||
SetFlag( OplockState, EXCLUSIVE );
|
||
|
||
//
|
||
// We short circuit the request if this request is treated
|
||
// synchronously or the open count is not 1. Otherwise the Io system
|
||
// will hold the return code until the Irp is completed.
|
||
//
|
||
// If cleanup has occurrred on this file, then we refuse
|
||
// the oplock request.
|
||
//
|
||
|
||
if ((OpenCount != 1) ||
|
||
IoIsOperationSynchronous( Irp ) ||
|
||
FlagOn( IrpSp->FileObject->Flags, FO_CLEANUP_COMPLETE )) {
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_OPLOCK_NOT_GRANTED );
|
||
Status = STATUS_OPLOCK_NOT_GRANTED;
|
||
|
||
} else {
|
||
|
||
Status = FsRtlRequestExclusiveOplock( (PNONOPAQUE_OPLOCK *) Oplock,
|
||
IrpSp,
|
||
Irp,
|
||
OplockState );
|
||
}
|
||
|
||
break;
|
||
|
||
case FSCTL_REQUEST_OPLOCK_LEVEL_2 :
|
||
|
||
//
|
||
// We short circuit the request if this request is treated
|
||
// synchronously. Otherwise the Io system will hold the return
|
||
// code until the Irp is completed.
|
||
//
|
||
// If cleanup has occurrred on this file, then we refuse
|
||
// the oplock request.
|
||
//
|
||
// A non-zero open count in this case indicates that there are
|
||
// file locks on the file. We will also fail the request in
|
||
// this case.
|
||
//
|
||
|
||
if ((OpenCount != 0) ||
|
||
IoIsOperationSynchronous( Irp ) ||
|
||
FlagOn( IrpSp->FileObject->Flags, FO_CLEANUP_COMPLETE )) {
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_OPLOCK_NOT_GRANTED );
|
||
Status = STATUS_OPLOCK_NOT_GRANTED;
|
||
|
||
} else {
|
||
|
||
Status = FsRtlRequestOplockII( (PNONOPAQUE_OPLOCK *) Oplock,
|
||
IrpSp,
|
||
Irp );
|
||
}
|
||
|
||
break;
|
||
|
||
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE :
|
||
|
||
Status = FsRtlAcknowledgeOplockBreak( (PNONOPAQUE_OPLOCK) *Oplock,
|
||
IrpSp,
|
||
Irp,
|
||
TRUE );
|
||
break;
|
||
|
||
case FSCTL_OPLOCK_BREAK_ACK_NO_2 :
|
||
|
||
Status = FsRtlAcknowledgeOplockBreak( (PNONOPAQUE_OPLOCK) *Oplock,
|
||
IrpSp,
|
||
Irp,
|
||
FALSE );
|
||
break;
|
||
|
||
case FSCTL_OPBATCH_ACK_CLOSE_PENDING :
|
||
|
||
Status = FsRtlOpBatchBreakClosePending( (PNONOPAQUE_OPLOCK) *Oplock,
|
||
IrpSp,
|
||
Irp );
|
||
break;
|
||
|
||
case FSCTL_OPLOCK_BREAK_NOTIFY :
|
||
|
||
Status = FsRtlOplockBreakNotify( (PNONOPAQUE_OPLOCK) *Oplock,
|
||
IrpSp,
|
||
Irp );
|
||
break;
|
||
|
||
default :
|
||
|
||
DebugTrace( 0,
|
||
Dbg,
|
||
"Invalid Control Code\n",
|
||
0);
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FsRtlOplockFsctrl: Exit -> %08lx\n", Status );
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FsRtlCheckOplock (
|
||
IN POPLOCK Oplock,
|
||
IN PIRP Irp,
|
||
IN PVOID Context,
|
||
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
||
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called as a support routine from a file system.
|
||
It is used to synchronize I/O requests with the current Oplock
|
||
state of a file. If the I/O operation will cause the Oplock to
|
||
break, that action is initiated. If the operation cannot continue
|
||
until the Oplock break is complete, STATUS_PENDING is returned and
|
||
the caller supplied routine is called.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Context - This value is passed as a parameter to the completion routine.
|
||
|
||
CompletionRoutine - This is the routine which is called if this
|
||
Irp must wait for an Oplock to break. This
|
||
is a synchronous operation if not specified
|
||
and we block in this thread waiting on
|
||
an event.
|
||
|
||
PostIrpRoutine - This is the routine to call before we put anything
|
||
on our waiting Irp queue.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we can complete the operation on exiting this thread.
|
||
STATUS_PENDING if we return here but hold the Irp.
|
||
STATUS_CANCELLED if the Irp is cancelled before we return.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PNONOPAQUE_OPLOCK ThisOplock = *Oplock;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCheckOplock: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// If there is no oplock structure or this is system I/O, we allow
|
||
// the operation to continue. Otherwise we check the major function code.
|
||
//
|
||
|
||
if ((ThisOplock != NULL) &&
|
||
!FlagOn( Irp->Flags, IRP_PAGING_IO )) {
|
||
|
||
OPLOCK_STATE OplockState;
|
||
PFILE_OBJECT OplockFileObject;
|
||
|
||
BOOLEAN BreakToII;
|
||
BOOLEAN BreakToNone;
|
||
|
||
ULONG CreateDisposition;
|
||
|
||
//
|
||
// Capture the file object first and then the oplock state to perform
|
||
// the unsafe checks below. We capture the file object first in case
|
||
// there is an exclusive oplock break in progress. Otherwise the oplock
|
||
// state may indicate break in progress but it could complete by
|
||
// the time we snap the file object.
|
||
//
|
||
|
||
OplockFileObject = ThisOplock->FileObject;
|
||
OplockState = ThisOplock->OplockState;
|
||
|
||
//
|
||
// Examine the Irp for the appropriate action provided there are
|
||
// current oplocks on the file.
|
||
//
|
||
|
||
if (OplockState != NoOplocksHeld) {
|
||
|
||
BreakToII = FALSE;
|
||
BreakToNone = FALSE;
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Determine whether we are going to BreakToII or BreakToNone.
|
||
//
|
||
|
||
switch (IrpSp->MajorFunction) {
|
||
|
||
case IRP_MJ_CREATE :
|
||
|
||
//
|
||
// If we are opening for attribute access only, we
|
||
// return status success. Always break the oplock if this caller
|
||
// wants a filter oplock.
|
||
//
|
||
|
||
if (!FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess,
|
||
~(FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE) ) &&
|
||
!FlagOn( IrpSp->Parameters.Create.Options, FILE_RESERVE_OPFILTER )) {
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If there is a filter oplock granted and this create iS reading
|
||
// the file then don't break the oplock as long as we share
|
||
// for reads.
|
||
//
|
||
|
||
if (FlagOn( OplockState, FILTER_OPLOCK ) &&
|
||
!FlagOn( IrpSp->Parameters.Create.SecurityContext->DesiredAccess,
|
||
~FILTER_OPLOCK_VALID_FLAGS ) &&
|
||
FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ )) {
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We we are superseding or overwriting, then break to none.
|
||
//
|
||
|
||
CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
|
||
|
||
if ((CreateDisposition == FILE_SUPERSEDE) ||
|
||
(CreateDisposition == FILE_OVERWRITE) ||
|
||
(CreateDisposition == FILE_OVERWRITE_IF) ||
|
||
FlagOn( IrpSp->Parameters.Create.Options, FILE_RESERVE_OPFILTER )) {
|
||
|
||
BreakToNone = TRUE;
|
||
|
||
} else {
|
||
|
||
BreakToII = TRUE;
|
||
}
|
||
|
||
break;
|
||
|
||
case IRP_MJ_READ :
|
||
|
||
//
|
||
// If a filter oplock has been granted then do nothing.
|
||
// We will assume the oplock will have been broken
|
||
// if this create needed to do that.
|
||
//
|
||
|
||
if (!FlagOn( OplockState, FILTER_OPLOCK )) {
|
||
|
||
BreakToII = TRUE;
|
||
}
|
||
|
||
break;
|
||
|
||
case IRP_MJ_FLUSH_BUFFERS :
|
||
|
||
BreakToII = TRUE;
|
||
break;
|
||
|
||
case IRP_MJ_CLEANUP :
|
||
|
||
FsRtlOplockCleanup( (PNONOPAQUE_OPLOCK) *Oplock,
|
||
IrpSp );
|
||
|
||
break;
|
||
|
||
case IRP_MJ_LOCK_CONTROL :
|
||
|
||
//
|
||
// If a filter oplock has been granted then do nothing.
|
||
// We will assume the oplock will have been broken
|
||
// if this create needed to do that.
|
||
//
|
||
|
||
if (FlagOn( OplockState, FILTER_OPLOCK )) {
|
||
|
||
break;
|
||
}
|
||
|
||
case IRP_MJ_WRITE :
|
||
|
||
BreakToNone = TRUE;
|
||
break;
|
||
|
||
case IRP_MJ_SET_INFORMATION :
|
||
|
||
//
|
||
// We are only interested in calls that shrink the file size
|
||
// or breaking batch oplocks for the rename case.
|
||
//
|
||
|
||
switch (IrpSp->Parameters.SetFile.FileInformationClass) {
|
||
|
||
case FileEndOfFileInformation :
|
||
|
||
//
|
||
// Break immediately if this is the lazy writer callback.
|
||
//
|
||
|
||
if (IrpSp->Parameters.SetFile.AdvanceOnly) {
|
||
|
||
break;
|
||
}
|
||
|
||
case FileAllocationInformation :
|
||
|
||
BreakToNone = TRUE;
|
||
break;
|
||
|
||
case FileRenameInformation :
|
||
case FileLinkInformation :
|
||
|
||
if (FlagOn( OplockState, BATCH_OPLOCK | FILTER_OPLOCK )) {
|
||
|
||
BreakToNone = TRUE;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (BreakToII) {
|
||
|
||
//
|
||
// If there are no outstanding oplocks or level II oplocks are held,
|
||
// we can return immediately. If the first two tests fail then there
|
||
// is an exclusive oplock. If the file objects match we allow the
|
||
// operation to continue.
|
||
//
|
||
|
||
if ((OplockState != OplockIIGranted) &&
|
||
(OplockFileObject != IrpSp->FileObject)) {
|
||
|
||
Status = FsRtlOplockBreakToII( (PNONOPAQUE_OPLOCK) *Oplock,
|
||
IrpSp,
|
||
Irp,
|
||
Context,
|
||
CompletionRoutine,
|
||
PostIrpRoutine );
|
||
}
|
||
|
||
} else if (BreakToNone) {
|
||
|
||
//
|
||
// If there are no oplocks, we can return immediately.
|
||
// Otherwise if there is no level 2 oplock and this file
|
||
// object matches the owning file object then this write is
|
||
// on behalf of the owner of the oplock.
|
||
//
|
||
|
||
if ((OplockState == OplockIIGranted) ||
|
||
(OplockFileObject != IrpSp->FileObject)) {
|
||
|
||
Status = FsRtlOplockBreakToNone( (PNONOPAQUE_OPLOCK) *Oplock,
|
||
IrpSp,
|
||
Irp,
|
||
Context,
|
||
CompletionRoutine,
|
||
PostIrpRoutine );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlCheckOplock: Exit -> %08lx\n", Status );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FsRtlOplockIsFastIoPossible (
|
||
IN POPLOCK Oplock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine indicates to the caller where there are any outstanding
|
||
oplocks which prevent fast Io from happening.
|
||
|
||
Arguments:
|
||
|
||
OpLock - Supplies the oplock being queried
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if there are outstanding oplocks and FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN FastIoPossible = TRUE;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FsRtlOplockIsFastIoPossible: Oplock -> %08lx\n", *Oplock);
|
||
|
||
//
|
||
// There are not any current oplocks if the variable is null or
|
||
// the state is no oplocks held. If an exclusive oplock was granted
|
||
// but no break is in progress then allow the Fast IO.
|
||
//
|
||
|
||
if (*Oplock != NULL) {
|
||
|
||
OPLOCK_STATE OplockState;
|
||
|
||
OplockState = ((PNONOPAQUE_OPLOCK) *Oplock)->OplockState;
|
||
|
||
if (FlagOn( OplockState, LEVEL_II_OPLOCK | OPLOCK_BREAK_MASK )) {
|
||
|
||
FastIoPossible = FALSE;
|
||
}
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FsRtlOplockIsFastIoPossible: Exit -> %08lx\n", FastIoPossible);
|
||
|
||
return FastIoPossible;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FsRtlCurrentBatchOplock (
|
||
IN POPLOCK Oplock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines indicates whether there are current outstanding
|
||
batch oplocks.
|
||
|
||
Arguments:
|
||
|
||
OpLock - Supplies the oplock being queried
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if there are outstanding batch oplocks and FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN BatchOplocks = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FsRtlCurrentBatchOplock: Oplock -> %08lx\n", *Oplock);
|
||
|
||
//
|
||
// There are not any current oplocks if the variable is null or
|
||
// the state is no oplocks held. We check whether there are batch
|
||
// oplocks or filter oplocks which have not been broken.
|
||
//
|
||
|
||
if ((*Oplock != NULL) &&
|
||
FlagOn( ((PNONOPAQUE_OPLOCK) *Oplock)->OplockState,
|
||
BATCH_OPLOCK | FILTER_OPLOCK )) {
|
||
|
||
BatchOplocks = TRUE;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FsRtlCurrentBatchOplock: Exit -> %08lx\n", BatchOplocks);
|
||
|
||
return BatchOplocks;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
PNONOPAQUE_OPLOCK
|
||
FsRtlAllocateOplock (
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize and allocate an opaque oplock
|
||
structure. After allocation, the two events are set to the signalled
|
||
state. The oplock state is set to NoOplocksHeld and the other
|
||
fields are filled with zeroes.
|
||
|
||
If the allocation fails, the appropriate status is raised.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
PNONOPAQUE_OPLOCK - A pointer to the allocated structure.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNONOPAQUE_OPLOCK NewOplock = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlAllocateOplock: Entered\n", 0);
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Raise an error status if the allocation is unsuccessful.
|
||
// The structure is allocated out of non-paged pool.
|
||
//
|
||
|
||
NewOplock = FsRtlAllocatePool( PagedPool, sizeof( NONOPAQUE_OPLOCK ));
|
||
|
||
RtlZeroMemory( NewOplock, sizeof( NONOPAQUE_OPLOCK ));
|
||
|
||
NewOplock->FastMutex = FsRtlAllocatePool( NonPagedPool, sizeof( FAST_MUTEX ));
|
||
|
||
ExInitializeFastMutex( NewOplock->FastMutex );
|
||
|
||
InitializeListHead( &NewOplock->IrpOplocksII );
|
||
InitializeListHead( &NewOplock->WaitingIrps );
|
||
|
||
NewOplock->OplockState = NoOplocksHeld;
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Cleanup the oplock if abnormal termination.
|
||
//
|
||
|
||
if (AbnormalTermination() && NewOplock != NULL) {
|
||
|
||
ExFreePool( NewOplock );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "GetOplockStructure: Exit -> %08lx\n", NewOplock);
|
||
}
|
||
|
||
return NewOplock;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlRequestExclusiveOplock (
|
||
IN OUT PNONOPAQUE_OPLOCK *Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp OPTIONAL,
|
||
IN OPLOCK_STATE NextOplockState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called whenever a user is requesting either a batch/filter
|
||
oplock or a level I oplock. The request is granted if there are currently
|
||
no oplocks on the file or we are completing the filter oplock request.
|
||
|
||
NOTE - We already know that the open count on this file is exactly one.
|
||
If the caller is requesting a PendingFilter Oplock then the state
|
||
must be NoOplockHeld.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation. This is not specified if we are granting a pending
|
||
filter oplock (during a create).
|
||
|
||
NextOplockState - Indicates the type of oplock being requested.
|
||
|
||
Return Value:
|
||
|
||
STATUS_PENDING if the oplock is granted (although it may be immediately cancelled).
|
||
STATUS_SUCCESS if a pending filter oplock is requested and tentatively granted.
|
||
STATUS_OPLOCK_NOT_GRANTED if the request is denied.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PNONOPAQUE_OPLOCK ThisOplock;
|
||
|
||
BOOLEAN AcquiredMutex;
|
||
BOOLEAN BreakOpFilter = FALSE;
|
||
|
||
PLIST_ENTRY Link;
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlRequestExclusiveOplock: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
DebugTrace( 0, Dbg, "BatchOplock -> %01x\n", BatchOplock );
|
||
|
||
//
|
||
// We can grant the oplock if no one else owns a level I or level II
|
||
// oplock on this file. If the oplock pointer is NULL then there
|
||
// are no oplocks on the file. Otherwise we need to check the
|
||
// oplock state in an existing oplock structure.
|
||
//
|
||
|
||
if (*Oplock == NULL) {
|
||
|
||
DebugTrace( 0,
|
||
Dbg,
|
||
"Oplock currently not allocated\n",
|
||
0);
|
||
|
||
ThisOplock = FsRtlAllocateOplock();
|
||
*Oplock = ThisOplock;
|
||
|
||
} else {
|
||
|
||
ThisOplock = *Oplock;
|
||
}
|
||
|
||
//
|
||
// Grab the synchronization object for the oplock.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( ThisOplock->FastMutex );
|
||
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If we are requesting a PendingFilter Oplock then it must be
|
||
// safe to grant. There is only one open handle and we are in
|
||
// the process of opening it.
|
||
//
|
||
|
||
if (NextOplockState == OpFilterReqPending) {
|
||
|
||
ASSERT( FlagOn( ThisOplock->OplockState, NO_OPLOCK | PENDING ));
|
||
|
||
ThisOplock->IrpExclusiveOplock = Irp;
|
||
ThisOplock->FileObject = IrpSp->FileObject;
|
||
ThisOplock->OpFilter = NULL;
|
||
|
||
ThisOplock->OplockState = OpFilterReqPending;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// If the current oplock state is no oplocks held then we
|
||
// will grant the oplock to this requestor. If the state is
|
||
// either of the OpFilter states then also grant the request.
|
||
// We won't check for a matching file object because there can
|
||
// only be one file object. Grant the request anyway.
|
||
//
|
||
// If the current state is OplockII granted then it must
|
||
// be owned by this request. Break the oplock II and grant
|
||
// the exclusive lock.
|
||
//
|
||
|
||
} else if (FlagOn( ThisOplock->OplockState,
|
||
LEVEL_II_OPLOCK | NO_OPLOCK | PENDING )) {
|
||
|
||
PFAST_MUTEX OplockFastMutex;
|
||
|
||
if (ThisOplock->OplockState == OplockIIGranted) {
|
||
|
||
ASSERT( ThisOplock->IrpOplocksII.Flink == ThisOplock->IrpOplocksII.Blink );
|
||
|
||
FsRtlRemoveAndCompleteIrp( ThisOplock->IrpOplocksII.Flink );
|
||
}
|
||
|
||
//
|
||
// Put the address of the fast mutex on the stack.
|
||
//
|
||
|
||
OplockFastMutex = ThisOplock->FastMutex;
|
||
|
||
//
|
||
// We store this Irp in the Oplocks structure.
|
||
// We set the oplock state to the correct exclusive oplock.
|
||
//
|
||
|
||
ThisOplock->IrpExclusiveOplock = Irp;
|
||
ThisOplock->FileObject = IrpSp->FileObject;
|
||
ThisOplock->OplockState = NextOplockState;
|
||
ThisOplock->OpFilter = NULL;
|
||
|
||
IoMarkIrpPending( Irp );
|
||
|
||
ObReferenceObject( IrpSp->FileObject );
|
||
|
||
Irp->IoStatus.Information = (ULONG) ThisOplock;
|
||
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
|
||
//
|
||
// Now if the irp is cancelled then we'll call the cancel
|
||
// routine right now to do away with the irp, otherwise
|
||
// we set the cancel routine
|
||
//
|
||
|
||
if (Irp->Cancel) {
|
||
|
||
AcquiredMutex = FALSE;
|
||
|
||
ExReleaseFastMutexUnsafe( OplockFastMutex );
|
||
|
||
FsRtlCancelExclusiveIrp( NULL, Irp );
|
||
|
||
} else {
|
||
|
||
IoSetCancelRoutine( Irp, FsRtlCancelExclusiveIrp );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
}
|
||
|
||
Status = STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We'll complete the Irp with the Oplock not granted message
|
||
// and return that value as a status.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Irp )) {
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_OPLOCK_NOT_GRANTED );
|
||
}
|
||
|
||
Status = STATUS_OPLOCK_NOT_GRANTED;
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Give up the oplock synchronization object.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( ThisOplock->FastMutex );
|
||
}
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlRequestExclusiveOplock: Exit\n", 0 );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlRequestOplockII (
|
||
IN OUT PNONOPAQUE_OPLOCK *Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a user is requesting an Oplock II on an
|
||
open file. The request is granted if there are currently no
|
||
level 1 oplocks on the file and an oplock break is not in progress.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Return Value:
|
||
|
||
STATUS_PENDING if the oplock is granted.
|
||
STATUS_OPLOCK_NOT_GRANTED if the request is denied.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PNONOPAQUE_OPLOCK ThisOplock;
|
||
|
||
BOOLEAN AcquiredMutex;
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlRequestOplockII: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// We can grant the oplock if no one else owns a level I
|
||
// oplock on this file. If the oplock pointer is NULL then there
|
||
// are no oplocks on the file. Otherwise we need to check the
|
||
// oplock state in an existing oplock structure.
|
||
//
|
||
|
||
if (*Oplock == NULL) {
|
||
|
||
DebugTrace( 0,
|
||
Dbg,
|
||
"Oplock currently not allocated\n",
|
||
0);
|
||
|
||
ThisOplock = FsRtlAllocateOplock();
|
||
*Oplock = ThisOplock;
|
||
|
||
} else {
|
||
|
||
ThisOplock = *Oplock;
|
||
}
|
||
|
||
//
|
||
// Grab the synchronization object for the oplock.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( ThisOplock->FastMutex );
|
||
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If the current oplock state is no oplocks held or OplockIIGranted
|
||
// then we will grant the oplock to this requestor.
|
||
//
|
||
|
||
if (FlagOn( ThisOplock->OplockState, NO_OPLOCK | LEVEL_II_OPLOCK )) {
|
||
|
||
PFAST_MUTEX OplockFastMutex = ThisOplock->FastMutex;
|
||
|
||
//
|
||
// We store this Irp in the Oplocks structure.
|
||
// We set the oplock state to 'OplockIIGranted'.
|
||
//
|
||
|
||
IoMarkIrpPending( Irp );
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
InsertHeadList( &ThisOplock->IrpOplocksII,
|
||
&Irp->Tail.Overlay.ListEntry );
|
||
|
||
Irp->IoStatus.Information = (ULONG) ThisOplock;
|
||
|
||
ThisOplock->OplockState = OplockIIGranted;
|
||
|
||
ObReferenceObject( IrpSp->FileObject );
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
|
||
//
|
||
// Now if the irp is cancelled then we'll call the cancel
|
||
// routine right now to do away with the irp, otherwise
|
||
// we set the cancel routine
|
||
//
|
||
|
||
if (Irp->Cancel) {
|
||
|
||
AcquiredMutex = FALSE;
|
||
|
||
ExReleaseFastMutexUnsafe( OplockFastMutex );
|
||
|
||
FsRtlCancelOplockIIIrp( NULL, Irp );
|
||
|
||
} else {
|
||
|
||
IoSetCancelRoutine( Irp, FsRtlCancelOplockIIIrp );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
}
|
||
|
||
Status = STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We'll complete the Irp with the Oplock not granted message
|
||
// and return that value as a status.
|
||
//
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_OPLOCK_NOT_GRANTED );
|
||
Status = STATUS_OPLOCK_NOT_GRANTED;
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Give up the oplock synchronization object.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( ThisOplock->FastMutex );
|
||
}
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlRequestOplockII: Exit\n", 0 );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlAcknowledgeOplockBreak (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp,
|
||
IN BOOLEAN GrantLevelII
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a user is acknowledging an Oplock I
|
||
break. If the level 1 oplock was being broken to level 2, then
|
||
a check is made to insure that the level 2 has not been broken
|
||
in the meantime.
|
||
|
||
If an oplock 1 break is not in progress then this will be treated
|
||
as an asynchronous break request. If this is an asynchronous break
|
||
request and the file object owns an outstanding level 1 oplock, then
|
||
the oplock will be broken at this point.
|
||
|
||
A spurious break request via a file object which does not (or did not)
|
||
own the level 1 oplock will generate a warning but will not affect
|
||
the oplock state.
|
||
|
||
At the end of an Oplock I break, all of the waiting irps are completed.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
GrantLevelII - Indicates that this caller wants a level II oplock left
|
||
on the file.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we can complete the operation on exiting this thread.
|
||
STATUS_CANCELLED if the Irp is cancelled before we return.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN AcquiredMutex;
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlAcknowledgeOplockBreak: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// If there is no oplock structure, we complete this with invalid
|
||
// oplock protocol.
|
||
//
|
||
|
||
if (Oplock == NULL) {
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_INVALID_OPLOCK_PROTOCOL );
|
||
DebugTrace( -1, Dbg, "FsRtlAcknowledgeOplockBreak: Exit -> %08lx\n", STATUS_INVALID_OPLOCK_PROTOCOL );
|
||
return STATUS_INVALID_OPLOCK_PROTOCOL;
|
||
}
|
||
|
||
//
|
||
// Grab the synchronization object for the oplock.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
BOOLEAN DereferenceFileObject = TRUE;
|
||
|
||
//
|
||
// If a break is underway but this is not the owner of the
|
||
// level 1 oplock, we complete the request and return a
|
||
// warning.
|
||
//
|
||
|
||
if (Oplock->FileObject != IrpSp->FileObject) {
|
||
|
||
Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Not oplock owner -> %08lx\n",
|
||
Status);
|
||
|
||
FsRtlCompleteRequest( Irp, Status );
|
||
try_return( Status );
|
||
}
|
||
|
||
//
|
||
// If the user would like a level II and we are breaking to level II
|
||
// then grant the oplock.
|
||
//
|
||
|
||
if (GrantLevelII &&
|
||
FlagOn( Oplock->OplockState, BREAK_TO_II )) {
|
||
|
||
DebugTrace(0, Dbg, "OplockItoII\n", 0);
|
||
|
||
//
|
||
// If the acknowledgement is a synchronous request, we will
|
||
// break the oplock to none since the Io system will not
|
||
// allow this request to complete.
|
||
//
|
||
|
||
if (IoIsOperationSynchronous( Irp )) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Synchronous acknowledgement, break to no oplock\n",
|
||
0);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
FsRtlCompleteRequest( Irp, Status );
|
||
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
|
||
//
|
||
// We need to add this Irp to the oplock II queue, change
|
||
// the oplock state to Oplock II granted and set the
|
||
// return value to STATUS_PENDING.
|
||
//
|
||
|
||
} else {
|
||
|
||
PFAST_MUTEX OplockFastMutex = Oplock->FastMutex;
|
||
|
||
IoMarkIrpPending( Irp );
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
InsertHeadList( &Oplock->IrpOplocksII,
|
||
&Irp->Tail.Overlay.ListEntry );
|
||
|
||
DereferenceFileObject = FALSE;
|
||
|
||
Oplock->OplockState = OplockIIGranted;
|
||
|
||
Irp->IoStatus.Information = (ULONG) Oplock;
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
|
||
//
|
||
// Now if the irp is cancelled then we'll call the cancel
|
||
// routine right now to do away with the irp, otherwise
|
||
// we set the cancel routine
|
||
//
|
||
|
||
if (Irp->Cancel) {
|
||
|
||
AcquiredMutex = FALSE;
|
||
|
||
ExReleaseFastMutexUnsafe( OplockFastMutex );
|
||
|
||
FsRtlCancelOplockIIIrp( NULL, Irp );
|
||
|
||
} else {
|
||
|
||
IoSetCancelRoutine( Irp, FsRtlCancelOplockIIIrp );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
}
|
||
|
||
Status = STATUS_PENDING;
|
||
}
|
||
|
||
//
|
||
// We will break to none since this is the expected case for these
|
||
// cases.
|
||
//
|
||
|
||
} else if (FlagOn( Oplock->OplockState, BREAK_TO_II | BREAK_TO_NONE )) {
|
||
|
||
//
|
||
// We need to complete this Irp and return STATUS_SUCCESS.
|
||
// We also set the oplock state to no oplocks held.
|
||
//
|
||
|
||
DebugTrace(0, Dbg, "OplockItoNone\n", 0);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
FsRtlCompleteRequest( Irp, Status );
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
|
||
//
|
||
// In this case the user expects to be at level II. He is
|
||
// expecting this Irp to be completed when the LevelII Oplock
|
||
// is broken.
|
||
//
|
||
|
||
} else if (FlagOn( Oplock->OplockState, BREAK_TO_II_TO_NONE )) {
|
||
|
||
DebugTrace(0, Dbg, "AcknowledgeOplockBreak: OplockItoIItoNone\n", 0);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
FsRtlCompleteRequest( Irp, Status );
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
|
||
} else {
|
||
|
||
Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"No break underway -> %08lx\n",
|
||
Status);
|
||
|
||
FsRtlCompleteRequest( Irp, Status );
|
||
try_return( Status );
|
||
}
|
||
|
||
//
|
||
// Complete the waiting Irps and cleanup the oplock structure.
|
||
//
|
||
|
||
while (!IsListEmpty( &Oplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
//
|
||
// Remove the entry found and complete the Irp.
|
||
//
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Oplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
|
||
if (DereferenceFileObject) {
|
||
|
||
ObDereferenceObject( Oplock->FileObject );
|
||
}
|
||
|
||
Oplock->FileObject = NULL;
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// Give up the oplock synchronization object.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlAcknowledgeOplockBreak: Exit -> %08x\n", Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlOpBatchBreakClosePending (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a user is acknowledging a batch oplock
|
||
break or Level I oplock break. In this case the user is planning
|
||
to close the file as well and doesn't need a level II oplock.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we can complete the operation on exiting this thread.
|
||
STATUS_CANCELLED if the Irp is cancelled before we return.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
BOOLEAN AcquiredMutex;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlOpBatchBreakClosePending: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// If there is no oplock structure, we complete this with invalid
|
||
// oplock protocol.
|
||
//
|
||
|
||
if (Oplock == NULL) {
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_INVALID_OPLOCK_PROTOCOL );
|
||
DebugTrace( -1, Dbg, "FsRtlOpBatchClosePending: Exit -> %08lx\n", STATUS_INVALID_OPLOCK_PROTOCOL );
|
||
return STATUS_INVALID_OPLOCK_PROTOCOL;
|
||
}
|
||
|
||
//
|
||
// Grab the synchronization object for the oplock.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try_finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If a break is underway but this is not the owner of the
|
||
// level 1 oplock, we complete the request and return a
|
||
// warning.
|
||
//
|
||
|
||
if (Oplock->FileObject != IrpSp->FileObject) {
|
||
|
||
Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Not oplock owner -> %08lx\n",
|
||
Status);
|
||
|
||
} else {
|
||
|
||
//
|
||
// If this is an opbatch operation we want to note that a
|
||
// close is pending. For an exclusive oplock we set the state to
|
||
// no oplocsk held. There must be a break in progress to
|
||
// process however.
|
||
//
|
||
|
||
if (FlagOn( Oplock->OplockState,
|
||
BREAK_TO_II | BREAK_TO_NONE | BREAK_TO_II_TO_NONE )) {
|
||
|
||
//
|
||
// Break all oplocks for an exclusive oplock.
|
||
//
|
||
|
||
if (FlagOn( Oplock->OplockState, LEVEL_I_OPLOCK )) {
|
||
|
||
//
|
||
// Clean up the oplock structure and complete all waiting Irps.
|
||
//
|
||
|
||
ObDereferenceObject( Oplock->FileObject );
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
Oplock->FileObject = NULL;
|
||
|
||
while (!IsListEmpty( &Oplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
//
|
||
// Remove the entry found and complete the Irp.
|
||
//
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Oplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
|
||
//
|
||
// Set the state to close pending for batch and filter
|
||
// oplocks.
|
||
//
|
||
|
||
} else {
|
||
|
||
ClearFlag( Oplock->OplockState, OPLOCK_BREAK_MASK );
|
||
SetFlag( Oplock->OplockState, CLOSE_PENDING );
|
||
}
|
||
|
||
} else {
|
||
|
||
Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"No break underway -> %08lx\n",
|
||
Status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// We simply complete this request.
|
||
//
|
||
|
||
FsRtlCompleteRequest( Irp, Status );
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Release the synchronization object.
|
||
//
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
|
||
DebugTrace(-1, Dbg, "FsRtlOpBatchBreakClosePending: Exit -> %08lx\n", Status);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlOplockBreakNotify (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when the Irp refers the user request to
|
||
be notified when there is no level 1 oplock break in progress.
|
||
Under any other condition this routine completes immediately with
|
||
STATUS_SUCCESS. Otherwise we simply add this Irp to the list
|
||
of Irp's waiting for the break to complete.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we can complete the operation on exiting this thread.
|
||
STATUS_PENDING if we return here but hold the Irp.
|
||
STATUS_CANCELLED if the Irp is cancelled before we return.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN AcquiredMutex = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlOplockBreakNotify: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// If there is no oplock structure, we complete this with status success.
|
||
//
|
||
|
||
if (Oplock == NULL) {
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_SUCCESS );
|
||
DebugTrace( -1, Dbg, "FsRtlOpBatchClosePending: Exit -> %08lx\n", STATUS_SUCCESS );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Grap the synchronization object.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If there are no outstanding level 1 oplocks breaks underway
|
||
// or batch oplock breaks underway we complete immediately.
|
||
//
|
||
|
||
if (!FlagOn( Oplock->OplockState, OPLOCK_BREAK_MASK )) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"No exclusive oplock break underway\n",
|
||
0);
|
||
|
||
FsRtlCompleteRequest( Irp, STATUS_SUCCESS );
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// Otherwise we need to add this Irp to the list of Irp's waiting
|
||
// for the oplock break to complete.
|
||
//
|
||
|
||
AcquiredMutex = FALSE;
|
||
|
||
//
|
||
// Initialize the return value to status success.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
Status = FsRtlWaitOnIrp( Oplock,
|
||
Irp,
|
||
NULL,
|
||
FsRtlNotifyCompletion,
|
||
NULL,
|
||
NULL );
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// Give up the synchronization event if we haven't done so.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlOplockBreakNotify: Exit -> %08lx\n", Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
FsRtlOplockCleanup (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to coordinate a cleanup operation with the
|
||
oplock state for a file. If there is no level 1 oplock for the
|
||
file, then there is no action to take. If the file object in this
|
||
Irp matches the file object used in granting the level 1 oplock,
|
||
then the close operation will terminate the oplock. If this
|
||
cleanup refers to a file object which has a level II oplock, then
|
||
that Irp is completed and removed from the list of level II
|
||
oplocked Irps.
|
||
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DebugTrace( +1, Dbg, "FsRtlOplockCleanup: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
|
||
//
|
||
// Grab the synchronization object for the oplock.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If the oplock has no oplock held we return immediately.
|
||
//
|
||
|
||
if (Oplock->OplockState == NoOplocksHeld) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"No oplocks on file\n",
|
||
0);
|
||
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// If level II oplocks are held, check if this matches any of them.
|
||
//
|
||
|
||
if (Oplock->OplockState == OplockIIGranted) {
|
||
|
||
PLIST_ENTRY Link;
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"File has level 2 oplocks\n",
|
||
0);
|
||
|
||
for (Link = Oplock->IrpOplocksII.Flink;
|
||
Link != &Oplock->IrpOplocksII;
|
||
Link = Link->Flink) {
|
||
|
||
PLIST_ENTRY NextLink;
|
||
|
||
if (!FsRtlCheckForMatchingFileObject( IrpSp->FileObject,
|
||
Link,
|
||
&Oplock->IrpOplocksII,
|
||
&NextLink )) {
|
||
|
||
DebugTrace( 0, Dbg, "No matching Irp found\n", 0 );
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// Back up to remember this link.
|
||
//
|
||
|
||
Link = NextLink->Blink;
|
||
|
||
//
|
||
// Remove the entry found and complete the Irp.
|
||
//
|
||
|
||
FsRtlRemoveAndCompleteIrp( NextLink );
|
||
}
|
||
|
||
//
|
||
// If all the level II oplocks are gone, then the state is
|
||
// no oplocks held.
|
||
//
|
||
|
||
if (IsListEmpty( &Oplock->IrpOplocksII )) {
|
||
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
}
|
||
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// If this file object matches that used to request an exclusive
|
||
// oplock, we completely close the oplock break.
|
||
//
|
||
|
||
if (IrpSp->FileObject == Oplock->FileObject) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Handle owns level 1 oplock\n",
|
||
0);
|
||
|
||
//
|
||
// If an oplock break is not in progress, we initiate one and
|
||
// complete the exclusive Irp immediately.
|
||
//
|
||
|
||
if (!FlagOn( Oplock->OplockState, OPLOCK_BREAK_MASK | PENDING )) {
|
||
|
||
PIRP ExclusiveIrp = Oplock->IrpExclusiveOplock;
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Initiate oplock break\n",
|
||
0);
|
||
|
||
IoAcquireCancelSpinLock( &ExclusiveIrp->CancelIrql );
|
||
|
||
IoSetCancelRoutine( ExclusiveIrp, NULL );
|
||
IoReleaseCancelSpinLock( ExclusiveIrp->CancelIrql );
|
||
|
||
ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
|
||
FsRtlCompleteRequest( Oplock->IrpExclusiveOplock, STATUS_SUCCESS );
|
||
|
||
Oplock->IrpExclusiveOplock = NULL;
|
||
}
|
||
|
||
//
|
||
// Clean up the oplock structure and complete all waiting Irps.
|
||
// Don't do this if this is a pending opfilter request.
|
||
//
|
||
|
||
if (!FlagOn( Oplock->OplockState, PENDING )) {
|
||
|
||
ObDereferenceObject( IrpSp->FileObject );
|
||
}
|
||
|
||
Oplock->FileObject = NULL;
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
Oplock->OpFilter = NULL;
|
||
|
||
while (!IsListEmpty( &Oplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
//
|
||
// Remove the entry found and complete the Irp.
|
||
//
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Oplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// Give up the oplock synchronization object.
|
||
//
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
DebugTrace( +1, Dbg, "FsRtlOplockCleanup: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlOplockBreakToII (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp,
|
||
IN PVOID Context,
|
||
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
||
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a generic worker routine which is called when an
|
||
operation will cause all oplocks to be broken to level II before the
|
||
operation can proceed.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Context - This value is passed as a parameter to the completion routine.
|
||
|
||
CompletionRoutine - This is the routine which is called if this
|
||
Irp must wait for an Oplock to break. This
|
||
is a synchronous operation if not specified
|
||
and we block in this thread waiting on
|
||
an event.
|
||
|
||
PostIrpRoutine - This is the routine to call before we put anything
|
||
on our waiting Irp queue.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we can complete the operation on exiting this thread.
|
||
STATUS_PENDING if we return here but hold the Irp.
|
||
STATUS_CANCELLED if the Irp is cancelled before we return.
|
||
|
||
--*/
|
||
|
||
{
|
||
KEVENT Event;
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN AcquiredMutex = FALSE;
|
||
|
||
DebugTrace( +1, Dbg, "CheckOplockBreakToII: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// Grap the synchronization object.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If there are no outstanding oplocks or level II oplocks are held,
|
||
// we can return immediately.
|
||
//
|
||
|
||
if (!FlagOn( Oplock->OplockState, EXCLUSIVE )) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"No oplocks or level II oplocks on file\n",
|
||
0);
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// At this point there is an exclusive oplock break in progress.
|
||
// If this file object owns that oplock, we allow the operation
|
||
// to continue.
|
||
//
|
||
|
||
if (Oplock->FileObject == IrpSp->FileObject) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Handle owns level 1 oplock\n",
|
||
0);
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// If there is currently an exclusive oplock held then complete
|
||
// the exclusive irp.
|
||
//
|
||
|
||
if (!FlagOn( Oplock->OplockState, PENDING | OPLOCK_BREAK_MASK )) {
|
||
|
||
PIRP IrpExclusive = Oplock->IrpExclusiveOplock;
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Breaking exclusive oplock\n",
|
||
0);
|
||
|
||
IoAcquireCancelSpinLock( &IrpExclusive->CancelIrql );
|
||
IoSetCancelRoutine( IrpExclusive, NULL );
|
||
IoReleaseCancelSpinLock( IrpExclusive->CancelIrql );
|
||
|
||
//
|
||
// If the Irp has been cancelled, we complete the Irp with
|
||
// status cancelled and break the oplock completely.
|
||
//
|
||
|
||
if (IrpExclusive->Cancel) {
|
||
|
||
IrpExclusive->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
FsRtlCompleteRequest( IrpExclusive, STATUS_CANCELLED );
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
Oplock->IrpExclusiveOplock = NULL;
|
||
|
||
ObDereferenceObject( Oplock->FileObject );
|
||
Oplock->FileObject = NULL;
|
||
Oplock->OpFilter = NULL;
|
||
|
||
//
|
||
// Release any waiting irps.
|
||
//
|
||
|
||
while (!IsListEmpty( &Oplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Oplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
} else {
|
||
|
||
NTSTATUS CompletionStatus;
|
||
|
||
if (FlagOn( Oplock->OplockState, LEVEL_I_OPLOCK | BATCH_OPLOCK )) {
|
||
|
||
SetFlag( Oplock->OplockState, BREAK_TO_II );
|
||
CompletionStatus = FILE_OPLOCK_BROKEN_TO_LEVEL_2;
|
||
|
||
} else {
|
||
|
||
SetFlag( Oplock->OplockState, BREAK_TO_NONE );
|
||
CompletionStatus = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
}
|
||
|
||
Oplock->IrpExclusiveOplock->IoStatus.Information = CompletionStatus;
|
||
FsRtlCompleteRequest( Oplock->IrpExclusiveOplock, STATUS_SUCCESS );
|
||
Oplock->IrpExclusiveOplock = NULL;
|
||
Oplock->OpFilter = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is an open operation and the user doesn't want to
|
||
// block, we will complete the operation now.
|
||
//
|
||
|
||
if ((IrpSp->MajorFunction == IRP_MJ_CREATE) &&
|
||
FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) {
|
||
|
||
//
|
||
// Start the oplock break if there is an OpFilter pending.
|
||
//
|
||
|
||
if (Oplock->OplockState == OpFilterReqPending) {
|
||
|
||
FsRtlInitializeOpFilter( Oplock );
|
||
}
|
||
|
||
DebugTrace( 0, Dbg, "Don't block open\n", 0 );
|
||
|
||
try_return( Status = STATUS_OPLOCK_BREAK_IN_PROGRESS );
|
||
}
|
||
|
||
//
|
||
// If we get here that means that this operation can't continue
|
||
// until the oplock break is complete.
|
||
//
|
||
// FsRtlWaitOnIrp will release the mutex.
|
||
//
|
||
|
||
AcquiredMutex = FALSE;
|
||
|
||
Status = FsRtlWaitOnIrp( Oplock,
|
||
Irp,
|
||
Context,
|
||
CompletionRoutine,
|
||
PostIrpRoutine,
|
||
&Event );
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// Give up the synchronization event if we haven't done so.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlOplockBreakToII: Exit -> %08lx\n", Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlOplockBreakToNone (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN PIRP Irp,
|
||
IN PVOID Context,
|
||
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
||
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a generic worker routine which is called when an
|
||
operation will cause all oplocks to be broken before the operation can
|
||
proceed.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
IrpSp - This is the Irp stack location for the current Irp.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Context - This value is passed as a parameter to the completion routine.
|
||
|
||
CompletionRoutine - This is the routine which is called if this
|
||
Irp must wait for an Oplock to break. This
|
||
is a synchronous operation if not specified
|
||
and we block in this thread waiting on
|
||
an event.
|
||
|
||
PostIrpRoutine - This is the routine to call before we put anything
|
||
on our waiting Irp queue.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we can complete the operation on exiting this thread.
|
||
STATUS_PENDING if we return here but hold the Irp.
|
||
STATUS_CANCELLED if the Irp is cancelled before we return.
|
||
|
||
--*/
|
||
|
||
{
|
||
KEVENT Event;
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN AcquiredMutex = FALSE;
|
||
|
||
DebugTrace( +1, Dbg, "CheckOplockBreakToNone: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Oplock -> %08lx\n", Oplock );
|
||
DebugTrace( 0, Dbg, "IrpSp -> %08lx\n", IrpSp );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// Grap the synchronization object.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If there are no outstanding oplocks, we can return immediately.
|
||
//
|
||
|
||
if (Oplock->OplockState == NoOplocksHeld) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"No oplocks on file\n",
|
||
0);
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// If there is an exclusive oplock held, we begin the break to none.
|
||
//
|
||
|
||
if (!FlagOn( Oplock->OplockState,
|
||
LEVEL_II_OPLOCK | PENDING | OPLOCK_BREAK_MASK )) {
|
||
|
||
PIRP IrpExclusive = Oplock->IrpExclusiveOplock;
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Breaking exclusive oplock\n",
|
||
0);
|
||
|
||
IoAcquireCancelSpinLock( &IrpExclusive->CancelIrql );
|
||
IoSetCancelRoutine( IrpExclusive, NULL );
|
||
IoReleaseCancelSpinLock( IrpExclusive->CancelIrql );
|
||
|
||
//
|
||
// If the Irp has been cancelled, we complete the Irp with
|
||
// status cancelled and break the oplock completely.
|
||
//
|
||
|
||
if (IrpExclusive->Cancel) {
|
||
|
||
IrpExclusive->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
FsRtlCompleteRequest( IrpExclusive, STATUS_CANCELLED );
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
Oplock->IrpExclusiveOplock = NULL;
|
||
|
||
ObDereferenceObject( Oplock->FileObject );
|
||
Oplock->FileObject = NULL;
|
||
Oplock->OpFilter = NULL;
|
||
|
||
//
|
||
// Release any waiting irps.
|
||
//
|
||
|
||
while (!IsListEmpty( &Oplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Oplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
} else {
|
||
|
||
Oplock->IrpExclusiveOplock->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
FsRtlCompleteRequest( Oplock->IrpExclusiveOplock, STATUS_SUCCESS );
|
||
Oplock->IrpExclusiveOplock = NULL;
|
||
|
||
SetFlag( Oplock->OplockState, BREAK_TO_NONE );
|
||
}
|
||
|
||
//
|
||
// If there are level II oplocks, this will break all of them.
|
||
//
|
||
|
||
} else if (Oplock->OplockState == OplockIIGranted) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Breaking all level 2 oplocks\n",
|
||
0);
|
||
|
||
while (!IsListEmpty( &Oplock->IrpOplocksII )) {
|
||
|
||
//
|
||
// Remove and complete this Irp with STATUS_SUCCESS.
|
||
//
|
||
|
||
FsRtlRemoveAndCompleteIrp( Oplock->IrpOplocksII.Flink );
|
||
}
|
||
|
||
//
|
||
// Set the oplock state to no oplocks held.
|
||
//
|
||
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
//
|
||
// If we are currently breaking to level II then change that
|
||
// to BreakToIIToNone.
|
||
//
|
||
|
||
} else if (FlagOn( Oplock->OplockState, BREAK_TO_II )) {
|
||
|
||
ClearFlag( Oplock->OplockState, BREAK_TO_II );
|
||
SetFlag( Oplock->OplockState, BREAK_TO_II_TO_NONE );
|
||
}
|
||
|
||
//
|
||
// At this point there is already an exclusive oplock break in progress.
|
||
// If this file object owns that oplock, we allow the operation
|
||
// to continue.
|
||
//
|
||
|
||
if (Oplock->FileObject == IrpSp->FileObject) {
|
||
|
||
DebugTrace(0,
|
||
Dbg,
|
||
"Handle owns level 1 oplock\n",
|
||
0);
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// If this is an open operation and the user doesn't want to
|
||
// block, we will complete the operation now.
|
||
//
|
||
|
||
if (IrpSp->MajorFunction == IRP_MJ_CREATE
|
||
&& FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) {
|
||
|
||
//
|
||
// Start the timer if there is a pending filter oplock.
|
||
//
|
||
|
||
if (Oplock->OplockState == OpFilterReqPending) {
|
||
|
||
FsRtlInitializeOpFilter( Oplock );
|
||
}
|
||
|
||
DebugTrace( 0, Dbg, "Don't block open\n", 0 );
|
||
|
||
try_return( Status = STATUS_OPLOCK_BREAK_IN_PROGRESS );
|
||
}
|
||
|
||
//
|
||
// If we get here that means that this operation can't continue
|
||
// until the oplock break is complete.
|
||
//
|
||
// FsRtlWaitOnIrp will release the mutex.
|
||
//
|
||
|
||
AcquiredMutex = FALSE;
|
||
|
||
Status = FsRtlWaitOnIrp( Oplock,
|
||
Irp,
|
||
Context,
|
||
CompletionRoutine,
|
||
PostIrpRoutine,
|
||
&Event );
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// Give up the synchronization event if we haven't done so.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "CheckOplockBreakToNone: Exit -> %08lx\n", Status );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlRemoveAndCompleteIrp (
|
||
IN PLIST_ENTRY Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to remove an Irp from a list of Irps linked
|
||
with the Tail.ListEntry field and complete them with STATUS_CANCELLED
|
||
if the Irp has been cancelled, STATUS_SUCCESS otherwise.
|
||
|
||
Arguments:
|
||
|
||
Link - Supplies the entry to remove from the list.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION OplockIIIrpSp;
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlRemoveAndCompleteIrp: Entered\n", 0 );
|
||
|
||
//
|
||
// Reference the Irp.
|
||
//
|
||
|
||
Irp = CONTAINING_RECORD( Link, IRP, Tail.Overlay.ListEntry );
|
||
|
||
//
|
||
// Get the stack location and dereference the file object.
|
||
//
|
||
|
||
OplockIIIrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
ObDereferenceObject( OplockIIIrpSp->FileObject );
|
||
|
||
//
|
||
// Clear the cancel routine in the irp.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// Remove this from the list.
|
||
//
|
||
|
||
RemoveEntryList( Link );
|
||
|
||
//
|
||
// Complete the oplock Irp.
|
||
//
|
||
|
||
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
||
|
||
FsRtlCompleteRequest( Irp, Irp->Cancel ? STATUS_CANCELLED : STATUS_SUCCESS );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlRemoveAndCompleteIrp: Exit\n", 0 );
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
NTSTATUS
|
||
FsRtlWaitOnIrp (
|
||
IN OUT PNONOPAQUE_OPLOCK Oplock,
|
||
IN PIRP Irp,
|
||
IN PVOID Context,
|
||
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
||
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL,
|
||
IN PKEVENT Event
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to create a Wait Irp structure and attach it
|
||
to the current Irp. The Irp is then added to the list of Irps waiting
|
||
for an oplock break. We check if the Irp has been cancelled and if
|
||
so we call our cancel routine to perform the work.
|
||
|
||
This routine is holding the Mutex for the oplock on entry and
|
||
must give it up on exit.
|
||
|
||
Arguments:
|
||
|
||
Oplock - Supplies a pointer to the non-opaque oplock structure for
|
||
this file.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Context - This value is passed as a parameter to the completion routine.
|
||
|
||
CompletionRoutine - This is the routine which is called if this
|
||
Irp must wait for an Oplock to break. This
|
||
is a synchronous operation if not specified
|
||
and we block in this thread waiting on
|
||
an event.
|
||
|
||
PostIrpRoutine - This is the routine to call before we put anything
|
||
on our waiting Irp queue.
|
||
|
||
Event - If there is no user completion routine, this thread will
|
||
block using this event.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we can complete the operation on exiting this thread.
|
||
STATUS_PENDING if we return here but hold the Irp.
|
||
STATUS_CANCELLED if the Irp is cancelled before we return.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN AcquiredMutex;
|
||
NTSTATUS Status;
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlWaitOnIrp: Entered\n", 0 );
|
||
|
||
//
|
||
// Remember that we have the mutex.
|
||
//
|
||
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
PFAST_MUTEX OplockFastMutex = Oplock->FastMutex;
|
||
|
||
//
|
||
// If the oplock state shows that we are waiting for
|
||
// an oplock filter request then start a timer to
|
||
// time out the request.
|
||
//
|
||
|
||
if (Oplock->OplockState == OpFilterReqPending) {
|
||
|
||
FsRtlInitializeOpFilter( Oplock );
|
||
}
|
||
|
||
//
|
||
// Allocate and initialize the Wait Irp structure.
|
||
//
|
||
|
||
WaitingIrp = FsRtlAllocatePool( PagedPool, sizeof( WAITING_IRP ));
|
||
|
||
WaitingIrp->Irp = Irp;
|
||
|
||
WaitingIrp->Context = Context;
|
||
WaitingIrp->Information = Irp->IoStatus.Information;
|
||
|
||
//
|
||
// Take appropriate action if depending on the value of the
|
||
// completion routine.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( CompletionRoutine )) {
|
||
|
||
WaitingIrp->CompletionRoutine = CompletionRoutine;
|
||
WaitingIrp->Context = Context;
|
||
|
||
} else {
|
||
|
||
WaitingIrp->CompletionRoutine = FsRtlCompletionRoutinePriv;
|
||
WaitingIrp->Context = Event;
|
||
|
||
KeInitializeEvent( Event, NotificationEvent, FALSE );
|
||
}
|
||
|
||
//
|
||
// Call the file system's post Irp code.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( PostIrpRoutine )) {
|
||
|
||
PostIrpRoutine( Context, Irp );
|
||
}
|
||
|
||
//
|
||
// Initialize the return value to status success.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// We put this into the Waiting Irp queue.
|
||
//
|
||
|
||
InsertTailList( &Oplock->WaitingIrps, &WaitingIrp->Links );
|
||
|
||
//
|
||
// We grab the cancel spinlock and store the address of the oplock.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
Irp->IoStatus.Information = (ULONG) Oplock;
|
||
|
||
//
|
||
// If the Irp is cancelled then we'll call the cancel routine
|
||
// right now to do away with the Waiting Irp structure.
|
||
//
|
||
|
||
if (Irp->Cancel) {
|
||
|
||
ExReleaseFastMutexUnsafe( OplockFastMutex );
|
||
AcquiredMutex = FALSE;
|
||
|
||
if (ARGUMENT_PRESENT( CompletionRoutine )) {
|
||
|
||
IoMarkIrpPending( Irp );
|
||
Status = STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
Status = STATUS_CANCELLED;
|
||
}
|
||
|
||
FsRtlCancelWaitIrp( NULL, Irp );
|
||
|
||
//
|
||
// Otherwise, we set the cancel routine and decide whether we
|
||
// are going to wait on our local event.
|
||
//
|
||
|
||
} else {
|
||
|
||
IoSetCancelRoutine( Irp, FsRtlCancelWaitIrp );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// If we wait on the event, we pull the return code out of
|
||
// the Irp.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT( CompletionRoutine )) {
|
||
|
||
AcquiredMutex = FALSE;
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
|
||
KeWaitForSingleObject( Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
Status = Irp->IoStatus.Status;
|
||
|
||
//
|
||
// Otherwise, we return STATUS_PENDING.
|
||
//
|
||
|
||
} else {
|
||
|
||
IoMarkIrpPending( Irp );
|
||
|
||
Status = STATUS_PENDING;
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Release the Mutex if we have not done so.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlWaitOnIrp: Exit\n", 0 );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlCompletionRoutinePriv (
|
||
IN PVOID Context,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when an operation must be synchronous with
|
||
respect to the oplock package. This routine will simply set the
|
||
event in the Signalled state, allowing some other thread to resume
|
||
execution.
|
||
|
||
Arguments:
|
||
|
||
Context - This is the event to signal.
|
||
|
||
Irp - Supplies a pointer to the Irp which declares the requested
|
||
operation.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCompletionRoutinePriv: Entered\n", 0 );
|
||
|
||
KeSetEvent( (PKEVENT)Context, 0, FALSE );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlCompletionRoutinePriv: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlCancelWaitIrp (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for an Irp that is placed on the waiting
|
||
Irp queue. We remove the Cancel routine from the specified Irp and
|
||
then call the completion routines for all the cancelled Irps on the
|
||
queue.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Ignored.
|
||
|
||
Irp - Supplies the Irp being cancelled. A pointer to the
|
||
Oplock structure for the Irp is stored in the information
|
||
field of the Irp's Iosb.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNONOPAQUE_OPLOCK Oplock;
|
||
|
||
PLIST_ENTRY Links;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCancelWaitIrp: Entered\n", 0 );
|
||
|
||
Oplock = (PNONOPAQUE_OPLOCK) Irp->IoStatus.Information;
|
||
|
||
//
|
||
// We now need to void the cancel routine and release the spinlock
|
||
//
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// Iterate through all of the waiting locks looking for a canceled one
|
||
// We do this under the protection of the waiting lock queue mutex
|
||
//
|
||
|
||
ExAcquireFastMutex( Oplock->FastMutex );
|
||
|
||
try {
|
||
|
||
for (Links = Oplock->WaitingIrps.Flink;
|
||
Links != &Oplock->WaitingIrps;
|
||
Links = Links->Flink ) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
//
|
||
// Get a pointer to the waiting Irp record
|
||
//
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Links, WAITING_IRP, Links );
|
||
|
||
DebugTrace(0, Dbg, "FsRtlCancelWaitIrp, Loop top, WaitingIrp = %08lx\n", WaitingIrp);
|
||
|
||
//
|
||
// Check if the irp has been cancelled
|
||
//
|
||
|
||
if (WaitingIrp->Irp->Cancel) {
|
||
|
||
//
|
||
// Now we need to remove this waiter and call the
|
||
// completion routine. But we must not mess up our link
|
||
// iteration so we need to back up link one step and
|
||
// then the next iteration will go to our current flink.
|
||
//
|
||
|
||
Links = Links->Blink;
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// No matter how we exit we release the mutex
|
||
//
|
||
|
||
ExReleaseFastMutex( Oplock->FastMutex );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlCancelWaitIrp: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlCancelOplockIIIrp (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for an Irp that is placed in the Oplock II
|
||
Irp queue. We remove the Cancel routine from the specified Irp and
|
||
then call the completion routines for all the cancelled Irps on the
|
||
queue.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Ignored.
|
||
|
||
Irp - Supplies the Irp being cancelled. A pointer to the
|
||
Oplock structure for the Irp is stored in the information
|
||
field of the Irp's Iosb.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNONOPAQUE_OPLOCK Oplock;
|
||
BOOLEAN LevelIIIrps;
|
||
|
||
PLIST_ENTRY Links;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCancelOplockIIIrp: Entered\n", 0 );
|
||
|
||
Oplock = (PNONOPAQUE_OPLOCK) Irp->IoStatus.Information;
|
||
|
||
//
|
||
// We now need to void the cancel routine and release the spinlock
|
||
//
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
LevelIIIrps = FALSE;
|
||
|
||
//
|
||
// Iterate through all of the level II oplocks looking for a canceled one
|
||
// We do this under the protection of the waiting lock queue mutex
|
||
//
|
||
|
||
ExAcquireFastMutex( Oplock->FastMutex );
|
||
|
||
try {
|
||
|
||
for (Links = Oplock->IrpOplocksII.Flink;
|
||
Links != &Oplock->IrpOplocksII;
|
||
Links = Links->Flink ) {
|
||
|
||
PIRP OplockIIIrp;
|
||
|
||
//
|
||
// Get a pointer to the Irp record
|
||
//
|
||
|
||
OplockIIIrp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
|
||
|
||
DebugTrace(0, Dbg, "FsRtlCancelOplockIIIrp, Loop top, Irp = %08lx\n", OplockIIIrp);
|
||
|
||
//
|
||
// Check if the irp has been cancelled
|
||
//
|
||
|
||
if (OplockIIIrp->Cancel) {
|
||
|
||
//
|
||
// Now we need to remove this waiter and call the
|
||
// completion routine. But we must not mess up our link
|
||
// iteration so we need to back up link one step and
|
||
// then the next iteration will go to our current flink.
|
||
//
|
||
|
||
Links = Links->Blink;
|
||
|
||
FsRtlRemoveAndCompleteIrp( Links->Flink );
|
||
|
||
LevelIIIrps = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the list is now empty, change the oplock status to
|
||
// no oplocks held.
|
||
//
|
||
|
||
if (IsListEmpty( &Oplock->IrpOplocksII ) &&
|
||
LevelIIIrps) {
|
||
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// No matter how we exit we release the mutex
|
||
//
|
||
|
||
ExReleaseFastMutex( Oplock->FastMutex );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlCancelOplockIIIrp: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlCancelExclusiveIrp (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for either an exclusive or oplock I Irp.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Ignored.
|
||
|
||
Irp - Supplies the Irp being cancelled. A pointer to the
|
||
Oplock structure for the Irp is stored in the information
|
||
field of the Irp's Iosb.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNONOPAQUE_OPLOCK Oplock;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCancelExclusiveIrp: Entered\n", 0 );
|
||
|
||
Oplock = (PNONOPAQUE_OPLOCK) Irp->IoStatus.Information;
|
||
|
||
//
|
||
// We now need to void the cancel routine and release the spinlock
|
||
//
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// Grab the synchronization object for this oplock.
|
||
//
|
||
|
||
ExAcquireFastMutex( Oplock->FastMutex );
|
||
|
||
try {
|
||
|
||
//
|
||
// We look for the exclusive Irp, if present and cancelled
|
||
// we complete it.
|
||
//
|
||
|
||
if (Oplock->IrpExclusiveOplock != NULL
|
||
&& Oplock->IrpExclusiveOplock->Cancel) {
|
||
|
||
FsRtlCompleteRequest( Oplock->IrpExclusiveOplock, STATUS_CANCELLED );
|
||
Oplock->IrpExclusiveOplock = NULL;
|
||
|
||
ObDereferenceObject( Oplock->FileObject );
|
||
Oplock->FileObject = NULL;
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
Oplock->OpFilter = NULL;
|
||
|
||
//
|
||
// Complete the waiting Irps.
|
||
//
|
||
|
||
while (!IsListEmpty( &Oplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
//
|
||
// Remove the entry found and complete the Irp.
|
||
//
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Oplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// No matter how we exit we release the mutex
|
||
//
|
||
|
||
ExReleaseFastMutex( Oplock->FastMutex );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlCancelExclusiveIrp: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlRemoveAndCompleteWaitIrp (
|
||
IN PWAITING_IRP WaitingIrp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to remove and perform any neccessary cleanup
|
||
for an Irp stored on the waiting Irp list in an oplock structure.
|
||
|
||
Arguments:
|
||
|
||
WaitingIrp - This is the auxilary structure attached to the Irp
|
||
being completed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP Irp;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlRemoveAndCompleteWaitIrp: Entered\n", 0 );
|
||
|
||
//
|
||
// Remove the Irp from the queue.
|
||
//
|
||
|
||
RemoveEntryList( &WaitingIrp->Links );
|
||
|
||
Irp = WaitingIrp->Irp;
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// Restore the information field.
|
||
//
|
||
|
||
Irp->IoStatus.Information = WaitingIrp->Information;
|
||
|
||
Irp->IoStatus.Status = (Irp->Cancel
|
||
? STATUS_CANCELLED
|
||
: STATUS_SUCCESS);
|
||
|
||
//
|
||
// Call the completion routine in the Waiting Irp.
|
||
//
|
||
|
||
WaitingIrp->CompletionRoutine( WaitingIrp->Context, Irp );
|
||
|
||
//
|
||
// And free up pool
|
||
//
|
||
|
||
ExFreePool( WaitingIrp );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlRemoveAndCompleteWaitIrp: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
|
||
BOOLEAN
|
||
FsRtlCheckForMatchingFileObject (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLIST_ENTRY Link,
|
||
IN PLIST_ENTRY EndOfList,
|
||
OUT PLIST_ENTRY *MatchingLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to walk through a linked list of Irp's, looking
|
||
for a file object to match the argument file object.
|
||
|
||
Arguments:
|
||
|
||
FileObject - This is the file object to match.
|
||
|
||
Link - This is the starting point in the list to search from.
|
||
|
||
EndOfList - This link value signals the end of the list.
|
||
|
||
MatchingLink - Supplies the address to store the link containing
|
||
the matching Irp.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if a matching Irp is found, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN FoundLink;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCheckForMatchingFileObject: Entered\n", 0 );
|
||
|
||
//
|
||
// Assume we won't find a match.
|
||
//
|
||
|
||
FoundLink = FALSE;
|
||
|
||
//
|
||
// Continue looking as long as we haven't reached the end of the list.
|
||
//
|
||
|
||
while (Link != EndOfList) {
|
||
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
//
|
||
// Get the Irp and IrpSp for this link.
|
||
//
|
||
|
||
Irp = CONTAINING_RECORD( Link, IRP, Tail.Overlay.ListEntry );
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// If the file objects match, we remember this link and break out
|
||
// of the loop.
|
||
//
|
||
|
||
if (FileObject == IrpSp->FileObject) {
|
||
|
||
DebugTrace( 0, Dbg, "Found a match\n", 0 );
|
||
|
||
*MatchingLink = Link;
|
||
FoundLink = TRUE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Otherwise we get the next link.
|
||
//
|
||
|
||
Link = Link->Flink;
|
||
}
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCheckForMatchingFileObject: Exit -> %08x\n", FoundLink );
|
||
|
||
return FoundLink;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlNotifyCompletion (
|
||
IN PVOID Context,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the completion routine called when a break notify Irp is to
|
||
be completed. We simply call FsRtlComplete request to dispose of the
|
||
Irp.
|
||
|
||
Arguments:
|
||
|
||
Context - Ignored.
|
||
|
||
Irp - Irp used to request break notify.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyCompletion: Entered\n", 0 );
|
||
|
||
//
|
||
// Call FsRtlCompleteRequest using the value in the Irp.
|
||
//
|
||
|
||
FsRtlCompleteRequest( Irp, Irp->IoStatus.Status );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyCompletion: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlOplockDpc (
|
||
IN PKDPC Dpc,
|
||
IN PVOID Context,
|
||
IN PVOID SystemArgument1,
|
||
IN PVOID SystemArgument2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when the timer in the oplock structure times
|
||
out while waiting for an filter oplock request. This routine posts
|
||
a request to a worker queue to do the actual work.
|
||
|
||
Arguments:
|
||
|
||
Context - This is the filter timer structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
POPFILTER_TIMER OpFilterTimer = (POPFILTER_TIMER) Context;
|
||
|
||
ExQueueWorkItem( &OpFilterTimer->OpFilterItem, CriticalWorkQueue );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlOpFilterWorkerRoutine (
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the worker routine which process the OpFilter timer operations
|
||
on a time out. It can either tear down the oplock structure, release
|
||
waiting irps or do nothing if the actual request for the oplock filter
|
||
has been processed.
|
||
|
||
Arguments:
|
||
|
||
Context - This is the filter timer structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
POPFILTER_TIMER OpFilter = (POPFILTER_TIMER) Context;
|
||
PNONOPAQUE_OPLOCK Oplock = OpFilter->Oplock;
|
||
BOOLEAN AcquiredMutex;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Grap the synchronization object.
|
||
//
|
||
|
||
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
||
AcquiredMutex = TRUE;
|
||
|
||
//
|
||
// Decrement the timer count.
|
||
//
|
||
|
||
Oplock->TimerCount -= 1;
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Teardown the structure if we are to uninitialize it.
|
||
//
|
||
|
||
if (Oplock->OplockState == UninitializeOplock) {
|
||
|
||
if (Oplock->TimerCount == 0) {
|
||
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
AcquiredMutex = FALSE;
|
||
|
||
FsRtlUninitializeOplock( &Oplock );
|
||
}
|
||
|
||
//
|
||
// If this is not the current timer then there is nothing to do.
|
||
//
|
||
|
||
} else {
|
||
|
||
if (Oplock->OpFilter == OpFilter) {
|
||
|
||
//
|
||
// Check if the timer expired naturally.
|
||
//
|
||
|
||
if (Oplock->OplockState == OpFilterTimerStarted) {
|
||
|
||
//
|
||
// Cleanup the oplock structure and complete all waiting Irps.
|
||
//
|
||
|
||
while (!IsListEmpty( &Oplock->WaitingIrps )) {
|
||
|
||
PWAITING_IRP WaitingIrp;
|
||
|
||
//
|
||
// Remove the entry found and complete the Irp.
|
||
//
|
||
|
||
WaitingIrp = CONTAINING_RECORD( Oplock->WaitingIrps.Flink,
|
||
WAITING_IRP,
|
||
Links );
|
||
|
||
FsRtlRemoveAndCompleteWaitIrp( WaitingIrp );
|
||
}
|
||
|
||
Oplock->OplockState = NoOplocksHeld;
|
||
Oplock->FileObject = NULL;
|
||
}
|
||
|
||
Oplock->OpFilter = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Always free the timer structure.
|
||
//
|
||
|
||
ExFreePool( OpFilter );
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Give up the synchronization event if we haven't done so.
|
||
//
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine.
|
||
//
|
||
|
||
VOID
|
||
FsRtlInitializeOpFilter (
|
||
IN PNONOPAQUE_OPLOCK Oplock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to allocate and initialize the oplock filter
|
||
structure. This will also initialize and start the timer.
|
||
|
||
Arguments:
|
||
|
||
Context - This is the filter timer structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
POPFILTER_TIMER OpFilter;
|
||
LONGLONG TwoSecondsFromNow;
|
||
|
||
PAGED_CODE();
|
||
|
||
OpFilter = FsRtlAllocatePool( NonPagedPool,
|
||
sizeof( OPFILTER_TIMER ));
|
||
|
||
KeInitializeDpc( &OpFilter->OpFilterDpc,
|
||
FsRtlOplockDpc,
|
||
OpFilter );
|
||
|
||
KeInitializeTimer( &OpFilter->OpFilterTimer );
|
||
|
||
ExInitializeWorkItem( &OpFilter->OpFilterItem,
|
||
FsRtlOpFilterWorkerRoutine,
|
||
OpFilter );
|
||
|
||
Oplock->TimerCount += 1;
|
||
|
||
Oplock->OpFilter = OpFilter;
|
||
Oplock->OplockState = OpFilterTimerStarted;
|
||
|
||
OpFilter->Oplock = Oplock;
|
||
|
||
TwoSecondsFromNow = -2 * 1000 * 1000 * 10;
|
||
|
||
KeSetTimer( &OpFilter->OpFilterTimer,
|
||
*(PLARGE_INTEGER) &TwoSecondsFromNow,
|
||
&OpFilter->OpFilterDpc );
|
||
|
||
return;
|
||
}
|