You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3737 lines
99 KiB
3737 lines
99 KiB
/*++
|
|
|
|
|
|
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 | \
|
|
FILE_EXECUTE | \
|
|
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 OPLOCK_TYPE_MASK (0x0000001f)
|
|
|
|
#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 OPLOCK_BREAK_MASK (0x00000f00)
|
|
|
|
//
|
|
// 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 OplockIIGranted (LEVEL_II_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;
|
|
|
|
} 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;
|
|
|
|
//
|
|
// Define a tag for general pool allocations from this module
|
|
//
|
|
|
|
#undef MODULE_POOL_TAG
|
|
#define MODULE_POOL_TAG ('orSF')
|
|
|
|
|
|
//
|
|
// 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
|
|
);
|
|
|
|
VOID
|
|
FsRtlNotifyCompletion (
|
|
IN PVOID Context,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FsRtlAllocateOplock)
|
|
#pragma alloc_text(PAGE, FsRtlCompletionRoutinePriv)
|
|
#pragma alloc_text(PAGE, FsRtlCurrentBatchOplock)
|
|
#pragma alloc_text(PAGE, FsRtlInitializeOplock)
|
|
#pragma alloc_text(PAGE, FsRtlNotifyCompletion)
|
|
#pragma alloc_text(PAGE, FsRtlOpBatchBreakClosePending)
|
|
#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 );
|
|
|
|
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 );
|
|
}
|
|
|
|
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.
|
|
//
|
|
// Also fail this if the flag is set which indicates that
|
|
// the IO system should copy data back to a user's buffer.
|
|
//
|
|
// If cleanup has occurrred on this file, then we refuse
|
|
// the oplock request.
|
|
//
|
|
|
|
if ((OpenCount != 1) ||
|
|
IoIsOperationSynchronous( Irp ) ||
|
|
FlagOn( Irp->Flags, IRP_INPUT_OPERATION ) ||
|
|
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.
|
|
//
|
|
// Also fail this if the flag is set which indicates that
|
|
// the IO system should copy data back to a user's buffer.
|
|
//
|
|
// 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( Irp->Flags, IRP_INPUT_OPERATION ) ||
|
|
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 :
|
|
case FileShortNameInformation :
|
|
|
|
if (FlagOn( OplockState, BATCH_OPLOCK | FILTER_OPLOCK )) {
|
|
|
|
BreakToNone = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IRP_MJ_FILE_SYSTEM_CONTROL :
|
|
|
|
//
|
|
// We need to break to none if this is a zeroing operation.
|
|
//
|
|
|
|
if (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_SET_ZERO_DATA) {
|
|
|
|
BreakToNone = TRUE;
|
|
}
|
|
}
|
|
|
|
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 = FsRtlpAllocatePool( PagedPool|POOL_COLD_ALLOCATION, sizeof( NONOPAQUE_OPLOCK ));
|
|
|
|
RtlZeroMemory( NewOplock, sizeof( NONOPAQUE_OPLOCK ));
|
|
|
|
NewOplock->FastMutex = FsRtlpAllocatePool( 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;
|
|
|
|
LOGICAL AcquiredMutex;
|
|
|
|
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.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
AcquiredMutex = TRUE;
|
|
|
|
ExAcquireFastMutexUnsafe( ThisOplock->FastMutex );
|
|
|
|
//
|
|
// 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->OplockState = OpFilterReqPending;
|
|
|
|
//
|
|
// 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;
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
ObReferenceObject( IrpSp->FileObject );
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR) 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;
|
|
|
|
LOGICAL 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.
|
|
//
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
AcquiredMutex = TRUE;
|
|
|
|
ExAcquireFastMutexUnsafe( ThisOplock->FastMutex );
|
|
|
|
//
|
|
// 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_PTR) 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 );
|
|
}
|
|
|
|
} 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 = STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
|
|
LOGICAL 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.
|
|
//
|
|
|
|
AcquiredMutex = TRUE;
|
|
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
|
|
|
//
|
|
// 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 )) {
|
|
|
|
PFAST_MUTEX OplockFastMutex = Oplock->FastMutex;
|
|
|
|
DebugTrace(0, Dbg, "OplockItoII\n", 0);
|
|
|
|
//
|
|
// The acknowledgement should never be synchronous.
|
|
//
|
|
|
|
ASSERT( !IoIsOperationSynchronous( Irp ));
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
InsertHeadList( &Oplock->IrpOplocksII,
|
|
&Irp->Tail.Overlay.ListEntry );
|
|
|
|
DereferenceFileObject = FALSE;
|
|
|
|
Oplock->OplockState = OplockIIGranted;
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR) 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) {
|
|
|
|
ExReleaseFastMutexUnsafe( OplockFastMutex );
|
|
|
|
AcquiredMutex = FALSE;
|
|
|
|
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;
|
|
|
|
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 );
|
|
|
|
//
|
|
// 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 | PENDING )) {
|
|
|
|
//
|
|
// Clean up the oplock structure and complete all waiting Irps.
|
|
//
|
|
|
|
if (FlagOn( Oplock->OplockState, LEVEL_I_OPLOCK )) {
|
|
|
|
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 = STATUS_SUCCESS;
|
|
|
|
LOGICAL AcquiredMutex;
|
|
|
|
UNREFERENCED_PARAMETER (IrpSp);
|
|
|
|
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.
|
|
//
|
|
|
|
AcquiredMutex = TRUE;
|
|
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
|
|
|
//
|
|
// 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 );
|
|
|
|
} else if (FlagOn( Oplock->OplockState, PENDING )) {
|
|
|
|
Oplock->OplockState = NoOplocksHeld;
|
|
Oplock->FileObject = NULL;
|
|
|
|
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;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION NextIrpSp;
|
|
|
|
DebugTrace(0,
|
|
Dbg,
|
|
"File has level 2 oplocks\n",
|
|
0);
|
|
|
|
for (Link = Oplock->IrpOplocksII.Flink;
|
|
Link != &Oplock->IrpOplocksII;
|
|
Link = Link->Flink) {
|
|
|
|
Irp = CONTAINING_RECORD( Link, IRP, Tail.Overlay.ListEntry );
|
|
|
|
NextIrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
//
|
|
// If the file objects match, then emove the entry found and complete the Irp.
|
|
//
|
|
|
|
if (IrpSp->FileObject == NextIrpSp->FileObject) {
|
|
|
|
//
|
|
// Back up to remember this link.
|
|
//
|
|
|
|
Link = Link->Blink;
|
|
|
|
//
|
|
//
|
|
|
|
FsRtlRemoveAndCompleteIrp( Link->Flink );
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
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;
|
|
|
|
LOGICAL AcquiredMutex;
|
|
|
|
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.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
AcquiredMutex = TRUE;
|
|
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// If there is a pending opfilter request then clear the request.
|
|
//
|
|
|
|
} else if (FlagOn( Oplock->OplockState, PENDING )) {
|
|
|
|
Oplock->OplockState = NoOplocksHeld;
|
|
Oplock->FileObject = NULL;
|
|
|
|
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 )) {
|
|
|
|
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 = STATUS_SUCCESS;
|
|
LOGICAL AcquiredMutex;
|
|
|
|
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.
|
|
//
|
|
|
|
AcquiredMutex = TRUE;
|
|
ExAcquireFastMutexUnsafe( Oplock->FastMutex );
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// 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 );
|
|
|
|
//
|
|
// If there is a pending opfilter request then clear that request.
|
|
//
|
|
|
|
} else if (FlagOn( Oplock->OplockState, PENDING )) {
|
|
|
|
Oplock->OplockState = NoOplocksHeld;
|
|
Oplock->FileObject = NULL;
|
|
|
|
try_return( Status = STATUS_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// 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 )) {
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
LOGICAL AcquiredMutex;
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
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;
|
|
|
|
//
|
|
// Allocate and initialize the Wait Irp structure.
|
|
//
|
|
|
|
WaitingIrp = FsRtlpAllocatePool( PagedPool, sizeof( WAITING_IRP ));
|
|
|
|
WaitingIrp->Irp = Irp;
|
|
|
|
WaitingIrp->Context = Context;
|
|
WaitingIrp->Information = (ULONG) 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_PTR) 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 )) {
|
|
|
|
ExReleaseFastMutexUnsafe( Oplock->FastMutex );
|
|
|
|
AcquiredMutex = FALSE;
|
|
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
|
|
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 oplock 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;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
|
|
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 oplock 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 (LevelIIIrps && IsListEmpty( &Oplock->IrpOplocksII )) {
|
|
|
|
Oplock->OplockState = NoOplocksHeld;
|
|
}
|
|
|
|
} finally {
|
|
|
|
//
|
|
// No matter how we exit we release the mutex
|
|
//
|
|
|
|
ExReleaseFastMutex( Oplock->FastMutex );
|
|
|
|
DebugTrace( -1, Dbg, "FsRtlCancelOplockIIIrp: Exit\n", 0 );
|
|
}
|
|
|
|
return;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
|
|
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;
|
|
|
|
//
|
|
// 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;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
}
|
|
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
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();
|
|
|
|
UNREFERENCED_PARAMETER (Context);
|
|
|
|
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;
|
|
}
|