2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/oplock.c
5 * PURPOSE: Provides an Opportunistic Lock for file system drivers.
6 * PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org)
9 /* INCLUDES ******************************************************************/
16 #define LEVEL_1_OPLOCK 0x2
17 #define BATCH_OPLOCK 0x4
18 #define FILTER_OPLOCK 0x8
19 #define LEVEL_2_OPLOCK 0x10
21 #define EXCLUSIVE_LOCK 0x40
22 #define PENDING_LOCK 0x80
24 #define BROKEN_TO_LEVEL_2 0x100
25 #define BROKEN_TO_NONE 0x200
26 #define BROKEN_TO_NONE_FROM_LEVEL_2 0x400
27 #define BROKEN_TO_CLOSE_PENDING 0x800
28 #define BROKEN_ANY (BROKEN_TO_LEVEL_2 | BROKEN_TO_NONE | BROKEN_TO_NONE_FROM_LEVEL_2 | BROKEN_TO_CLOSE_PENDING)
30 #define TAG_OPLOCK 'orSF'
32 typedef struct _INTERNAL_OPLOCK
36 /* Level I FILE_OBJECT */
37 PFILE_OBJECT FileObject
;
39 LIST_ENTRY SharedListHead
;
40 /* IRPs waiting on level I */
41 LIST_ENTRY WaitListHead
;
44 } INTERNAL_OPLOCK
, *PINTERNAL_OPLOCK
;
46 typedef struct _WAIT_CONTEXT
48 LIST_ENTRY WaitListEntry
;
50 POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine
;
51 PVOID CompletionContext
;
53 ULONG_PTR SavedInformation
;
54 } WAIT_CONTEXT
, *PWAIT_CONTEXT
;
58 FsRtlNotifyCompletion(IN PVOID Context
,
63 DPRINT("FsRtlNotifyCompletion(%p, %p)\n", Context
, Irp
);
65 /* Just complete the IRP */
66 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
71 FsRtlCompletionRoutinePriv(IN PVOID Context
,
78 DPRINT("FsRtlCompletionRoutinePriv(%p, %p)\n", Context
, Irp
);
81 WaitEvent
= (PKEVENT
)Context
;
82 KeSetEvent(WaitEvent
, IO_NO_INCREMENT
, FALSE
);
86 FsRtlRemoveAndCompleteWaitIrp(IN PWAIT_CONTEXT WaitCtx
)
92 DPRINT("FsRtlRemoveAndCompleteWaitIrp(%p)\n", WaitCtx
);
94 RemoveEntryList(&WaitCtx
->WaitListEntry
);
97 /* No cancel routine anymore */
98 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
99 IoSetCancelRoutine(Irp
, NULL
);
100 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
102 /* Set the information */
103 Irp
->IoStatus
.Information
= WaitCtx
->SavedInformation
;
104 /* Set the status according to the fact it got cancel or not */
105 Irp
->IoStatus
.Status
= (Irp
->Cancel
? STATUS_CANCELLED
: STATUS_SUCCESS
);
107 /* Call the completion routine */
108 WaitCtx
->CompletionRoutine(WaitCtx
->CompletionContext
, Irp
);
110 /* And get rid of the now useless wait context */
111 ExFreePoolWithTag(WaitCtx
, TAG_OPLOCK
);
116 FsRtlCancelWaitIrp(IN PDEVICE_OBJECT DeviceObject
,
119 PINTERNAL_OPLOCK Oplock
;
120 PLIST_ENTRY NextEntry
;
121 PWAIT_CONTEXT WaitCtx
;
123 DPRINT("FsRtlCancelWaitIrp(%p, %p)\n", DeviceObject
, Irp
);
125 /* Get the associated oplock */
126 Oplock
= (PINTERNAL_OPLOCK
)Irp
->IoStatus
.Information
;
128 /* Remove the cancel routine (we're being called!) */
129 IoSetCancelRoutine(Irp
, NULL
);
130 /* And release the cancel spin lock (always locked when cancel routine is called) */
131 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
133 /* Now, remove and complete any associated waiter */
134 ExAcquireFastMutex(Oplock
->IntLock
);
135 for (NextEntry
= Oplock
->WaitListHead
.Flink
;
136 NextEntry
!= &Oplock
->WaitListHead
;
137 NextEntry
= NextEntry
->Flink
)
139 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
141 if (WaitCtx
->Irp
->Cancel
)
143 FsRtlRemoveAndCompleteWaitIrp(WaitCtx
);
146 ExReleaseFastMutex(Oplock
->IntLock
);
150 FsRtlWaitOnIrp(IN PINTERNAL_OPLOCK Oplock
,
152 IN PVOID CompletionContext
,
153 IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine
,
154 IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine
,
155 IN PKEVENT WaitEvent
)
158 PWAIT_CONTEXT WaitCtx
;
160 DPRINT("FsRtlWaitOnIrp(%p, %p, %p, %p, %p, %p)\n", Oplock
, Irp
, CompletionContext
, CompletionRoutine
, PostIrpRoutine
, WaitEvent
);
162 /* We must always be called with IntLock locked! */
164 /* Dirty check for above statement */
165 ASSERT(Oplock
->IntLock
->Owner
== KeGetCurrentThread());
167 /* Allocate a wait context for the IRP */
168 WaitCtx
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
, sizeof(WAIT_CONTEXT
), TAG_OPLOCK
);
170 WaitCtx
->SavedInformation
= Irp
->IoStatus
.Information
;
171 /* If caller provided everything required, us it */
172 if (CompletionRoutine
!= NULL
)
174 WaitCtx
->CompletionRoutine
= CompletionRoutine
;
175 WaitCtx
->CompletionContext
= CompletionContext
;
177 /* Otherwise, put ourselves */
180 WaitCtx
->CompletionRoutine
= FsRtlCompletionRoutinePriv
;
181 WaitCtx
->CompletionContext
= WaitEvent
;
182 KeInitializeEvent(WaitEvent
, NotificationEvent
, FALSE
);
185 /* If we got a prepost routine, call it now! */
186 if (PostIrpRoutine
!= NULL
)
188 PostIrpRoutine(CompletionContext
, Irp
);
191 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
193 /* Queue the IRP - it's OK, we're locked */
194 InsertHeadList(&Oplock
->WaitListHead
, &WaitCtx
->WaitListEntry
);
196 /* Set the oplock as information of the IRP (for the cancel routine)
197 * And lock the cancel routine lock for setting it
199 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
200 Irp
->IoStatus
.Information
= (ULONG_PTR
)Oplock
;
202 /* If there's already a cancel routine
207 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
210 if (CompletionRoutine
!= NULL
)
212 IoMarkIrpPending(Irp
);
214 FsRtlCancelWaitIrp(NULL
, Irp
);
216 /* Otherwise, put ourselves as the cancel routine and start waiting */
219 IoSetCancelRoutine(Irp
, FsRtlCancelWaitIrp
);
220 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
221 if (CompletionRoutine
!= NULL
)
223 IoMarkIrpPending(Irp
);
227 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
229 KeWaitForSingleObject(WaitEvent
, Executive
, KernelMode
, FALSE
, NULL
);
233 /* If we didn't unlock yet, do it now */
236 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
241 FsRtlOplockBreakNotify(IN PINTERNAL_OPLOCK Oplock
,
242 IN PIO_STACK_LOCATION Stack
,
247 DPRINT("FsRtlOplockBreakNotify(%p, %p, %p)\n", Oplock
, Stack
, Irp
);
249 /* No oplock, no break to notify */
252 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
253 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
254 return STATUS_SUCCESS
;
257 /* Notify by completing the IRP, unless we have broken to shared */
258 ExAcquireFastMutexUnsafe(Oplock
->IntLock
);
259 if (!BooleanFlagOn(Oplock
->Flags
, BROKEN_TO_LEVEL_2
))
261 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
262 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
263 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
264 return STATUS_SUCCESS
;
267 /* If it's pending, just complete the IRP and get rid of the oplock */
268 if (BooleanFlagOn(Oplock
->Flags
, PENDING_LOCK
))
270 Oplock
->FileObject
= NULL
;
271 Oplock
->Flags
= NO_OPLOCK
;
272 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
273 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
274 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
275 return STATUS_SUCCESS
;
278 /* Otherwise, wait on the IRP */
279 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
280 FsRtlWaitOnIrp(Oplock
, Irp
, NULL
, FsRtlNotifyCompletion
, NULL
, NULL
);
281 return STATUS_SUCCESS
;
285 FsRtlRemoveAndCompleteIrp(IN PIRP Irp
)
287 PIO_STACK_LOCATION Stack
;
289 DPRINT("FsRtlRemoveAndCompleteIrp(%p)\n", Irp
);
291 Stack
= IoGetCurrentIrpStackLocation(Irp
);
293 /* Remove our extra ref */
294 ObDereferenceObject(Stack
->FileObject
);
296 /* Remove our cancel routine */
297 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
298 IoSetCancelRoutine(Irp
, NULL
);
299 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
301 /* Remove the IRP from the list it may be in (wait or shared) */
302 RemoveEntryList(&Irp
->Tail
.Overlay
.ListEntry
);
305 Irp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
306 Irp
->IoStatus
.Status
= (Irp
->Cancel
? STATUS_CANCELLED
: STATUS_SUCCESS
);
308 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
313 FsRtlCancelOplockIIIrp(IN PDEVICE_OBJECT DeviceObject
,
316 PINTERNAL_OPLOCK Oplock
;
317 PLIST_ENTRY NextEntry
;
321 DPRINT("FsRtlCancelOplockIIIrp(%p, %p)\n", DeviceObject
, Irp
);
323 /* Get the associated oplock */
324 Oplock
= (PINTERNAL_OPLOCK
)Irp
->IoStatus
.Information
;
326 /* Remove the cancel routine (it's OK, we're the cancel routine! )*/
327 IoSetCancelRoutine(Irp
, NULL
);
328 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
330 /* Nothing removed yet */
332 ExAcquireFastMutex(Oplock
->IntLock
);
333 /* Browse all the IRPs associated to the shared lock */
334 for (NextEntry
= Oplock
->SharedListHead
.Flink
;
335 NextEntry
!= &Oplock
->SharedListHead
;
336 NextEntry
= NextEntry
->Flink
)
338 ListIrp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
340 /* If canceled, remove it */
343 FsRtlRemoveAndCompleteIrp(ListIrp
);
348 /* If no IRP left, the oplock is gone */
349 if (Removed
&& IsListEmpty(&Oplock
->SharedListHead
))
351 Oplock
->Flags
= NO_OPLOCK
;
353 /* Don't forget to release the mutex */
354 ExReleaseFastMutex(Oplock
->IntLock
);
358 FsRtlAcknowledgeOplockBreak(IN PINTERNAL_OPLOCK Oplock
,
359 IN PIO_STACK_LOCATION Stack
,
361 IN BOOLEAN SwitchToLevel2
)
363 PLIST_ENTRY NextEntry
;
364 PWAIT_CONTEXT WaitCtx
;
368 DPRINT("FsRtlAcknowledgeOplockBreak(%p, %p, %p, %u)\n", Oplock
, Stack
, Irp
, Unknown
);
370 /* No oplock, nothing to acknowledge */
373 Irp
->IoStatus
.Status
= STATUS_INVALID_OPLOCK_PROTOCOL
;
374 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
375 return STATUS_INVALID_OPLOCK_PROTOCOL
;
378 /* Acquire oplock internal lock */
379 ExAcquireFastMutexUnsafe(Oplock
->IntLock
);
381 /* Does it match the file? */
382 if (Oplock
->FileObject
!= Stack
->FileObject
)
384 Irp
->IoStatus
.Status
= STATUS_INVALID_OPLOCK_PROTOCOL
;
385 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
386 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
387 return STATUS_INVALID_OPLOCK_PROTOCOL
;
390 /* Assume we'll have to deref our extra ref (level I) */
393 /* If we got broken to level 2 and asked for a shared lock
394 * switch the oplock to shared
396 if (SwitchToLevel2
&& BooleanFlagOn(Oplock
->Flags
, BROKEN_TO_LEVEL_2
))
398 /* The IRP cannot be synchronous, we'll move it to the LEVEL_2 IRPs */
399 ASSERT(!IoIsOperationSynchronous(Irp
));
401 /* Mark the IRP pending, and queue it for the shared IRPs */
402 IoMarkIrpPending(Irp
);
403 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
404 InsertTailList(&Oplock
->SharedListHead
, &Irp
->Tail
.Overlay
.ListEntry
);
406 /* Don't deref, we're not done yet */
408 /* And mark we've got a shared lock */
409 Oplock
->Flags
= LEVEL_2_OPLOCK
;
410 /* To find the lock back on cancel */
411 Irp
->IoStatus
.Information
= (ULONG_PTR
)Oplock
;
413 /* Acquire the spinlock to set the cancel routine */
414 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
415 /* If IRP got canceled, call it immediately */
418 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
420 FsRtlCancelOplockIIIrp(NULL
, Irp
);
422 /* Otherwise, just set our cancel routine */
425 IoSetCancelRoutine(Irp
, FsRtlCancelOplockIIIrp
);
426 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
429 /* If oplock got broken, remove it */
430 else if (BooleanFlagOn(Oplock
->Flags
, (BROKEN_TO_NONE
| BROKEN_TO_LEVEL_2
)))
432 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
433 IofCompleteRequest(Irp
, IO_DISK_INCREMENT
);
434 Oplock
->Flags
= NO_OPLOCK
;
436 /* Same, but precise we got broken from none to shared */
437 else if (BooleanFlagOn(Oplock
->Flags
, BROKEN_TO_NONE_FROM_LEVEL_2
))
439 Irp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
440 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
441 IofCompleteRequest(Irp
, IO_DISK_INCREMENT
);
442 Oplock
->Flags
= NO_OPLOCK
;
445 /* Now, complete any IRP waiting */
446 for (NextEntry
= Oplock
->WaitListHead
.Flink
;
447 NextEntry
!= &Oplock
->WaitListHead
;
448 NextEntry
= NextEntry
->Flink
)
450 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
451 FsRtlRemoveAndCompleteWaitIrp(WaitCtx
);
454 /* If we dropped oplock, remove our extra ref */
457 ObfDereferenceObject(Oplock
->FileObject
);
459 /* And unset FO: no oplock left or shared */
460 Oplock
->FileObject
= NULL
;
462 /* Don't leak the mutex! */
465 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
468 return STATUS_SUCCESS
;
472 FsRtlOpBatchBreakClosePending(IN PINTERNAL_OPLOCK Oplock
,
473 IN PIO_STACK_LOCATION Stack
,
477 PLIST_ENTRY NextEntry
;
478 PWAIT_CONTEXT WaitCtx
;
482 DPRINT("FsRtlOpBatchBreakClosePending(%p, %p, %p)\n", Oplock
, Stack
, Irp
);
484 /* No oplock, that's not legit! */
487 Irp
->IoStatus
.Status
= STATUS_INVALID_OPLOCK_PROTOCOL
;
488 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
489 return STATUS_INVALID_OPLOCK_PROTOCOL
;
492 Status
= STATUS_SUCCESS
;
493 ExAcquireFastMutexUnsafe(Oplock
->IntLock
);
495 /* First of all, check if all conditions are met:
496 * Correct FO + broken oplock
498 if (Oplock
->FileObject
== Stack
->FileObject
&& (BooleanFlagOn(Oplock
->Flags
, (BROKEN_TO_LEVEL_2
| BROKEN_TO_NONE
| BROKEN_TO_NONE_FROM_LEVEL_2
))))
500 /* If we have a pending or level 1 oplock... */
501 if (BooleanFlagOn(Oplock
->Flags
, (PENDING_LOCK
| LEVEL_1_OPLOCK
)))
503 /* Remove our extra ref from the FO */
504 if (Oplock
->Flags
& LEVEL_1_OPLOCK
)
506 ObDereferenceObject(Oplock
->FileObject
);
509 /* And remove the oplock */
510 Oplock
->Flags
= NO_OPLOCK
;
511 Oplock
->FileObject
= NULL
;
513 /* Complete any waiting IRP */
514 for (NextEntry
= Oplock
->WaitListHead
.Flink
;
515 NextEntry
!= &Oplock
->WaitListHead
;
516 NextEntry
= NextEntry
->Flink
)
518 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
519 FsRtlRemoveAndCompleteWaitIrp(WaitCtx
);
522 /* Otherwise, mark the oplock as close pending */
525 ClearFlag(Oplock
->Flags
, BROKEN_ANY
);
526 SetFlag(Oplock
->Flags
, BROKEN_TO_CLOSE_PENDING
);
529 /* Oplock is in invalid state */
532 Status
= STATUS_INVALID_OPLOCK_PROTOCOL
;
536 Irp
->IoStatus
.Status
= Status
;
537 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
538 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
544 FsRtlAllocateOplock(VOID
)
546 PINTERNAL_OPLOCK Oplock
= NULL
;
550 DPRINT("FsRtlAllocateOplock()\n");
554 /* Allocate and initialize the oplock */
555 Oplock
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
| POOL_COLD_ALLOCATION
, sizeof(INTERNAL_OPLOCK
), TAG_OPLOCK
);
556 RtlZeroMemory(Oplock
, sizeof(INTERNAL_OPLOCK
));
557 /* We allocate the fast mutex separately to have it non paged (while the rest of the oplock can be paged) */
558 Oplock
->IntLock
= ExAllocatePoolWithTag(NonPagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
, sizeof(FAST_MUTEX
), TAG_OPLOCK
);
559 ExInitializeFastMutex(Oplock
->IntLock
);
560 /* Initialize the IRP list for level 2 oplock */
561 InitializeListHead(&Oplock
->SharedListHead
);
562 /* And for the wait IRPs */
563 InitializeListHead(&Oplock
->WaitListHead
);
564 Oplock
->Flags
= NO_OPLOCK
;
568 /* In case of abnormal termination, it means either OPLOCK or FAST_MUTEX allocation failed */
569 if (_abnormal_termination())
571 /* That FAST_MUTEX, free OPLOCK */
574 ExFreePoolWithTag(Oplock
, TAG_OPLOCK
);
586 FsRtlCancelExclusiveIrp(IN PDEVICE_OBJECT DeviceObject
,
589 PINTERNAL_OPLOCK IntOplock
;
590 PLIST_ENTRY NextEntry
;
591 PWAIT_CONTEXT WaitCtx
;
593 DPRINT("FsRtlCancelExclusiveIrp(%p, %p)\n", DeviceObject
, Irp
);
595 /* Get the associated oplock */
596 IntOplock
= (PINTERNAL_OPLOCK
)Irp
->IoStatus
.Information
;
598 /* Remove the cancel routine (us!) and release the cancel spinlock */
599 IoSetCancelRoutine(Irp
, NULL
);
600 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
602 /* Acquire our internal FAST_MUTEX */
603 ExAcquireFastMutex(IntOplock
->IntLock
);
604 /* If we had an exclusive IRP */
605 if (IntOplock
->ExclusiveIrp
!= NULL
&& IntOplock
->ExclusiveIrp
->Cancel
)
607 /* Cancel it, and remove it from the oplock */
608 IntOplock
->ExclusiveIrp
->IoStatus
.Status
= STATUS_CANCELLED
;
609 IoCompleteRequest(IntOplock
->ExclusiveIrp
, IO_DISK_INCREMENT
);
610 IntOplock
->ExclusiveIrp
= NULL
;
612 /* Dereference the fileobject and remove the oplock */
613 ObDereferenceObject(IntOplock
->FileObject
);
614 IntOplock
->FileObject
= NULL
;
615 IntOplock
->Flags
= NO_OPLOCK
;
617 /* And complete any waiting IRP */
618 for (NextEntry
= IntOplock
->WaitListHead
.Flink
;
619 NextEntry
!= &IntOplock
->WaitListHead
;
620 NextEntry
= NextEntry
->Flink
)
622 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
623 FsRtlRemoveAndCompleteWaitIrp(WaitCtx
);
628 ExReleaseFastMutexUnsafe(IntOplock
->IntLock
);
632 FsRtlRequestExclusiveOplock(IN POPLOCK Oplock
,
633 IN PIO_STACK_LOCATION Stack
,
637 PINTERNAL_OPLOCK IntOplock
;
642 DPRINT("FsRtlRequestExclusiveOplock(%p, %p, %p, %lu)\n", Oplock
, Stack
, Irp
, Flags
);
646 Status
= STATUS_SUCCESS
;
651 /* Was the oplock already allocated? If not, do it now! */
652 if (IntOplock
== NULL
)
654 *Oplock
= FsRtlAllocateOplock();
658 /* Acquire our internal lock */
659 ExAcquireFastMutexUnsafe(IntOplock
->IntLock
);
662 /* If we request exclusiveness, a filter or a pending oplock, grant it */
663 if (Flags
== (EXCLUSIVE_LOCK
| PENDING_LOCK
| FILTER_OPLOCK
))
665 /* Either no oplock, or pending */
666 ASSERT(BooleanFlagOn(IntOplock
->Flags
, (NO_OPLOCK
| PENDING_LOCK
)));
667 IntOplock
->ExclusiveIrp
= Irp
;
668 IntOplock
->FileObject
= Stack
->FileObject
;
669 IntOplock
->Flags
= (EXCLUSIVE_LOCK
| PENDING_LOCK
| FILTER_OPLOCK
);
673 /* Otherwise, shared or no effective oplock */
674 if (BooleanFlagOn(IntOplock
->Flags
, (LEVEL_2_OPLOCK
| PENDING_LOCK
| NO_OPLOCK
)))
676 /* The shared IRPs list should contain a single entry! */
677 if (IntOplock
->Flags
== LEVEL_2_OPLOCK
)
679 ListIrp
= CONTAINING_RECORD(IntOplock
->SharedListHead
.Flink
, IRP
, Tail
.Overlay
.ListEntry
);
680 ASSERT(IntOplock
->SharedListHead
.Flink
== IntOplock
->SharedListHead
.Blink
);
681 FsRtlRemoveAndCompleteIrp(ListIrp
);
684 /* Set the exclusiveness */
685 IntOplock
->ExclusiveIrp
= Irp
;
686 IntOplock
->FileObject
= Stack
->FileObject
;
687 IntOplock
->Flags
= Flags
;
689 /* Mark the IRP pending and reference our file object */
690 IoMarkIrpPending(Irp
);
691 ObReferenceObject(Stack
->FileObject
);
692 Irp
->IoStatus
.Information
= (ULONG_PTR
)IntOplock
;
694 /* Now, set ourselves as cancel routine */
695 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
696 /* Unless IRP got canceled, then, just give up */
699 ExReleaseFastMutexUnsafe(IntOplock
->IntLock
);
701 FsRtlCancelExclusiveIrp(NULL
, Irp
);
702 Status
= STATUS_CANCELLED
;
706 IoSetCancelRoutine(Irp
, FsRtlCancelExclusiveIrp
);
707 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
710 /* Cannot set exclusiveness, fail */
715 Irp
->IoStatus
.Status
= STATUS_OPLOCK_NOT_GRANTED
;
716 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
717 Status
= STATUS_OPLOCK_NOT_GRANTED
;
722 /* If locked, release */
727 ExReleaseFastMutexUnsafe(IntOplock
->IntLock
);
736 FsRtlRequestOplockII(IN POPLOCK Oplock
,
737 IN PIO_STACK_LOCATION Stack
,
742 PINTERNAL_OPLOCK IntOplock
;
744 DPRINT("FsRtlRequestOplockII(%p, %p, %p)\n", Oplock
, Stack
, Irp
);
748 Status
= STATUS_SUCCESS
;
752 /* No oplock yet? Allocate it */
753 if (IntOplock
== NULL
)
755 *Oplock
= FsRtlAllocateOplock();
759 /* Acquire the oplock */
760 ExAcquireFastMutexUnsafe(IntOplock
->IntLock
);
763 /* If already shared, or no oplock that's fine! */
764 if (BooleanFlagOn(IntOplock
->Flags
, (LEVEL_2_OPLOCK
| NO_OPLOCK
)))
766 IoMarkIrpPending(Irp
);
768 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
770 /* Insert in the shared list */
771 InsertTailList(&IntOplock
->SharedListHead
, &Irp
->Tail
.Overlay
.ListEntry
);
773 /* Save the associated oplock */
774 Irp
->IoStatus
.Information
= (ULONG_PTR
)IntOplock
;
776 /* The oplock is shared */
777 IntOplock
->Flags
= LEVEL_2_OPLOCK
;
779 /* Reference the fileobject */
780 ObReferenceObject(Stack
->FileObject
);
782 /* Set our cancel routine, unless the IRP got canceled in-between */
783 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
786 ExReleaseFastMutexUnsafe(IntOplock
->IntLock
);
788 FsRtlCancelOplockIIIrp(NULL
, Irp
);
789 Status
= STATUS_CANCELLED
;
793 IoSetCancelRoutine(Irp
, FsRtlCancelOplockIIIrp
);
794 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
797 /* Otherwise, just fail */
800 Irp
->IoStatus
.Status
= STATUS_OPLOCK_NOT_GRANTED
;
801 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
802 Status
= STATUS_OPLOCK_NOT_GRANTED
;
809 ExReleaseFastMutexUnsafe(IntOplock
->IntLock
);
818 FsRtlOplockCleanup(IN PINTERNAL_OPLOCK Oplock
,
819 IN PIO_STACK_LOCATION Stack
)
821 PIO_STACK_LOCATION ListStack
;
822 PLIST_ENTRY NextEntry
;
824 PWAIT_CONTEXT WaitCtx
;
826 DPRINT("FsRtlOplockCleanup(%p, %p)\n", Oplock
, Stack
);
828 ExAcquireFastMutexUnsafe(Oplock
->IntLock
);
829 /* oplock cleaning only makes sense if there's an oplock */
830 if (Oplock
->Flags
!= NO_OPLOCK
)
833 if (Oplock
->Flags
== LEVEL_2_OPLOCK
)
835 /* Complete any associated IRP */
836 for (NextEntry
= Oplock
->SharedListHead
.Flink
;
837 NextEntry
!= &Oplock
->SharedListHead
;
838 NextEntry
= NextEntry
->Flink
)
840 ListIrp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
841 ListStack
= IoGetCurrentIrpStackLocation(ListIrp
);
843 if (Stack
->FileObject
== ListStack
->FileObject
)
845 FsRtlRemoveAndCompleteIrp(ListIrp
);
849 /* If, in the end, no IRP is left, then the lock is gone */
850 if (IsListEmpty(&Oplock
->SharedListHead
))
852 Oplock
->Flags
= NO_OPLOCK
;
857 /* If we have matching file */
858 if (Oplock
->FileObject
== Stack
->FileObject
)
860 /* Oplock wasn't broken (still exclusive), easy case */
861 if (!BooleanFlagOn(Oplock
->Flags
, (BROKEN_ANY
| PENDING_LOCK
)))
863 /* Remove the cancel routine we set previously */
864 IoAcquireCancelSpinLock(&Oplock
->ExclusiveIrp
->CancelIrql
);
865 IoSetCancelRoutine(Oplock
->ExclusiveIrp
, NULL
);
866 IoReleaseCancelSpinLock(Oplock
->ExclusiveIrp
->CancelIrql
);
868 /* And return the fact we broke the oplock to no oplock */
869 Oplock
->ExclusiveIrp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
870 Oplock
->ExclusiveIrp
->IoStatus
.Status
= STATUS_SUCCESS
;
873 IoCompleteRequest(Oplock
->ExclusiveIrp
, IO_DISK_INCREMENT
);
874 Oplock
->ExclusiveIrp
= NULL
;
877 /* If no pending, we can safely dereference the file object */
878 if (!BooleanFlagOn(Oplock
->Flags
, PENDING_LOCK
))
880 ObDereferenceObject(Oplock
->FileObject
);
883 /* Now, remove the oplock */
884 Oplock
->FileObject
= NULL
;
885 Oplock
->Flags
= NO_OPLOCK
;
887 /* And complete any waiting IRP */
888 for (NextEntry
= Oplock
->WaitListHead
.Flink
;
889 NextEntry
!= &Oplock
->WaitListHead
;
890 NextEntry
= NextEntry
->Flink
)
892 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
893 FsRtlRemoveAndCompleteWaitIrp(WaitCtx
);
898 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
903 FsRtlOplockBreakToNone(IN PINTERNAL_OPLOCK Oplock
,
904 IN PIO_STACK_LOCATION Stack
,
907 IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL
,
908 IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL
)
910 PLIST_ENTRY NextEntry
;
911 PWAIT_CONTEXT WaitCtx
;
915 DPRINT("FsRtlOplockBreakToNone(%p, %p, %p, %p, %p, %p)\n", Oplock
, Stack
, Irp
, Context
, CompletionRoutine
, PostIrpRoutine
);
917 ExAcquireFastMutexUnsafe(Oplock
->IntLock
);
919 /* No oplock to break! */
920 if (Oplock
->Flags
== NO_OPLOCK
)
922 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
923 return STATUS_SUCCESS
;
926 /* Not broken yet, but set... Let's do it!
927 * Also, we won't break a shared oplock
929 if (!BooleanFlagOn(Oplock
->Flags
, (BROKEN_ANY
| PENDING_LOCK
| LEVEL_2_OPLOCK
)))
931 /* Remove our cancel routine, no longer needed */
932 IoAcquireCancelSpinLock(&Oplock
->ExclusiveIrp
->CancelIrql
);
933 IoSetCancelRoutine(Oplock
->ExclusiveIrp
, NULL
);
934 IoReleaseCancelSpinLock(Oplock
->ExclusiveIrp
->CancelIrql
);
936 /* If the IRP got canceled, we need to cleanup a bit */
937 if (Oplock
->ExclusiveIrp
->Cancel
)
939 /* Return cancelation */
940 Oplock
->ExclusiveIrp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
941 Oplock
->ExclusiveIrp
->IoStatus
.Status
= STATUS_CANCELLED
;
942 IoCompleteRequest(Oplock
->ExclusiveIrp
, IO_DISK_INCREMENT
);
945 Oplock
->Flags
= NO_OPLOCK
;
946 Oplock
->ExclusiveIrp
= NULL
;
948 /* No need for the FO anymore */
949 ObDereferenceObject(Oplock
->FileObject
);
950 Oplock
->FileObject
= NULL
;
952 /* And complete any waiting IRP */
953 for (NextEntry
= Oplock
->WaitListHead
.Flink
;
954 NextEntry
!= &Oplock
->WaitListHead
;
955 NextEntry
= NextEntry
->Flink
)
957 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
958 FsRtlRemoveAndCompleteWaitIrp(WaitCtx
);
962 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
964 return STATUS_SUCCESS
;
967 /* Easier break, just complete :-) */
968 Oplock
->ExclusiveIrp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
969 Oplock
->ExclusiveIrp
->IoStatus
.Status
= STATUS_SUCCESS
;
970 IoCompleteRequest(Oplock
->ExclusiveIrp
, IO_DISK_INCREMENT
);
972 /* And remove our exclusive IRP */
973 Oplock
->ExclusiveIrp
= NULL
;
974 SetFlag(Oplock
->Flags
, BROKEN_TO_NONE
);
977 else if (Oplock
->Flags
== LEVEL_2_OPLOCK
)
979 /* Complete any IRP in the shared lock */
980 for (NextEntry
= Oplock
->SharedListHead
.Flink
;
981 NextEntry
!= &Oplock
->SharedListHead
;
982 NextEntry
= NextEntry
->Flink
)
984 ListIrp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
985 FsRtlRemoveAndCompleteIrp(ListIrp
);
989 Oplock
->Flags
= NO_OPLOCK
;
992 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
993 return STATUS_SUCCESS
;
995 /* If it was broken to level 2, break it to none from level 2 */
996 else if (Oplock
->Flags
& BROKEN_TO_LEVEL_2
)
998 ClearFlag(Oplock
->Flags
, BROKEN_TO_LEVEL_2
);
999 SetFlag(Oplock
->Flags
, BROKEN_TO_NONE_FROM_LEVEL_2
);
1001 /* If it was pending, just drop the lock */
1002 else if (BooleanFlagOn(Oplock
->Flags
, PENDING_LOCK
))
1004 Oplock
->Flags
= NO_OPLOCK
;
1005 Oplock
->FileObject
= NULL
;
1007 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1008 return STATUS_SUCCESS
;
1011 /* If that's ours, job done */
1012 if (Oplock
->FileObject
== Stack
->FileObject
)
1014 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1015 return STATUS_SUCCESS
;
1018 /* Otherwise, wait on the IRP */
1019 if (Stack
->MajorFunction
!= IRP_MJ_CREATE
|| !BooleanFlagOn(Stack
->Parameters
.Create
.Options
, FILE_COMPLETE_IF_OPLOCKED
))
1021 FsRtlWaitOnIrp(Oplock
, Irp
, Context
, CompletionRoutine
, PostIrpRoutine
, &WaitEvent
);
1023 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1025 return STATUS_SUCCESS
;
1030 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1031 return STATUS_OPLOCK_BREAK_IN_PROGRESS
;
1037 FsRtlOplockBreakToII(IN PINTERNAL_OPLOCK Oplock
,
1038 IN PIO_STACK_LOCATION Stack
,
1041 IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL
,
1042 IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL
)
1044 PLIST_ENTRY NextEntry
;
1045 PWAIT_CONTEXT WaitCtx
;
1048 DPRINT("FsRtlOplockBreakToII(%p, %p, %p, %p, %p, %p)\n", Oplock
, Stack
, Irp
, Context
, CompletionRoutine
, PostIrpRoutine
);
1050 ExAcquireFastMutexUnsafe(Oplock
->IntLock
);
1052 /* If our lock, or if not exclusively locked, nothing to break! */
1053 if (!BooleanFlagOn(Oplock
->Flags
, EXCLUSIVE_LOCK
) || Oplock
->FileObject
== Stack
->FileObject
)
1055 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1056 return STATUS_SUCCESS
;
1059 /* If already broken or not set yet */
1060 if (BooleanFlagOn(Oplock
->Flags
, (BROKEN_ANY
| PENDING_LOCK
)))
1062 /* Drop oplock if pending */
1063 if (BooleanFlagOn(Oplock
->Flags
, PENDING_LOCK
))
1065 Oplock
->Flags
= NO_OPLOCK
;
1066 Oplock
->FileObject
= NULL
;
1068 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1069 return STATUS_SUCCESS
;
1076 /* Drop the cancel routine of the exclusive IRP */
1077 IoAcquireCancelSpinLock(&Oplock
->ExclusiveIrp
->CancelIrql
);
1078 IoSetCancelRoutine(Oplock
->ExclusiveIrp
, NULL
);
1079 IoReleaseCancelSpinLock(Oplock
->ExclusiveIrp
->CancelIrql
);
1081 /* If it was canceled in between, break to no oplock */
1082 if (Oplock
->ExclusiveIrp
->Cancel
)
1084 /* Complete the IRP with cancellation */
1085 Oplock
->ExclusiveIrp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
1086 Oplock
->ExclusiveIrp
->IoStatus
.Status
= STATUS_CANCELLED
;
1087 IoCompleteRequest(Oplock
->ExclusiveIrp
, IO_DISK_INCREMENT
);
1089 /* And mark we have no longer lock */
1090 Oplock
->Flags
= NO_OPLOCK
;
1091 Oplock
->ExclusiveIrp
= NULL
;
1092 ObDereferenceObject(Oplock
->FileObject
);
1093 Oplock
->FileObject
= NULL
;
1095 /* Finally, complete any waiter */
1096 for (NextEntry
= Oplock
->WaitListHead
.Flink
;
1097 NextEntry
!= &Oplock
->WaitListHead
;
1098 NextEntry
= NextEntry
->Flink
)
1100 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
1101 FsRtlRemoveAndCompleteWaitIrp(WaitCtx
);
1104 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1106 return STATUS_SUCCESS
;
1109 /* It wasn't canceled, so break to shared unless we were alone, in that case we break to no lock! */
1110 Oplock
->ExclusiveIrp
->IoStatus
.Status
= STATUS_SUCCESS
;
1111 if (BooleanFlagOn(Oplock
->Flags
, (BATCH_OPLOCK
| LEVEL_1_OPLOCK
)))
1113 SetFlag(Oplock
->Flags
, BROKEN_TO_LEVEL_2
);
1114 Oplock
->ExclusiveIrp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_LEVEL_2
;
1118 SetFlag(Oplock
->Flags
, BROKEN_TO_NONE
);
1119 Oplock
->ExclusiveIrp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
1122 IoCompleteRequest(Oplock
->ExclusiveIrp
, IO_DISK_INCREMENT
);
1123 Oplock
->ExclusiveIrp
= NULL
;
1126 /* Wait if required */
1127 if (Stack
->MajorFunction
!= IRP_MJ_CREATE
|| !BooleanFlagOn(Stack
->Parameters
.Create
.Options
, FILE_COMPLETE_IF_OPLOCKED
))
1129 FsRtlWaitOnIrp(Oplock
, Irp
, Context
, CompletionRoutine
, PostIrpRoutine
, &WaitEvent
);
1131 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1133 return STATUS_SUCCESS
;
1137 ExReleaseFastMutexUnsafe(Oplock
->IntLock
);
1138 return STATUS_OPLOCK_BREAK_IN_PROGRESS
;
1142 /* PUBLIC FUNCTIONS **********************************************************/
1145 * @name FsRtlCheckOplock
1159 * @param CompletionRoutine
1162 * @param PostIrpRoutine
1172 FsRtlCheckOplock(IN POPLOCK Oplock
,
1175 IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL
,
1176 IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL
)
1178 PINTERNAL_OPLOCK IntOplock
;
1179 PIO_STACK_LOCATION Stack
;
1180 ACCESS_MASK DesiredAccess
;
1181 FILE_INFORMATION_CLASS FileInfo
;
1182 ULONG CreateDisposition
;
1184 #define BreakToIIIfRequired \
1185 if (IntOplock->Flags != LEVEL_2_OPLOCK || IntOplock->FileObject != Stack->FileObject) \
1186 return FsRtlOplockBreakToII(IntOplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine)
1188 #define BreakToNoneIfRequired \
1189 if (IntOplock->Flags == LEVEL_2_OPLOCK || IntOplock->FileObject != Stack->FileObject) \
1190 return FsRtlOplockBreakToNone(IntOplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine)
1192 DPRINT("FsRtlCheckOplock(%p, %p, %p, %p, %p)\n", Oplock
, Irp
, Context
, CompletionRoutine
, PostIrpRoutine
);
1194 IntOplock
= *Oplock
;
1196 /* No oplock, easy! */
1197 if (IntOplock
== NULL
)
1199 return STATUS_SUCCESS
;
1202 /* No sense on paging */
1203 if (Irp
->Flags
& IRP_PAGING_IO
)
1205 return STATUS_SUCCESS
;
1208 /* No oplock, easy (bis!) */
1209 if (IntOplock
->Flags
== NO_OPLOCK
)
1211 return STATUS_SUCCESS
;
1214 Stack
= IoGetCurrentIrpStackLocation(Irp
);
1216 /* If cleanup, cleanup the associated oplock & return */
1217 if (Stack
->MajorFunction
== IRP_MJ_CLEANUP
)
1219 FsRtlOplockCleanup(IntOplock
, Stack
);
1220 return STATUS_SUCCESS
;
1222 else if (Stack
->MajorFunction
== IRP_MJ_LOCK_CONTROL
)
1225 if (BooleanFlagOn(IntOplock
->Flags
, FILTER_OPLOCK
))
1227 return STATUS_SUCCESS
;
1230 /* Lock operation, we will have to break to no lock if shared or not us */
1231 BreakToNoneIfRequired
;
1233 return STATUS_SUCCESS
;
1235 else if (Stack
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
)
1237 /* FSCTL should be safe, unless user wants a write FSCTL */
1238 if (Stack
->Parameters
.FileSystemControl
.FsControlCode
!= FSCTL_SET_ZERO_DATA
)
1240 return STATUS_SUCCESS
;
1243 /* We will have to break for write if shared or not us! */
1244 BreakToNoneIfRequired
;
1246 return STATUS_SUCCESS
;
1248 else if (Stack
->MajorFunction
== IRP_MJ_WRITE
)
1250 /* Write operation, we will have to break if shared or not us */
1251 BreakToNoneIfRequired
;
1253 return STATUS_SUCCESS
;
1255 else if (Stack
->MajorFunction
== IRP_MJ_READ
)
1257 /* If that's filter oplock, it's alright */
1258 if (BooleanFlagOn(IntOplock
->Flags
, FILTER_OPLOCK
))
1260 return STATUS_SUCCESS
;
1263 /* Otherwise, we need to break to shared oplock */
1264 BreakToIIIfRequired
;
1266 return STATUS_SUCCESS
;
1268 else if (Stack
->MajorFunction
== IRP_MJ_CREATE
)
1270 DesiredAccess
= Stack
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
1272 /* If that's just for reading, the oplock is fine */
1273 if ((!(DesiredAccess
& ~(SYNCHRONIZE
| FILE_WRITE_ATTRIBUTES
| FILE_READ_ATTRIBUTES
| FILE_READ_DATA
)) && !(Stack
->Parameters
.Create
.Options
& FILE_RESERVE_OPFILTER
))
1274 || (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
)))
1276 return STATUS_SUCCESS
;
1279 /* Otherwise, check the disposition */
1280 CreateDisposition
= (Stack
->Parameters
.Create
.Options
>> 24) & 0x000000FF;
1281 if (!CreateDisposition
|| CreateDisposition
== FILE_OVERWRITE
||
1282 CreateDisposition
== FILE_OVERWRITE_IF
||
1283 BooleanFlagOn(Stack
->Parameters
.Create
.Options
, FILE_RESERVE_OPFILTER
))
1285 /* Not us, we have to break the oplock! */
1286 BreakToNoneIfRequired
;
1288 return STATUS_SUCCESS
;
1291 /* It's fine, we can have the oplock shared */
1292 BreakToIIIfRequired
;
1294 return STATUS_SUCCESS
;
1296 else if (Stack
->MajorFunction
== IRP_MJ_FLUSH_BUFFERS
)
1298 /* We need to share the lock, if not done yet! */
1299 BreakToIIIfRequired
;
1301 return STATUS_SUCCESS
;
1303 else if (Stack
->MajorFunction
== IRP_MJ_SET_INFORMATION
)
1305 /* Only deal with really specific classes */
1306 FileInfo
= Stack
->Parameters
.SetFile
.FileInformationClass
;
1307 if (FileInfo
== FileRenameInformation
|| FileInfo
== FileLinkInformation
||
1308 FileInfo
== FileShortNameInformation
)
1310 /* No need to break */
1311 if (!BooleanFlagOn(IntOplock
->Flags
, (FILTER_OPLOCK
| BATCH_OPLOCK
)))
1313 return STATUS_SUCCESS
;
1315 /* Otherwise break to none */
1318 BreakToNoneIfRequired
;
1320 return STATUS_SUCCESS
;
1323 else if (FileInfo
== FileAllocationInformation
)
1325 BreakToNoneIfRequired
;
1327 return STATUS_SUCCESS
;
1329 else if (FileInfo
== FileEndOfFileInformation
)
1331 /* Advance only, nothing to do */
1332 if (Stack
->Parameters
.SetFile
.AdvanceOnly
)
1334 return STATUS_SUCCESS
;
1337 /* Otherwise, attempt to break to none */
1338 BreakToNoneIfRequired
;
1340 return STATUS_SUCCESS
;
1344 #undef BreakToIIIfRequired
1345 #undef BreakToNoneIfRequired
1347 return STATUS_SUCCESS
;
1351 * @name FsRtlCurrentBatchOplock
1366 FsRtlCurrentBatchOplock(IN POPLOCK Oplock
)
1368 PINTERNAL_OPLOCK IntOplock
;
1372 DPRINT("FsRtlCurrentBatchOplock(%p)\n", Oplock
);
1374 IntOplock
= *Oplock
;
1376 /* Only return true if batch or filter oplock */
1377 if (IntOplock
!= NULL
&&
1378 BooleanFlagOn(IntOplock
->Flags
, (FILTER_OPLOCK
| BATCH_OPLOCK
)))
1387 * @name FsRtlInitializeOplock
1402 FsRtlInitializeOplock(IN OUT POPLOCK Oplock
)
1407 DPRINT("FsRtlInitializeOplock(%p)\n", Oplock
);
1411 * @name FsRtlOplockFsctrl
1432 FsRtlOplockFsctrl(IN POPLOCK Oplock
,
1436 PIO_STACK_LOCATION Stack
;
1437 PINTERNAL_OPLOCK IntOplock
;
1441 DPRINT("FsRtlOplockFsctrl(%p, %p, %lu)\n", Oplock
, Irp
, OpenCount
);
1443 IntOplock
= *Oplock
;
1444 Stack
= IoGetCurrentIrpStackLocation(Irp
);
1445 /* Make sure it's not called on create */
1446 if (Stack
->MajorFunction
!= IRP_MJ_CREATE
)
1448 switch (Stack
->Parameters
.FileSystemControl
.FsControlCode
)
1450 case FSCTL_OPLOCK_BREAK_NOTIFY
:
1451 return FsRtlOplockBreakNotify(IntOplock
, Stack
, Irp
);
1453 case FSCTL_OPLOCK_BREAK_ACK_NO_2
:
1454 return FsRtlAcknowledgeOplockBreak(IntOplock
, Stack
, Irp
, FALSE
);
1456 case FSCTL_OPBATCH_ACK_CLOSE_PENDING
:
1457 return FsRtlOpBatchBreakClosePending(IntOplock
, Stack
, Irp
);
1459 case FSCTL_REQUEST_OPLOCK_LEVEL_1
:
1460 /* We can only grant level 1 if synchronous, and only a single handle to it
1461 * (plus, not a paging IO - obvious, and not cleanup done...)
1463 if (OpenCount
== 1 && !IoIsOperationSynchronous(Irp
) &&
1464 !BooleanFlagOn(Irp
->Flags
, IRP_SYNCHRONOUS_PAGING_IO
) && !BooleanFlagOn(Stack
->FileObject
->Flags
, FO_CLEANUP_COMPLETE
))
1466 return FsRtlRequestExclusiveOplock(Oplock
, Stack
, Irp
, EXCLUSIVE_LOCK
| LEVEL_1_OPLOCK
);
1468 /* Not matching, fail */
1471 Irp
->IoStatus
.Status
= STATUS_OPLOCK_NOT_GRANTED
;
1472 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1473 return STATUS_OPLOCK_NOT_GRANTED
;
1476 case FSCTL_REQUEST_OPLOCK_LEVEL_2
:
1477 /* Shared can only be granted if no byte-range lock, and async operation
1478 * (plus, not a paging IO - obvious, and not cleanup done...)
1480 if (OpenCount
== 0 && !IoIsOperationSynchronous(Irp
) &&
1481 !BooleanFlagOn(Irp
->Flags
, IRP_SYNCHRONOUS_PAGING_IO
) && !BooleanFlagOn(Stack
->FileObject
->Flags
, FO_CLEANUP_COMPLETE
))
1483 return FsRtlRequestOplockII(Oplock
, Stack
, Irp
);
1485 /* Not matching, fail */
1488 Irp
->IoStatus
.Status
= STATUS_OPLOCK_NOT_GRANTED
;
1489 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1490 return STATUS_OPLOCK_NOT_GRANTED
;
1493 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
:
1494 return FsRtlAcknowledgeOplockBreak(IntOplock
, Stack
, Irp
, TRUE
);
1496 case FSCTL_REQUEST_BATCH_OPLOCK
:
1497 /* Batch oplock can only be granted if there's a byte-range lock and async operation
1498 * (plus, not a paging IO - obvious, and not cleanup done...)
1500 if (OpenCount
== 1 && !IoIsOperationSynchronous(Irp
) &&
1501 !BooleanFlagOn(Irp
->Flags
, IRP_SYNCHRONOUS_PAGING_IO
) && !BooleanFlagOn(Stack
->FileObject
->Flags
, FO_CLEANUP_COMPLETE
))
1503 return FsRtlRequestExclusiveOplock(Oplock
, Stack
, Irp
, EXCLUSIVE_LOCK
| BATCH_OPLOCK
);
1505 /* Not matching, fail */
1508 Irp
->IoStatus
.Status
= STATUS_OPLOCK_NOT_GRANTED
;
1509 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1510 return STATUS_OPLOCK_NOT_GRANTED
;
1513 case FSCTL_REQUEST_FILTER_OPLOCK
:
1514 /* Filter oplock can only be granted if there's a byte-range lock and async operation
1515 * (plus, not a paging IO - obvious, and not cleanup done...)
1517 if (OpenCount
== 1 && !IoIsOperationSynchronous(Irp
) &&
1518 !BooleanFlagOn(Irp
->Flags
, IRP_SYNCHRONOUS_PAGING_IO
) && !BooleanFlagOn(Stack
->FileObject
->Flags
, FO_CLEANUP_COMPLETE
))
1520 return FsRtlRequestExclusiveOplock(Oplock
, Stack
, Irp
, EXCLUSIVE_LOCK
| FILTER_OPLOCK
);
1522 /* Not matching, fail */
1525 Irp
->IoStatus
.Status
= STATUS_OPLOCK_NOT_GRANTED
;
1526 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1527 return STATUS_OPLOCK_NOT_GRANTED
;
1531 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
1532 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1533 return STATUS_INVALID_PARAMETER
;
1537 /* That's a create operation! Only grant exclusive if there's a single user handle opened
1538 * and we're only performing reading operations.
1540 if (OpenCount
== 1 &&
1541 !(Stack
->Parameters
.Create
.SecurityContext
->DesiredAccess
& ~(FILE_READ_ATTRIBUTES
| FILE_READ_DATA
)) &&
1542 (Stack
->Parameters
.Create
.ShareAccess
& FILE_SHARE_VALID_FLAGS
) == FILE_SHARE_VALID_FLAGS
)
1544 return FsRtlRequestExclusiveOplock(Oplock
, Stack
, NULL
, EXCLUSIVE_LOCK
| PENDING_LOCK
| FILTER_OPLOCK
);
1547 return STATUS_OPLOCK_NOT_GRANTED
;
1551 * @name FsRtlOplockIsFastIoPossible
1566 FsRtlOplockIsFastIoPossible(IN POPLOCK Oplock
)
1568 PINTERNAL_OPLOCK IntOplock
;
1572 DPRINT("FsRtlOplockIsFastIoPossible(%p)\n", Oplock
);
1574 IntOplock
= *Oplock
;
1576 /* If there's a shared oplock or if it was used for write operation, deny FastIO */
1577 if (IntOplock
!= NULL
&&
1578 BooleanFlagOn(IntOplock
->Flags
, (BROKEN_ANY
| LEVEL_2_OPLOCK
)))
1587 * @name FsRtlUninitializeOplock
1602 FsRtlUninitializeOplock(IN POPLOCK Oplock
)
1604 PINTERNAL_OPLOCK IntOplock
;
1605 PLIST_ENTRY NextEntry
;
1606 PWAIT_CONTEXT WaitCtx
;
1608 PIO_STACK_LOCATION Stack
;
1610 DPRINT("FsRtlUninitializeOplock(%p)\n", Oplock
);
1612 IntOplock
= *Oplock
;
1614 /* No oplock, nothing to do */
1615 if (IntOplock
== NULL
)
1620 /* Caller won't have the oplock anymore */
1625 ExAcquireFastMutexUnsafe(IntOplock
->IntLock
);
1627 /* If we had IRPs waiting for the lock, complete them */
1628 for (NextEntry
= IntOplock
->WaitListHead
.Flink
;
1629 NextEntry
!= &IntOplock
->WaitListHead
;
1630 NextEntry
= NextEntry
->Flink
)
1632 WaitCtx
= CONTAINING_RECORD(NextEntry
, WAIT_CONTEXT
, WaitListEntry
);
1635 RemoveEntryList(&WaitCtx
->WaitListEntry
);
1636 /* Remove the cancel routine */
1637 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
1638 IoSetCancelRoutine(Irp
, NULL
);
1639 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
1642 Irp
->IoStatus
.Information
= 0;
1643 WaitCtx
->CompletionRoutine(WaitCtx
->CompletionContext
, WaitCtx
->Irp
);
1645 ExFreePoolWithTag(WaitCtx
, TAG_OPLOCK
);
1648 /* If we had shared IRPs (LEVEL_2), complete them */
1649 for (NextEntry
= IntOplock
->SharedListHead
.Flink
;
1650 NextEntry
!= &IntOplock
->SharedListHead
;
1651 NextEntry
= NextEntry
->Flink
)
1653 Irp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
1655 RemoveEntryList(&Irp
->Tail
.Overlay
.ListEntry
);
1657 /* Remvoe the cancel routine */
1658 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
1659 IoSetCancelRoutine(Irp
, NULL
);
1660 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
1662 /* Dereference the file object */
1663 Stack
= IoGetCurrentIrpStackLocation(Irp
);
1664 ObDereferenceObject(Stack
->FileObject
);
1667 Irp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
1668 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1669 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1672 /* If we have an exclusive IRP, complete it */
1673 Irp
= IntOplock
->ExclusiveIrp
;
1676 /* Remvoe the cancel routine */
1677 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
1678 IoSetCancelRoutine(Irp
, NULL
);
1679 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
1682 Irp
->IoStatus
.Information
= FILE_OPLOCK_BROKEN_TO_NONE
;
1683 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1684 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1685 IntOplock
->ExclusiveIrp
= NULL
;
1687 /* If still referenced, dereference */
1688 if (IntOplock
->FileObject
!= NULL
)
1690 ObDereferenceObject(IntOplock
->FileObject
);
1696 ExReleaseFastMutexUnsafe(IntOplock
->IntLock
);
1700 ExFreePoolWithTag(IntOplock
->IntLock
, TAG_OPLOCK
);
1701 ExFreePoolWithTag(IntOplock
, TAG_OPLOCK
);