mirror of
https://github.com/reactos/reactos.git
synced 2024-08-28 22:28:36 +00:00
![Bișoc George](/assets/img/avatar_default.png)
"Most code should not directly call the platform-specific ObfDereferenceObject() function but use the ObDereferenceObject() macro instead." CORE-16081
1704 lines
52 KiB
C
1704 lines
52 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/fsrtl/oplock.c
|
|
* PURPOSE: Provides an Opportunistic Lock for file system drivers.
|
|
* PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#define NO_OPLOCK 0x1
|
|
#define LEVEL_1_OPLOCK 0x2
|
|
#define BATCH_OPLOCK 0x4
|
|
#define FILTER_OPLOCK 0x8
|
|
#define LEVEL_2_OPLOCK 0x10
|
|
|
|
#define EXCLUSIVE_LOCK 0x40
|
|
#define PENDING_LOCK 0x80
|
|
|
|
#define BROKEN_TO_LEVEL_2 0x100
|
|
#define BROKEN_TO_NONE 0x200
|
|
#define BROKEN_TO_NONE_FROM_LEVEL_2 0x400
|
|
#define BROKEN_TO_CLOSE_PENDING 0x800
|
|
#define BROKEN_ANY (BROKEN_TO_LEVEL_2 | BROKEN_TO_NONE | BROKEN_TO_NONE_FROM_LEVEL_2 | BROKEN_TO_CLOSE_PENDING)
|
|
|
|
#define TAG_OPLOCK 'orSF'
|
|
|
|
typedef struct _INTERNAL_OPLOCK
|
|
{
|
|
/* Level I IRP */
|
|
PIRP ExclusiveIrp;
|
|
/* Level I FILE_OBJECT */
|
|
PFILE_OBJECT FileObject;
|
|
/* Level II IRPs */
|
|
LIST_ENTRY SharedListHead;
|
|
/* IRPs waiting on level I */
|
|
LIST_ENTRY WaitListHead;
|
|
ULONG Flags;
|
|
PFAST_MUTEX IntLock;
|
|
} INTERNAL_OPLOCK, *PINTERNAL_OPLOCK;
|
|
|
|
typedef struct _WAIT_CONTEXT
|
|
{
|
|
LIST_ENTRY WaitListEntry;
|
|
PIRP Irp;
|
|
POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine;
|
|
PVOID CompletionContext;
|
|
ULONG Reserved;
|
|
ULONG_PTR SavedInformation;
|
|
} WAIT_CONTEXT, *PWAIT_CONTEXT;
|
|
|
|
VOID
|
|
NTAPI
|
|
FsRtlNotifyCompletion(IN PVOID Context,
|
|
IN PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlNotifyCompletion(%p, %p)\n", Context, Irp);
|
|
|
|
/* Just complete the IRP */
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
FsRtlCompletionRoutinePriv(IN PVOID Context,
|
|
IN PIRP Irp)
|
|
{
|
|
PKEVENT WaitEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlCompletionRoutinePriv(%p, %p)\n", Context, Irp);
|
|
|
|
/* Set the event */
|
|
WaitEvent = (PKEVENT)Context;
|
|
KeSetEvent(WaitEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
VOID
|
|
FsRtlRemoveAndCompleteWaitIrp(IN PWAIT_CONTEXT WaitCtx)
|
|
{
|
|
PIRP Irp;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlRemoveAndCompleteWaitIrp(%p)\n", WaitCtx);
|
|
|
|
RemoveEntryList(&WaitCtx->WaitListEntry);
|
|
Irp = WaitCtx->Irp;
|
|
|
|
/* No cancel routine anymore */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* Set the information */
|
|
Irp->IoStatus.Information = WaitCtx->SavedInformation;
|
|
/* Set the status according to the fact it got cancel or not */
|
|
Irp->IoStatus.Status = (Irp->Cancel ? STATUS_CANCELLED : STATUS_SUCCESS);
|
|
|
|
/* Call the completion routine */
|
|
WaitCtx->CompletionRoutine(WaitCtx->CompletionContext, Irp);
|
|
|
|
/* And get rid of the now useless wait context */
|
|
ExFreePoolWithTag(WaitCtx, TAG_OPLOCK);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
FsRtlCancelWaitIrp(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PINTERNAL_OPLOCK Oplock;
|
|
PLIST_ENTRY NextEntry;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
|
|
DPRINT("FsRtlCancelWaitIrp(%p, %p)\n", DeviceObject, Irp);
|
|
|
|
/* Get the associated oplock */
|
|
Oplock = (PINTERNAL_OPLOCK)Irp->IoStatus.Information;
|
|
|
|
/* Remove the cancel routine (we're being called!) */
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
/* And release the cancel spin lock (always locked when cancel routine is called) */
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* Now, remove and complete any associated waiter */
|
|
ExAcquireFastMutex(Oplock->IntLock);
|
|
for (NextEntry = Oplock->WaitListHead.Flink;
|
|
NextEntry != &Oplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
|
|
if (WaitCtx->Irp->Cancel)
|
|
{
|
|
FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
|
|
}
|
|
}
|
|
ExReleaseFastMutex(Oplock->IntLock);
|
|
}
|
|
|
|
VOID
|
|
FsRtlWaitOnIrp(IN PINTERNAL_OPLOCK Oplock,
|
|
IN PIRP Irp,
|
|
IN PVOID CompletionContext,
|
|
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine,
|
|
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine,
|
|
IN PKEVENT WaitEvent)
|
|
{
|
|
BOOLEAN Locked;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
|
|
DPRINT("FsRtlWaitOnIrp(%p, %p, %p, %p, %p, %p)\n", Oplock, Irp, CompletionContext, CompletionRoutine, PostIrpRoutine, WaitEvent);
|
|
|
|
/* We must always be called with IntLock locked! */
|
|
Locked = TRUE;
|
|
/* Dirty check for above statement */
|
|
ASSERT(Oplock->IntLock->Owner == KeGetCurrentThread());
|
|
|
|
/* Allocate a wait context for the IRP */
|
|
WaitCtx = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, sizeof(WAIT_CONTEXT), TAG_OPLOCK);
|
|
WaitCtx->Irp = Irp;
|
|
WaitCtx->SavedInformation = Irp->IoStatus.Information;
|
|
/* If caller provided everything required, us it */
|
|
if (CompletionRoutine != NULL)
|
|
{
|
|
WaitCtx->CompletionRoutine = CompletionRoutine;
|
|
WaitCtx->CompletionContext = CompletionContext;
|
|
}
|
|
/* Otherwise, put ourselves */
|
|
else
|
|
{
|
|
WaitCtx->CompletionRoutine = FsRtlCompletionRoutinePriv;
|
|
WaitCtx->CompletionContext = WaitEvent;
|
|
KeInitializeEvent(WaitEvent, NotificationEvent, FALSE);
|
|
}
|
|
|
|
/* If we got a prepost routine, call it now! */
|
|
if (PostIrpRoutine != NULL)
|
|
{
|
|
PostIrpRoutine(CompletionContext, Irp);
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
/* Queue the IRP - it's OK, we're locked */
|
|
InsertHeadList(&Oplock->WaitListHead, &WaitCtx->WaitListEntry);
|
|
|
|
/* Set the oplock as information of the IRP (for the cancel routine)
|
|
* And lock the cancel routine lock for setting it
|
|
*/
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
Irp->IoStatus.Information = (ULONG_PTR)Oplock;
|
|
|
|
/* If there's already a cancel routine
|
|
* Cancel the IRP
|
|
*/
|
|
if (Irp->Cancel)
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
Locked = FALSE;
|
|
|
|
if (CompletionRoutine != NULL)
|
|
{
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
FsRtlCancelWaitIrp(NULL, Irp);
|
|
}
|
|
/* Otherwise, put ourselves as the cancel routine and start waiting */
|
|
else
|
|
{
|
|
IoSetCancelRoutine(Irp, FsRtlCancelWaitIrp);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
if (CompletionRoutine != NULL)
|
|
{
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
else
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
Locked = FALSE;
|
|
KeWaitForSingleObject(WaitEvent, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
/* If we didn't unlock yet, do it now */
|
|
if (Locked)
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
FsRtlOplockBreakNotify(IN PINTERNAL_OPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack,
|
|
IN PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlOplockBreakNotify(%p, %p, %p)\n", Oplock, Stack, Irp);
|
|
|
|
/* No oplock, no break to notify */
|
|
if (Oplock == NULL)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Notify by completing the IRP, unless we have broken to shared */
|
|
ExAcquireFastMutexUnsafe(Oplock->IntLock);
|
|
if (!BooleanFlagOn(Oplock->Flags, BROKEN_TO_LEVEL_2))
|
|
{
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* If it's pending, just complete the IRP and get rid of the oplock */
|
|
if (BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
|
|
{
|
|
Oplock->FileObject = NULL;
|
|
Oplock->Flags = NO_OPLOCK;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Otherwise, wait on the IRP */
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
FsRtlWaitOnIrp(Oplock, Irp, NULL, FsRtlNotifyCompletion, NULL, NULL);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FsRtlRemoveAndCompleteIrp(IN PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION Stack;
|
|
|
|
DPRINT("FsRtlRemoveAndCompleteIrp(%p)\n", Irp);
|
|
|
|
Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
/* Remove our extra ref */
|
|
ObDereferenceObject(Stack->FileObject);
|
|
|
|
/* Remove our cancel routine */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* Remove the IRP from the list it may be in (wait or shared) */
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
|
|
/* And complete! */
|
|
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Irp->IoStatus.Status = (Irp->Cancel ? STATUS_CANCELLED : STATUS_SUCCESS);
|
|
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
FsRtlCancelOplockIIIrp(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PINTERNAL_OPLOCK Oplock;
|
|
PLIST_ENTRY NextEntry;
|
|
PIRP ListIrp;
|
|
BOOLEAN Removed;
|
|
|
|
DPRINT("FsRtlCancelOplockIIIrp(%p, %p)\n", DeviceObject, Irp);
|
|
|
|
/* Get the associated oplock */
|
|
Oplock = (PINTERNAL_OPLOCK)Irp->IoStatus.Information;
|
|
|
|
/* Remove the cancel routine (it's OK, we're the cancel routine! )*/
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* Nothing removed yet */
|
|
Removed = FALSE;
|
|
ExAcquireFastMutex(Oplock->IntLock);
|
|
/* Browse all the IRPs associated to the shared lock */
|
|
for (NextEntry = Oplock->SharedListHead.Flink;
|
|
NextEntry != &Oplock->SharedListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
/* If canceled, remove it */
|
|
if (ListIrp->Cancel)
|
|
{
|
|
FsRtlRemoveAndCompleteIrp(ListIrp);
|
|
Removed = TRUE;
|
|
}
|
|
}
|
|
|
|
/* If no IRP left, the oplock is gone */
|
|
if (Removed && IsListEmpty(&Oplock->SharedListHead))
|
|
{
|
|
Oplock->Flags = NO_OPLOCK;
|
|
}
|
|
/* Don't forget to release the mutex */
|
|
ExReleaseFastMutex(Oplock->IntLock);
|
|
}
|
|
|
|
NTSTATUS
|
|
FsRtlAcknowledgeOplockBreak(IN PINTERNAL_OPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack,
|
|
IN PIRP Irp,
|
|
IN BOOLEAN SwitchToLevel2)
|
|
{
|
|
PLIST_ENTRY NextEntry;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
BOOLEAN Deref;
|
|
BOOLEAN Locked;
|
|
|
|
DPRINT("FsRtlAcknowledgeOplockBreak(%p, %p, %p, %u)\n", Oplock, Stack, Irp, Unknown);
|
|
|
|
/* No oplock, nothing to acknowledge */
|
|
if (Oplock == NULL)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
}
|
|
|
|
/* Acquire oplock internal lock */
|
|
ExAcquireFastMutexUnsafe(Oplock->IntLock);
|
|
Locked = TRUE;
|
|
/* Does it match the file? */
|
|
if (Oplock->FileObject != Stack->FileObject)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
}
|
|
|
|
/* Assume we'll have to deref our extra ref (level I) */
|
|
Deref = TRUE;
|
|
|
|
/* If we got broken to level 2 and asked for a shared lock
|
|
* switch the oplock to shared
|
|
*/
|
|
if (SwitchToLevel2 && BooleanFlagOn(Oplock->Flags, BROKEN_TO_LEVEL_2))
|
|
{
|
|
/* The IRP cannot be synchronous, we'll move it to the LEVEL_2 IRPs */
|
|
ASSERT(!IoIsOperationSynchronous(Irp));
|
|
|
|
/* Mark the IRP pending, and queue it for the shared IRPs */
|
|
IoMarkIrpPending(Irp);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
InsertTailList(&Oplock->SharedListHead, &Irp->Tail.Overlay.ListEntry);
|
|
|
|
/* Don't deref, we're not done yet */
|
|
Deref = FALSE;
|
|
/* And mark we've got a shared lock */
|
|
Oplock->Flags = LEVEL_2_OPLOCK;
|
|
/* To find the lock back on cancel */
|
|
Irp->IoStatus.Information = (ULONG_PTR)Oplock;
|
|
|
|
/* Acquire the spinlock to set the cancel routine */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
/* If IRP got canceled, call it immediately */
|
|
if (Irp->Cancel)
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
Locked = FALSE;
|
|
FsRtlCancelOplockIIIrp(NULL, Irp);
|
|
}
|
|
/* Otherwise, just set our cancel routine */
|
|
else
|
|
{
|
|
IoSetCancelRoutine(Irp, FsRtlCancelOplockIIIrp);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
}
|
|
}
|
|
/* If oplock got broken, remove it */
|
|
else if (BooleanFlagOn(Oplock->Flags, (BROKEN_TO_NONE | BROKEN_TO_LEVEL_2)))
|
|
{
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IofCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
Oplock->Flags = NO_OPLOCK;
|
|
}
|
|
/* Same, but precise we got broken from none to shared */
|
|
else if (BooleanFlagOn(Oplock->Flags, BROKEN_TO_NONE_FROM_LEVEL_2))
|
|
{
|
|
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IofCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
Oplock->Flags = NO_OPLOCK;
|
|
}
|
|
|
|
/* Now, complete any IRP waiting */
|
|
for (NextEntry = Oplock->WaitListHead.Flink;
|
|
NextEntry != &Oplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
|
|
}
|
|
|
|
/* If we dropped oplock, remove our extra ref */
|
|
if (Deref)
|
|
{
|
|
ObDereferenceObject(Oplock->FileObject);
|
|
}
|
|
/* And unset FO: no oplock left or shared */
|
|
Oplock->FileObject = NULL;
|
|
|
|
/* Don't leak the mutex! */
|
|
if (Locked)
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
FsRtlOpBatchBreakClosePending(IN PINTERNAL_OPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack,
|
|
IN PIRP Irp)
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY NextEntry;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlOpBatchBreakClosePending(%p, %p, %p)\n", Oplock, Stack, Irp);
|
|
|
|
/* No oplock, that's not legit! */
|
|
if (Oplock == NULL)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
ExAcquireFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
/* First of all, check if all conditions are met:
|
|
* Correct FO + broken oplock
|
|
*/
|
|
if (Oplock->FileObject == Stack->FileObject && (BooleanFlagOn(Oplock->Flags, (BROKEN_TO_LEVEL_2 | BROKEN_TO_NONE | BROKEN_TO_NONE_FROM_LEVEL_2))))
|
|
{
|
|
/* If we have a pending or level 1 oplock... */
|
|
if (BooleanFlagOn(Oplock->Flags, (PENDING_LOCK | LEVEL_1_OPLOCK)))
|
|
{
|
|
/* Remove our extra ref from the FO */
|
|
if (Oplock->Flags & LEVEL_1_OPLOCK)
|
|
{
|
|
ObDereferenceObject(Oplock->FileObject);
|
|
}
|
|
|
|
/* And remove the oplock */
|
|
Oplock->Flags = NO_OPLOCK;
|
|
Oplock->FileObject = NULL;
|
|
|
|
/* Complete any waiting IRP */
|
|
for (NextEntry = Oplock->WaitListHead.Flink;
|
|
NextEntry != &Oplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
|
|
}
|
|
}
|
|
/* Otherwise, mark the oplock as close pending */
|
|
else
|
|
{
|
|
ClearFlag(Oplock->Flags, BROKEN_ANY);
|
|
SetFlag(Oplock->Flags, BROKEN_TO_CLOSE_PENDING);
|
|
}
|
|
}
|
|
/* Oplock is in invalid state */
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_OPLOCK_PROTOCOL;
|
|
}
|
|
|
|
/* And complete */
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
PINTERNAL_OPLOCK
|
|
FsRtlAllocateOplock(VOID)
|
|
{
|
|
PINTERNAL_OPLOCK Oplock = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlAllocateOplock()\n");
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate and initialize the oplock */
|
|
Oplock = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE | POOL_COLD_ALLOCATION, sizeof(INTERNAL_OPLOCK), TAG_OPLOCK);
|
|
RtlZeroMemory(Oplock, sizeof(INTERNAL_OPLOCK));
|
|
/* We allocate the fast mutex separately to have it non paged (while the rest of the oplock can be paged) */
|
|
Oplock->IntLock = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, sizeof(FAST_MUTEX), TAG_OPLOCK);
|
|
ExInitializeFastMutex(Oplock->IntLock);
|
|
/* Initialize the IRP list for level 2 oplock */
|
|
InitializeListHead(&Oplock->SharedListHead);
|
|
/* And for the wait IRPs */
|
|
InitializeListHead(&Oplock->WaitListHead);
|
|
Oplock->Flags = NO_OPLOCK;
|
|
}
|
|
_SEH2_FINALLY
|
|
{
|
|
/* In case of abnormal termination, it means either OPLOCK or FAST_MUTEX allocation failed */
|
|
if (_SEH2_AbnormalTermination())
|
|
{
|
|
/* That FAST_MUTEX, free OPLOCK */
|
|
if (Oplock != NULL)
|
|
{
|
|
ExFreePoolWithTag(Oplock, TAG_OPLOCK);
|
|
Oplock = NULL;
|
|
}
|
|
}
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Oplock;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
FsRtlCancelExclusiveIrp(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
PLIST_ENTRY NextEntry;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
|
|
DPRINT("FsRtlCancelExclusiveIrp(%p, %p)\n", DeviceObject, Irp);
|
|
|
|
/* Get the associated oplock */
|
|
IntOplock = (PINTERNAL_OPLOCK)Irp->IoStatus.Information;
|
|
|
|
/* Remove the cancel routine (us!) and release the cancel spinlock */
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* Acquire our internal FAST_MUTEX */
|
|
ExAcquireFastMutex(IntOplock->IntLock);
|
|
/* If we had an exclusive IRP */
|
|
if (IntOplock->ExclusiveIrp != NULL && IntOplock->ExclusiveIrp->Cancel)
|
|
{
|
|
/* Cancel it, and remove it from the oplock */
|
|
IntOplock->ExclusiveIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(IntOplock->ExclusiveIrp, IO_DISK_INCREMENT);
|
|
IntOplock->ExclusiveIrp = NULL;
|
|
|
|
/* Dereference the fileobject and remove the oplock */
|
|
ObDereferenceObject(IntOplock->FileObject);
|
|
IntOplock->FileObject = NULL;
|
|
IntOplock->Flags = NO_OPLOCK;
|
|
|
|
/* And complete any waiting IRP */
|
|
for (NextEntry = IntOplock->WaitListHead.Flink;
|
|
NextEntry != &IntOplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
|
|
}
|
|
}
|
|
|
|
/* Done! */
|
|
ExReleaseFastMutexUnsafe(IntOplock->IntLock);
|
|
}
|
|
|
|
NTSTATUS
|
|
FsRtlRequestExclusiveOplock(IN POPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack,
|
|
IN PIRP Irp,
|
|
IN ULONG Flags)
|
|
{
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
PIRP ListIrp;
|
|
BOOLEAN Locked;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("FsRtlRequestExclusiveOplock(%p, %p, %p, %lu)\n", Oplock, Stack, Irp, Flags);
|
|
|
|
IntOplock = *Oplock;
|
|
Locked = FALSE;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Time to work! */
|
|
_SEH2_TRY
|
|
{
|
|
/* Was the oplock already allocated? If not, do it now! */
|
|
if (IntOplock == NULL)
|
|
{
|
|
*Oplock = FsRtlAllocateOplock();
|
|
IntOplock = *Oplock;
|
|
}
|
|
|
|
/* Acquire our internal lock */
|
|
ExAcquireFastMutexUnsafe(IntOplock->IntLock);
|
|
Locked = TRUE;
|
|
|
|
/* If we request exclusiveness, a filter or a pending oplock, grant it */
|
|
if (Flags == (EXCLUSIVE_LOCK | PENDING_LOCK | FILTER_OPLOCK))
|
|
{
|
|
/* Either no oplock, or pending */
|
|
ASSERT(BooleanFlagOn(IntOplock->Flags, (NO_OPLOCK | PENDING_LOCK)));
|
|
IntOplock->ExclusiveIrp = Irp;
|
|
IntOplock->FileObject = Stack->FileObject;
|
|
IntOplock->Flags = (EXCLUSIVE_LOCK | PENDING_LOCK | FILTER_OPLOCK);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, shared or no effective oplock */
|
|
if (BooleanFlagOn(IntOplock->Flags, (LEVEL_2_OPLOCK | PENDING_LOCK | NO_OPLOCK)))
|
|
{
|
|
/* The shared IRPs list should contain a single entry! */
|
|
if (IntOplock->Flags == LEVEL_2_OPLOCK)
|
|
{
|
|
ListIrp = CONTAINING_RECORD(IntOplock->SharedListHead.Flink, IRP, Tail.Overlay.ListEntry);
|
|
ASSERT(IntOplock->SharedListHead.Flink == IntOplock->SharedListHead.Blink);
|
|
FsRtlRemoveAndCompleteIrp(ListIrp);
|
|
}
|
|
|
|
/* Set the exclusiveness */
|
|
IntOplock->ExclusiveIrp = Irp;
|
|
IntOplock->FileObject = Stack->FileObject;
|
|
IntOplock->Flags = Flags;
|
|
|
|
/* Mark the IRP pending and reference our file object */
|
|
IoMarkIrpPending(Irp);
|
|
ObReferenceObject(Stack->FileObject);
|
|
Irp->IoStatus.Information = (ULONG_PTR)IntOplock;
|
|
|
|
/* Now, set ourselves as cancel routine */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
/* Unless IRP got canceled, then, just give up */
|
|
if (Irp->Cancel)
|
|
{
|
|
ExReleaseFastMutexUnsafe(IntOplock->IntLock);
|
|
Locked = FALSE;
|
|
FsRtlCancelExclusiveIrp(NULL, Irp);
|
|
Status = STATUS_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
IoSetCancelRoutine(Irp, FsRtlCancelExclusiveIrp);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
}
|
|
}
|
|
/* Cannot set exclusiveness, fail */
|
|
else
|
|
{
|
|
if (Irp != NULL)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* If locked, release */
|
|
_SEH2_FINALLY
|
|
{
|
|
if (Locked)
|
|
{
|
|
ExReleaseFastMutexUnsafe(IntOplock->IntLock);
|
|
}
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
FsRtlRequestOplockII(IN POPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack,
|
|
IN PIRP Irp)
|
|
{
|
|
BOOLEAN Locked;
|
|
NTSTATUS Status;
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
|
|
DPRINT("FsRtlRequestOplockII(%p, %p, %p)\n", Oplock, Stack, Irp);
|
|
|
|
IntOplock = *Oplock;
|
|
Locked = FALSE;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* No oplock yet? Allocate it */
|
|
if (IntOplock == NULL)
|
|
{
|
|
*Oplock = FsRtlAllocateOplock();
|
|
IntOplock = *Oplock;
|
|
}
|
|
|
|
/* Acquire the oplock */
|
|
ExAcquireFastMutexUnsafe(IntOplock->IntLock);
|
|
Locked = TRUE;
|
|
|
|
/* If already shared, or no oplock that's fine! */
|
|
if (BooleanFlagOn(IntOplock->Flags, (LEVEL_2_OPLOCK | NO_OPLOCK)))
|
|
{
|
|
IoMarkIrpPending(Irp);
|
|
/* Granted! */
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
/* Insert in the shared list */
|
|
InsertTailList(&IntOplock->SharedListHead, &Irp->Tail.Overlay.ListEntry);
|
|
|
|
/* Save the associated oplock */
|
|
Irp->IoStatus.Information = (ULONG_PTR)IntOplock;
|
|
|
|
/* The oplock is shared */
|
|
IntOplock->Flags = LEVEL_2_OPLOCK;
|
|
|
|
/* Reference the fileobject */
|
|
ObReferenceObject(Stack->FileObject);
|
|
|
|
/* Set our cancel routine, unless the IRP got canceled in-between */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
if (Irp->Cancel)
|
|
{
|
|
ExReleaseFastMutexUnsafe(IntOplock->IntLock);
|
|
Locked = FALSE;
|
|
FsRtlCancelOplockIIIrp(NULL, Irp);
|
|
Status = STATUS_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
IoSetCancelRoutine(Irp, FsRtlCancelOplockIIIrp);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
}
|
|
}
|
|
/* Otherwise, just fail */
|
|
else
|
|
{
|
|
Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
}
|
|
}
|
|
_SEH2_FINALLY
|
|
{
|
|
if (Locked)
|
|
{
|
|
ExReleaseFastMutexUnsafe(IntOplock->IntLock);
|
|
}
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
FsRtlOplockCleanup(IN PINTERNAL_OPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack)
|
|
{
|
|
PIO_STACK_LOCATION ListStack;
|
|
PLIST_ENTRY NextEntry;
|
|
PIRP ListIrp;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
|
|
DPRINT("FsRtlOplockCleanup(%p, %p)\n", Oplock, Stack);
|
|
|
|
ExAcquireFastMutexUnsafe(Oplock->IntLock);
|
|
/* oplock cleaning only makes sense if there's an oplock */
|
|
if (Oplock->Flags != NO_OPLOCK)
|
|
{
|
|
/* Shared lock */
|
|
if (Oplock->Flags == LEVEL_2_OPLOCK)
|
|
{
|
|
/* Complete any associated IRP */
|
|
for (NextEntry = Oplock->SharedListHead.Flink;
|
|
NextEntry != &Oplock->SharedListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
|
|
ListStack = IoGetCurrentIrpStackLocation(ListIrp);
|
|
|
|
if (Stack->FileObject == ListStack->FileObject)
|
|
{
|
|
FsRtlRemoveAndCompleteIrp(ListIrp);
|
|
}
|
|
}
|
|
|
|
/* If, in the end, no IRP is left, then the lock is gone */
|
|
if (IsListEmpty(&Oplock->SharedListHead))
|
|
{
|
|
Oplock->Flags = NO_OPLOCK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If we have matching file */
|
|
if (Oplock->FileObject == Stack->FileObject)
|
|
{
|
|
/* Oplock wasn't broken (still exclusive), easy case */
|
|
if (!BooleanFlagOn(Oplock->Flags, (BROKEN_ANY | PENDING_LOCK)))
|
|
{
|
|
/* Remove the cancel routine we set previously */
|
|
IoAcquireCancelSpinLock(&Oplock->ExclusiveIrp->CancelIrql);
|
|
IoSetCancelRoutine(Oplock->ExclusiveIrp, NULL);
|
|
IoReleaseCancelSpinLock(Oplock->ExclusiveIrp->CancelIrql);
|
|
|
|
/* And return the fact we broke the oplock to no oplock */
|
|
Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Oplock->ExclusiveIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
/* And complete! */
|
|
IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
|
|
Oplock->ExclusiveIrp = NULL;
|
|
}
|
|
|
|
/* If no pending, we can safely dereference the file object */
|
|
if (!BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
|
|
{
|
|
ObDereferenceObject(Oplock->FileObject);
|
|
}
|
|
|
|
/* Now, remove the oplock */
|
|
Oplock->FileObject = NULL;
|
|
Oplock->Flags = NO_OPLOCK;
|
|
|
|
/* And complete any waiting IRP */
|
|
for (NextEntry = Oplock->WaitListHead.Flink;
|
|
NextEntry != &Oplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlOplockBreakToNone(IN PINTERNAL_OPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack,
|
|
IN PIRP Irp,
|
|
IN PVOID Context,
|
|
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
|
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL)
|
|
{
|
|
PLIST_ENTRY NextEntry;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
PIRP ListIrp;
|
|
KEVENT WaitEvent;
|
|
|
|
DPRINT("FsRtlOplockBreakToNone(%p, %p, %p, %p, %p, %p)\n", Oplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine);
|
|
|
|
ExAcquireFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
/* No oplock to break! */
|
|
if (Oplock->Flags == NO_OPLOCK)
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Not broken yet, but set... Let's do it!
|
|
* Also, we won't break a shared oplock
|
|
*/
|
|
if (!BooleanFlagOn(Oplock->Flags, (BROKEN_ANY | PENDING_LOCK | LEVEL_2_OPLOCK)))
|
|
{
|
|
/* Remove our cancel routine, no longer needed */
|
|
IoAcquireCancelSpinLock(&Oplock->ExclusiveIrp->CancelIrql);
|
|
IoSetCancelRoutine(Oplock->ExclusiveIrp, NULL);
|
|
IoReleaseCancelSpinLock(Oplock->ExclusiveIrp->CancelIrql);
|
|
|
|
/* If the IRP got canceled, we need to cleanup a bit */
|
|
if (Oplock->ExclusiveIrp->Cancel)
|
|
{
|
|
/* Return cancelation */
|
|
Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Oplock->ExclusiveIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
|
|
|
|
/* No oplock left */
|
|
Oplock->Flags = NO_OPLOCK;
|
|
Oplock->ExclusiveIrp = NULL;
|
|
|
|
/* No need for the FO anymore */
|
|
ObDereferenceObject(Oplock->FileObject);
|
|
Oplock->FileObject = NULL;
|
|
|
|
/* And complete any waiting IRP */
|
|
for (NextEntry = Oplock->WaitListHead.Flink;
|
|
NextEntry != &Oplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
|
|
}
|
|
|
|
/* Done! */
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Easier break, just complete :-) */
|
|
Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Oplock->ExclusiveIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
|
|
|
|
/* And remove our exclusive IRP */
|
|
Oplock->ExclusiveIrp = NULL;
|
|
SetFlag(Oplock->Flags, BROKEN_TO_NONE);
|
|
}
|
|
/* Shared lock */
|
|
else if (Oplock->Flags == LEVEL_2_OPLOCK)
|
|
{
|
|
/* Complete any IRP in the shared lock */
|
|
for (NextEntry = Oplock->SharedListHead.Flink;
|
|
NextEntry != &Oplock->SharedListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
|
|
FsRtlRemoveAndCompleteIrp(ListIrp);
|
|
}
|
|
|
|
/* No lock left */
|
|
Oplock->Flags = NO_OPLOCK;
|
|
|
|
/* Done */
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/* If it was broken to level 2, break it to none from level 2 */
|
|
else if (Oplock->Flags & BROKEN_TO_LEVEL_2)
|
|
{
|
|
ClearFlag(Oplock->Flags, BROKEN_TO_LEVEL_2);
|
|
SetFlag(Oplock->Flags, BROKEN_TO_NONE_FROM_LEVEL_2);
|
|
}
|
|
/* If it was pending, just drop the lock */
|
|
else if (BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
|
|
{
|
|
Oplock->Flags = NO_OPLOCK;
|
|
Oplock->FileObject = NULL;
|
|
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* If that's ours, job done */
|
|
if (Oplock->FileObject == Stack->FileObject)
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Otherwise, wait on the IRP */
|
|
if (Stack->MajorFunction != IRP_MJ_CREATE || !BooleanFlagOn(Stack->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED))
|
|
{
|
|
FsRtlWaitOnIrp(Oplock, Irp, Context, CompletionRoutine, PostIrpRoutine, &WaitEvent);
|
|
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/* Done */
|
|
else
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_OPLOCK_BREAK_IN_PROGRESS;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlOplockBreakToII(IN PINTERNAL_OPLOCK Oplock,
|
|
IN PIO_STACK_LOCATION Stack,
|
|
IN PIRP Irp,
|
|
IN PVOID Context,
|
|
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
|
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL)
|
|
{
|
|
PLIST_ENTRY NextEntry;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
KEVENT WaitEvent;
|
|
|
|
DPRINT("FsRtlOplockBreakToII(%p, %p, %p, %p, %p, %p)\n", Oplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine);
|
|
|
|
ExAcquireFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
/* If our lock, or if not exclusively locked, nothing to break! */
|
|
if (!BooleanFlagOn(Oplock->Flags, EXCLUSIVE_LOCK) || Oplock->FileObject == Stack->FileObject)
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* If already broken or not set yet */
|
|
if (BooleanFlagOn(Oplock->Flags, (BROKEN_ANY | PENDING_LOCK)))
|
|
{
|
|
/* Drop oplock if pending */
|
|
if (BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
|
|
{
|
|
Oplock->Flags = NO_OPLOCK;
|
|
Oplock->FileObject = NULL;
|
|
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
/* To break! */
|
|
else
|
|
{
|
|
/* Drop the cancel routine of the exclusive IRP */
|
|
IoAcquireCancelSpinLock(&Oplock->ExclusiveIrp->CancelIrql);
|
|
IoSetCancelRoutine(Oplock->ExclusiveIrp, NULL);
|
|
IoReleaseCancelSpinLock(Oplock->ExclusiveIrp->CancelIrql);
|
|
|
|
/* If it was canceled in between, break to no oplock */
|
|
if (Oplock->ExclusiveIrp->Cancel)
|
|
{
|
|
/* Complete the IRP with cancellation */
|
|
Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Oplock->ExclusiveIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
|
|
|
|
/* And mark we have no longer lock */
|
|
Oplock->Flags = NO_OPLOCK;
|
|
Oplock->ExclusiveIrp = NULL;
|
|
ObDereferenceObject(Oplock->FileObject);
|
|
Oplock->FileObject = NULL;
|
|
|
|
/* Finally, complete any waiter */
|
|
for (NextEntry = Oplock->WaitListHead.Flink;
|
|
NextEntry != &Oplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
|
|
}
|
|
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* It wasn't canceled, so break to shared unless we were alone, in that case we break to no lock! */
|
|
Oplock->ExclusiveIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
if (BooleanFlagOn(Oplock->Flags, (BATCH_OPLOCK | LEVEL_1_OPLOCK)))
|
|
{
|
|
SetFlag(Oplock->Flags, BROKEN_TO_LEVEL_2);
|
|
Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_LEVEL_2;
|
|
}
|
|
else
|
|
{
|
|
SetFlag(Oplock->Flags, BROKEN_TO_NONE);
|
|
Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
}
|
|
/* And complete */
|
|
IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
|
|
Oplock->ExclusiveIrp = NULL;
|
|
}
|
|
|
|
/* Wait if required */
|
|
if (Stack->MajorFunction != IRP_MJ_CREATE || !BooleanFlagOn(Stack->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED))
|
|
{
|
|
FsRtlWaitOnIrp(Oplock, Irp, Context, CompletionRoutine, PostIrpRoutine, &WaitEvent);
|
|
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ExReleaseFastMutexUnsafe(Oplock->IntLock);
|
|
return STATUS_OPLOCK_BREAK_IN_PROGRESS;
|
|
}
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*++
|
|
* @name FsRtlCheckOplock
|
|
* @unimplemented
|
|
*
|
|
* FILLME
|
|
*
|
|
* @param Oplock
|
|
* FILLME
|
|
*
|
|
* @param Irp
|
|
* FILLME
|
|
*
|
|
* @param Context
|
|
* FILLME
|
|
*
|
|
* @param CompletionRoutine
|
|
* FILLME
|
|
*
|
|
* @param PostIrpRoutine
|
|
* FILLME
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlCheckOplock(IN POPLOCK Oplock,
|
|
IN PIRP Irp,
|
|
IN PVOID Context,
|
|
IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
|
|
IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL)
|
|
{
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
PIO_STACK_LOCATION Stack;
|
|
ACCESS_MASK DesiredAccess;
|
|
FILE_INFORMATION_CLASS FileInfo;
|
|
ULONG CreateDisposition;
|
|
|
|
#define BreakToIIIfRequired \
|
|
if (IntOplock->Flags != LEVEL_2_OPLOCK || IntOplock->FileObject != Stack->FileObject) \
|
|
return FsRtlOplockBreakToII(IntOplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine)
|
|
|
|
#define BreakToNoneIfRequired \
|
|
if (IntOplock->Flags == LEVEL_2_OPLOCK || IntOplock->FileObject != Stack->FileObject) \
|
|
return FsRtlOplockBreakToNone(IntOplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine)
|
|
|
|
DPRINT("FsRtlCheckOplock(%p, %p, %p, %p, %p)\n", Oplock, Irp, Context, CompletionRoutine, PostIrpRoutine);
|
|
|
|
IntOplock = *Oplock;
|
|
|
|
/* No oplock, easy! */
|
|
if (IntOplock == NULL)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* No sense on paging */
|
|
if (Irp->Flags & IRP_PAGING_IO)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* No oplock, easy (bis!) */
|
|
if (IntOplock->Flags == NO_OPLOCK)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
/* If cleanup, cleanup the associated oplock & return */
|
|
if (Stack->MajorFunction == IRP_MJ_CLEANUP)
|
|
{
|
|
FsRtlOplockCleanup(IntOplock, Stack);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (Stack->MajorFunction == IRP_MJ_LOCK_CONTROL)
|
|
{
|
|
/* OK for filter */
|
|
if (BooleanFlagOn(IntOplock->Flags, FILTER_OPLOCK))
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Lock operation, we will have to break to no lock if shared or not us */
|
|
BreakToNoneIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (Stack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL)
|
|
{
|
|
/* FSCTL should be safe, unless user wants a write FSCTL */
|
|
if (Stack->Parameters.FileSystemControl.FsControlCode != FSCTL_SET_ZERO_DATA)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* We will have to break for write if shared or not us! */
|
|
BreakToNoneIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (Stack->MajorFunction == IRP_MJ_WRITE)
|
|
{
|
|
/* Write operation, we will have to break if shared or not us */
|
|
BreakToNoneIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (Stack->MajorFunction == IRP_MJ_READ)
|
|
{
|
|
/* If that's filter oplock, it's alright */
|
|
if (BooleanFlagOn(IntOplock->Flags, FILTER_OPLOCK))
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Otherwise, we need to break to shared oplock */
|
|
BreakToIIIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (Stack->MajorFunction == IRP_MJ_CREATE)
|
|
{
|
|
DesiredAccess = Stack->Parameters.Create.SecurityContext->DesiredAccess;
|
|
|
|
/* If that's just for reading, the oplock is fine */
|
|
if ((!(DesiredAccess & ~(SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_READ_DATA)) && !(Stack->Parameters.Create.Options & FILE_RESERVE_OPFILTER))
|
|
|| (BooleanFlagOn(IntOplock->Flags, FILTER_OPLOCK) && !(DesiredAccess & ~(SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_READ_EA | FILE_WRITE_DATA)) && BooleanFlagOn(Stack->Parameters.Create.ShareAccess, FILE_SHARE_READ)))
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Otherwise, check the disposition */
|
|
CreateDisposition = (Stack->Parameters.Create.Options >> 24) & 0x000000FF;
|
|
if (!CreateDisposition || CreateDisposition == FILE_OVERWRITE ||
|
|
CreateDisposition == FILE_OVERWRITE_IF ||
|
|
BooleanFlagOn(Stack->Parameters.Create.Options, FILE_RESERVE_OPFILTER))
|
|
{
|
|
/* Not us, we have to break the oplock! */
|
|
BreakToNoneIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* It's fine, we can have the oplock shared */
|
|
BreakToIIIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (Stack->MajorFunction == IRP_MJ_FLUSH_BUFFERS)
|
|
{
|
|
/* We need to share the lock, if not done yet! */
|
|
BreakToIIIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (Stack->MajorFunction == IRP_MJ_SET_INFORMATION)
|
|
{
|
|
/* Only deal with really specific classes */
|
|
FileInfo = Stack->Parameters.SetFile.FileInformationClass;
|
|
if (FileInfo == FileRenameInformation || FileInfo == FileLinkInformation ||
|
|
FileInfo == FileShortNameInformation)
|
|
{
|
|
/* No need to break */
|
|
if (!BooleanFlagOn(IntOplock->Flags, (FILTER_OPLOCK | BATCH_OPLOCK)))
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/* Otherwise break to none */
|
|
else
|
|
{
|
|
BreakToNoneIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else if (FileInfo == FileAllocationInformation)
|
|
{
|
|
BreakToNoneIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (FileInfo == FileEndOfFileInformation)
|
|
{
|
|
/* Advance only, nothing to do */
|
|
if (Stack->Parameters.SetFile.AdvanceOnly)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Otherwise, attempt to break to none */
|
|
BreakToNoneIfRequired;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
#undef BreakToIIIfRequired
|
|
#undef BreakToNoneIfRequired
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*++
|
|
* @name FsRtlCurrentBatchOplock
|
|
* @implemented
|
|
*
|
|
* FILLME
|
|
*
|
|
* @param Oplock
|
|
* FILLME
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlCurrentBatchOplock(IN POPLOCK Oplock)
|
|
{
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlCurrentBatchOplock(%p)\n", Oplock);
|
|
|
|
IntOplock = *Oplock;
|
|
|
|
/* Only return true if batch or filter oplock */
|
|
if (IntOplock != NULL &&
|
|
BooleanFlagOn(IntOplock->Flags, (FILTER_OPLOCK | BATCH_OPLOCK)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*++
|
|
* @name FsRtlInitializeOplock
|
|
* @implemented
|
|
*
|
|
* FILLME
|
|
*
|
|
* @param Oplock
|
|
* FILLME
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlInitializeOplock(IN OUT POPLOCK Oplock)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* Nothing to do */
|
|
DPRINT("FsRtlInitializeOplock(%p)\n", Oplock);
|
|
}
|
|
|
|
/*++
|
|
* @name FsRtlOplockFsctrl
|
|
* @unimplemented
|
|
*
|
|
* FILLME
|
|
*
|
|
* @param Oplock
|
|
* FILLME
|
|
*
|
|
* @param Irp
|
|
* FILLME
|
|
*
|
|
* @param OpenCount
|
|
* FILLME
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlOplockFsctrl(IN POPLOCK Oplock,
|
|
IN PIRP Irp,
|
|
IN ULONG OpenCount)
|
|
{
|
|
PIO_STACK_LOCATION Stack;
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlOplockFsctrl(%p, %p, %lu)\n", Oplock, Irp, OpenCount);
|
|
|
|
IntOplock = *Oplock;
|
|
Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
/* Make sure it's not called on create */
|
|
if (Stack->MajorFunction != IRP_MJ_CREATE)
|
|
{
|
|
switch (Stack->Parameters.FileSystemControl.FsControlCode)
|
|
{
|
|
case FSCTL_OPLOCK_BREAK_NOTIFY:
|
|
return FsRtlOplockBreakNotify(IntOplock, Stack, Irp);
|
|
|
|
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
|
|
return FsRtlAcknowledgeOplockBreak(IntOplock, Stack, Irp, FALSE);
|
|
|
|
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
|
|
return FsRtlOpBatchBreakClosePending(IntOplock, Stack, Irp);
|
|
|
|
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
|
|
/* We can only grant level 1 if synchronous, and only a single handle to it
|
|
* (plus, not a paging IO - obvious, and not cleanup done...)
|
|
*/
|
|
if (OpenCount == 1 && !IoIsOperationSynchronous(Irp) &&
|
|
!BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
|
|
{
|
|
return FsRtlRequestExclusiveOplock(Oplock, Stack, Irp, EXCLUSIVE_LOCK | LEVEL_1_OPLOCK);
|
|
}
|
|
/* Not matching, fail */
|
|
else
|
|
{
|
|
Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_OPLOCK_NOT_GRANTED;
|
|
}
|
|
|
|
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
|
|
/* Shared can only be granted if no byte-range lock, and async operation
|
|
* (plus, not a paging IO - obvious, and not cleanup done...)
|
|
*/
|
|
if (OpenCount == 0 && !IoIsOperationSynchronous(Irp) &&
|
|
!BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
|
|
{
|
|
return FsRtlRequestOplockII(Oplock, Stack, Irp);
|
|
}
|
|
/* Not matching, fail */
|
|
else
|
|
{
|
|
Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_OPLOCK_NOT_GRANTED;
|
|
}
|
|
|
|
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
|
|
return FsRtlAcknowledgeOplockBreak(IntOplock, Stack, Irp, TRUE);
|
|
|
|
case FSCTL_REQUEST_BATCH_OPLOCK:
|
|
/* Batch oplock can only be granted if there's a byte-range lock and async operation
|
|
* (plus, not a paging IO - obvious, and not cleanup done...)
|
|
*/
|
|
if (OpenCount == 1 && !IoIsOperationSynchronous(Irp) &&
|
|
!BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
|
|
{
|
|
return FsRtlRequestExclusiveOplock(Oplock, Stack, Irp, EXCLUSIVE_LOCK | BATCH_OPLOCK);
|
|
}
|
|
/* Not matching, fail */
|
|
else
|
|
{
|
|
Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_OPLOCK_NOT_GRANTED;
|
|
}
|
|
|
|
case FSCTL_REQUEST_FILTER_OPLOCK:
|
|
/* Filter oplock can only be granted if there's a byte-range lock and async operation
|
|
* (plus, not a paging IO - obvious, and not cleanup done...)
|
|
*/
|
|
if (OpenCount == 1 && !IoIsOperationSynchronous(Irp) &&
|
|
!BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
|
|
{
|
|
return FsRtlRequestExclusiveOplock(Oplock, Stack, Irp, EXCLUSIVE_LOCK | FILTER_OPLOCK);
|
|
}
|
|
/* Not matching, fail */
|
|
else
|
|
{
|
|
Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_OPLOCK_NOT_GRANTED;
|
|
}
|
|
|
|
default:
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
/* That's a create operation! Only grant exclusive if there's a single user handle opened
|
|
* and we're only performing reading operations.
|
|
*/
|
|
if (OpenCount == 1 &&
|
|
!(Stack->Parameters.Create.SecurityContext->DesiredAccess & ~(FILE_READ_ATTRIBUTES | FILE_READ_DATA)) &&
|
|
(Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS) == FILE_SHARE_VALID_FLAGS)
|
|
{
|
|
return FsRtlRequestExclusiveOplock(Oplock, Stack, NULL, EXCLUSIVE_LOCK | PENDING_LOCK | FILTER_OPLOCK);
|
|
}
|
|
|
|
return STATUS_OPLOCK_NOT_GRANTED;
|
|
}
|
|
|
|
/*++
|
|
* @name FsRtlOplockIsFastIoPossible
|
|
* @implemented
|
|
*
|
|
* FILLME
|
|
*
|
|
* @param Oplock
|
|
* FILLME
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlOplockIsFastIoPossible(IN POPLOCK Oplock)
|
|
{
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("FsRtlOplockIsFastIoPossible(%p)\n", Oplock);
|
|
|
|
IntOplock = *Oplock;
|
|
|
|
/* If there's a shared oplock or if it was used for write operation, deny FastIO */
|
|
if (IntOplock != NULL &&
|
|
BooleanFlagOn(IntOplock->Flags, (BROKEN_ANY | LEVEL_2_OPLOCK)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
* @name FsRtlUninitializeOplock
|
|
* @implemented
|
|
*
|
|
* FILLME
|
|
*
|
|
* @param Oplock
|
|
* FILLME
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlUninitializeOplock(IN POPLOCK Oplock)
|
|
{
|
|
PINTERNAL_OPLOCK IntOplock;
|
|
PLIST_ENTRY NextEntry;
|
|
PWAIT_CONTEXT WaitCtx;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION Stack;
|
|
|
|
DPRINT("FsRtlUninitializeOplock(%p)\n", Oplock);
|
|
|
|
IntOplock = *Oplock;
|
|
|
|
/* No oplock, nothing to do */
|
|
if (IntOplock == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Caller won't have the oplock anymore */
|
|
*Oplock = NULL;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ExAcquireFastMutexUnsafe(IntOplock->IntLock);
|
|
|
|
/* If we had IRPs waiting for the lock, complete them */
|
|
for (NextEntry = IntOplock->WaitListHead.Flink;
|
|
NextEntry != &IntOplock->WaitListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
|
|
Irp = WaitCtx->Irp;
|
|
|
|
RemoveEntryList(&WaitCtx->WaitListEntry);
|
|
/* Remove the cancel routine */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* And complete */
|
|
Irp->IoStatus.Information = 0;
|
|
WaitCtx->CompletionRoutine(WaitCtx->CompletionContext, WaitCtx->Irp);
|
|
|
|
ExFreePoolWithTag(WaitCtx, TAG_OPLOCK);
|
|
}
|
|
|
|
/* If we had shared IRPs (LEVEL_2), complete them */
|
|
for (NextEntry = IntOplock->SharedListHead.Flink;
|
|
NextEntry != &IntOplock->SharedListHead;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
|
|
/* Remvoe the cancel routine */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* Dereference the file object */
|
|
Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
ObDereferenceObject(Stack->FileObject);
|
|
|
|
/* And complete */
|
|
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
/* If we have an exclusive IRP, complete it */
|
|
Irp = IntOplock->ExclusiveIrp;
|
|
if (Irp != NULL)
|
|
{
|
|
/* Remvoe the cancel routine */
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/* And complete */
|
|
Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
IntOplock->ExclusiveIrp = NULL;
|
|
|
|
/* If still referenced, dereference */
|
|
if (IntOplock->FileObject != NULL)
|
|
{
|
|
ObDereferenceObject(IntOplock->FileObject);
|
|
}
|
|
}
|
|
}
|
|
_SEH2_FINALLY
|
|
{
|
|
ExReleaseFastMutexUnsafe(IntOplock->IntLock);
|
|
}
|
|
_SEH2_END;
|
|
|
|
ExFreePoolWithTag(IntOplock->IntLock, TAG_OPLOCK);
|
|
ExFreePoolWithTag(IntOplock, TAG_OPLOCK);
|
|
}
|
|
|