ReactOS 0.4.15-dev-7958-gcd0bb1a
oplock.c
Go to the documentation of this file.
1/*
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)
7 */
8
9/* INCLUDES ******************************************************************/
10
11#include <ntoskrnl.h>
12#define NDEBUG
13#include <debug.h>
14
15#define NO_OPLOCK 0x1
16#define LEVEL_1_OPLOCK 0x2
17#define BATCH_OPLOCK 0x4
18#define FILTER_OPLOCK 0x8
19#define LEVEL_2_OPLOCK 0x10
20
21#define EXCLUSIVE_LOCK 0x40
22#define PENDING_LOCK 0x80
23
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)
29
30typedef struct _INTERNAL_OPLOCK
31{
32 /* Level I IRP */
34 /* Level I FILE_OBJECT */
36 /* Level II IRPs */
38 /* IRPs waiting on level I */
43
44typedef struct _WAIT_CONTEXT
45{
53
54VOID
57 IN PIRP Irp)
58{
59 PAGED_CODE();
60
61 DPRINT("FsRtlNotifyCompletion(%p, %p)\n", Context, Irp);
62
63 /* Just complete the IRP */
65}
66
67VOID
70 IN PIRP Irp)
71{
72 PKEVENT WaitEvent;
73
74 PAGED_CODE();
75
76 DPRINT("FsRtlCompletionRoutinePriv(%p, %p)\n", Context, Irp);
77
78 /* Set the event */
79 WaitEvent = (PKEVENT)Context;
80 KeSetEvent(WaitEvent, IO_NO_INCREMENT, FALSE);
81}
82
83VOID
85{
86 PIRP Irp;
87
88 PAGED_CODE();
89
90 DPRINT("FsRtlRemoveAndCompleteWaitIrp(%p)\n", WaitCtx);
91
92 RemoveEntryList(&WaitCtx->WaitListEntry);
93 Irp = WaitCtx->Irp;
94
95 /* No cancel routine anymore */
96 IoAcquireCancelSpinLock(&Irp->CancelIrql);
98 IoReleaseCancelSpinLock(Irp->CancelIrql);
99
100 /* Set the information */
101 Irp->IoStatus.Information = WaitCtx->SavedInformation;
102 /* Set the status according to the fact it got cancel or not */
103 Irp->IoStatus.Status = (Irp->Cancel ? STATUS_CANCELLED : STATUS_SUCCESS);
104
105 /* Call the completion routine */
106 WaitCtx->CompletionRoutine(WaitCtx->CompletionContext, Irp);
107
108 /* And get rid of the now useless wait context */
110}
111
112VOID
113NTAPI
115 IN PIRP Irp)
116{
117 PINTERNAL_OPLOCK Oplock;
118 PLIST_ENTRY NextEntry;
119 PWAIT_CONTEXT WaitCtx;
120
121 DPRINT("FsRtlCancelWaitIrp(%p, %p)\n", DeviceObject, Irp);
122
123 /* Get the associated oplock */
124 Oplock = (PINTERNAL_OPLOCK)Irp->IoStatus.Information;
125
126 /* Remove the cancel routine (we're being called!) */
128 /* And release the cancel spin lock (always locked when cancel routine is called) */
129 IoReleaseCancelSpinLock(Irp->CancelIrql);
130
131 /* Now, remove and complete any associated waiter */
133 for (NextEntry = Oplock->WaitListHead.Flink;
134 NextEntry != &Oplock->WaitListHead;
135 NextEntry = NextEntry->Flink)
136 {
137 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
138
139 if (WaitCtx->Irp->Cancel)
140 {
142 }
143 }
145}
146
147VOID
149 IN PIRP Irp,
153 IN PKEVENT WaitEvent)
154{
156 PWAIT_CONTEXT WaitCtx;
157
158 DPRINT("FsRtlWaitOnIrp(%p, %p, %p, %p, %p, %p)\n", Oplock, Irp, CompletionContext, CompletionRoutine, PostIrpRoutine, WaitEvent);
159
160 /* We must always be called with IntLock locked! */
161 Locked = TRUE;
162 /* Dirty check for above statement */
163 ASSERT(Oplock->IntLock->Owner == KeGetCurrentThread());
164
165 /* Allocate a wait context for the IRP */
167 WaitCtx->Irp = Irp;
168 WaitCtx->SavedInformation = Irp->IoStatus.Information;
169 /* If caller provided everything required, us it */
170 if (CompletionRoutine != NULL)
171 {
174 }
175 /* Otherwise, put ourselves */
176 else
177 {
179 WaitCtx->CompletionContext = WaitEvent;
181 }
182
183 /* If we got a prepost routine, call it now! */
184 if (PostIrpRoutine != NULL)
185 {
187 }
188
189 Irp->IoStatus.Status = STATUS_SUCCESS;
190
191 /* Queue the IRP - it's OK, we're locked */
192 InsertHeadList(&Oplock->WaitListHead, &WaitCtx->WaitListEntry);
193
194 /* Set the oplock as information of the IRP (for the cancel routine)
195 * And lock the cancel routine lock for setting it
196 */
197 IoAcquireCancelSpinLock(&Irp->CancelIrql);
198 Irp->IoStatus.Information = (ULONG_PTR)Oplock;
199
200 /* If there's already a cancel routine
201 * Cancel the IRP
202 */
203 if (Irp->Cancel)
204 {
205 ExReleaseFastMutexUnsafe(Oplock->IntLock);
206 Locked = FALSE;
207
208 if (CompletionRoutine != NULL)
209 {
211 }
213 }
214 /* Otherwise, put ourselves as the cancel routine and start waiting */
215 else
216 {
218 IoReleaseCancelSpinLock(Irp->CancelIrql);
219 if (CompletionRoutine != NULL)
220 {
222 }
223 else
224 {
225 ExReleaseFastMutexUnsafe(Oplock->IntLock);
226 Locked = FALSE;
228 }
229 }
230
231 /* If we didn't unlock yet, do it now */
232 if (Locked)
233 {
234 ExReleaseFastMutexUnsafe(Oplock->IntLock);
235 }
236}
237
241 IN PIRP Irp)
242{
243 PAGED_CODE();
244
245 DPRINT("FsRtlOplockBreakNotify(%p, %p, %p)\n", Oplock, Stack, Irp);
246
247 /* No oplock, no break to notify */
248 if (Oplock == NULL)
249 {
250 Irp->IoStatus.Status = STATUS_SUCCESS;
252 return STATUS_SUCCESS;
253 }
254
255 /* Notify by completing the IRP, unless we have broken to shared */
256 ExAcquireFastMutexUnsafe(Oplock->IntLock);
257 if (!BooleanFlagOn(Oplock->Flags, BROKEN_TO_LEVEL_2))
258 {
259 Irp->IoStatus.Status = STATUS_SUCCESS;
261 ExReleaseFastMutexUnsafe(Oplock->IntLock);
262 return STATUS_SUCCESS;
263 }
264
265 /* If it's pending, just complete the IRP and get rid of the oplock */
266 if (BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
267 {
268 Oplock->FileObject = NULL;
269 Oplock->Flags = NO_OPLOCK;
270 Irp->IoStatus.Status = STATUS_SUCCESS;
272 ExReleaseFastMutexUnsafe(Oplock->IntLock);
273 return STATUS_SUCCESS;
274 }
275
276 /* Otherwise, wait on the IRP */
277 Irp->IoStatus.Status = STATUS_SUCCESS;
279 return STATUS_SUCCESS;
280}
281
282VOID
284{
286
287 DPRINT("FsRtlRemoveAndCompleteIrp(%p)\n", Irp);
288
290
291 /* Remove our extra ref */
292 ObDereferenceObject(Stack->FileObject);
293
294 /* Remove our cancel routine */
295 IoAcquireCancelSpinLock(&Irp->CancelIrql);
297 IoReleaseCancelSpinLock(Irp->CancelIrql);
298
299 /* Remove the IRP from the list it may be in (wait or shared) */
300 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
301
302 /* And complete! */
303 Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
304 Irp->IoStatus.Status = (Irp->Cancel ? STATUS_CANCELLED : STATUS_SUCCESS);
305
307}
308
309VOID
310NTAPI
312 IN PIRP Irp)
313{
314 PINTERNAL_OPLOCK Oplock;
315 PLIST_ENTRY NextEntry;
316 PIRP ListIrp;
318
319 DPRINT("FsRtlCancelOplockIIIrp(%p, %p)\n", DeviceObject, Irp);
320
321 /* Get the associated oplock */
322 Oplock = (PINTERNAL_OPLOCK)Irp->IoStatus.Information;
323
324 /* Remove the cancel routine (it's OK, we're the cancel routine! )*/
326 IoReleaseCancelSpinLock(Irp->CancelIrql);
327
328 /* Nothing removed yet */
329 Removed = FALSE;
331 /* Browse all the IRPs associated to the shared lock */
332 for (NextEntry = Oplock->SharedListHead.Flink;
333 NextEntry != &Oplock->SharedListHead;
334 NextEntry = NextEntry->Flink)
335 {
336 ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
337
338 /* If canceled, remove it */
339 if (ListIrp->Cancel)
340 {
342 Removed = TRUE;
343 }
344 }
345
346 /* If no IRP left, the oplock is gone */
347 if (Removed && IsListEmpty(&Oplock->SharedListHead))
348 {
349 Oplock->Flags = NO_OPLOCK;
350 }
351 /* Don't forget to release the mutex */
353}
354
358 IN PIRP Irp,
359 IN BOOLEAN SwitchToLevel2)
360{
361 PLIST_ENTRY NextEntry;
362 PWAIT_CONTEXT WaitCtx;
363 BOOLEAN Deref;
365
366 DPRINT("FsRtlAcknowledgeOplockBreak(%p, %p, %p, %u)\n", Oplock, Stack, Irp, Unknown);
367
368 /* No oplock, nothing to acknowledge */
369 if (Oplock == NULL)
370 {
371 Irp->IoStatus.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
374 }
375
376 /* Acquire oplock internal lock */
377 ExAcquireFastMutexUnsafe(Oplock->IntLock);
378 Locked = TRUE;
379 /* Does it match the file? */
380 if (Oplock->FileObject != Stack->FileObject)
381 {
382 Irp->IoStatus.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
384 ExReleaseFastMutexUnsafe(Oplock->IntLock);
386 }
387
388 /* Assume we'll have to deref our extra ref (level I) */
389 Deref = TRUE;
390
391 /* If we got broken to level 2 and asked for a shared lock
392 * switch the oplock to shared
393 */
394 if (SwitchToLevel2 && BooleanFlagOn(Oplock->Flags, BROKEN_TO_LEVEL_2))
395 {
396 /* The IRP cannot be synchronous, we'll move it to the LEVEL_2 IRPs */
398
399 /* Mark the IRP pending, and queue it for the shared IRPs */
401 Irp->IoStatus.Status = STATUS_SUCCESS;
402 InsertTailList(&Oplock->SharedListHead, &Irp->Tail.Overlay.ListEntry);
403
404 /* Don't deref, we're not done yet */
405 Deref = FALSE;
406 /* And mark we've got a shared lock */
407 Oplock->Flags = LEVEL_2_OPLOCK;
408 /* To find the lock back on cancel */
409 Irp->IoStatus.Information = (ULONG_PTR)Oplock;
410
411 /* Acquire the spinlock to set the cancel routine */
412 IoAcquireCancelSpinLock(&Irp->CancelIrql);
413 /* If IRP got canceled, call it immediately */
414 if (Irp->Cancel)
415 {
416 ExReleaseFastMutexUnsafe(Oplock->IntLock);
417 Locked = FALSE;
419 }
420 /* Otherwise, just set our cancel routine */
421 else
422 {
424 IoReleaseCancelSpinLock(Irp->CancelIrql);
425 }
426 }
427 /* If oplock got broken, remove it */
428 else if (BooleanFlagOn(Oplock->Flags, (BROKEN_TO_NONE | BROKEN_TO_LEVEL_2)))
429 {
430 Irp->IoStatus.Status = STATUS_SUCCESS;
432 Oplock->Flags = NO_OPLOCK;
433 }
434 /* Same, but precise we got broken from none to shared */
435 else if (BooleanFlagOn(Oplock->Flags, BROKEN_TO_NONE_FROM_LEVEL_2))
436 {
437 Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
438 Irp->IoStatus.Status = STATUS_SUCCESS;
440 Oplock->Flags = NO_OPLOCK;
441 }
442
443 /* Now, complete any IRP waiting */
444 for (NextEntry = Oplock->WaitListHead.Flink;
445 NextEntry != &Oplock->WaitListHead;
446 NextEntry = NextEntry->Flink)
447 {
448 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
450 }
451
452 /* If we dropped oplock, remove our extra ref */
453 if (Deref)
454 {
455 ObDereferenceObject(Oplock->FileObject);
456 }
457 /* And unset FO: no oplock left or shared */
458 Oplock->FileObject = NULL;
459
460 /* Don't leak the mutex! */
461 if (Locked)
462 {
463 ExReleaseFastMutexUnsafe(Oplock->IntLock);
464 }
465
466 return STATUS_SUCCESS;
467}
468
472 IN PIRP Irp)
473{
475 PLIST_ENTRY NextEntry;
476 PWAIT_CONTEXT WaitCtx;
477
478 PAGED_CODE();
479
480 DPRINT("FsRtlOpBatchBreakClosePending(%p, %p, %p)\n", Oplock, Stack, Irp);
481
482 /* No oplock, that's not legit! */
483 if (Oplock == NULL)
484 {
485 Irp->IoStatus.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
488 }
489
491 ExAcquireFastMutexUnsafe(Oplock->IntLock);
492
493 /* First of all, check if all conditions are met:
494 * Correct FO + broken oplock
495 */
496 if (Oplock->FileObject == Stack->FileObject && (BooleanFlagOn(Oplock->Flags, (BROKEN_TO_LEVEL_2 | BROKEN_TO_NONE | BROKEN_TO_NONE_FROM_LEVEL_2))))
497 {
498 /* If we have a pending or level 1 oplock... */
499 if (BooleanFlagOn(Oplock->Flags, (PENDING_LOCK | LEVEL_1_OPLOCK)))
500 {
501 /* Remove our extra ref from the FO */
502 if (Oplock->Flags & LEVEL_1_OPLOCK)
503 {
504 ObDereferenceObject(Oplock->FileObject);
505 }
506
507 /* And remove the oplock */
508 Oplock->Flags = NO_OPLOCK;
509 Oplock->FileObject = NULL;
510
511 /* Complete any waiting IRP */
512 for (NextEntry = Oplock->WaitListHead.Flink;
513 NextEntry != &Oplock->WaitListHead;
514 NextEntry = NextEntry->Flink)
515 {
516 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
518 }
519 }
520 /* Otherwise, mark the oplock as close pending */
521 else
522 {
523 ClearFlag(Oplock->Flags, BROKEN_ANY);
524 SetFlag(Oplock->Flags, BROKEN_TO_CLOSE_PENDING);
525 }
526 }
527 /* Oplock is in invalid state */
528 else
529 {
531 }
532
533 /* And complete */
534 Irp->IoStatus.Status = Status;
536 ExReleaseFastMutexUnsafe(Oplock->IntLock);
537
538 return Status;
539}
540
543{
544 PINTERNAL_OPLOCK Oplock = NULL;
545
546 PAGED_CODE();
547
548 DPRINT("FsRtlAllocateOplock()\n");
549
551 {
552 /* Allocate and initialize the oplock */
554 RtlZeroMemory(Oplock, sizeof(INTERNAL_OPLOCK));
555 /* We allocate the fast mutex separately to have it non paged (while the rest of the oplock can be paged) */
558 /* Initialize the IRP list for level 2 oplock */
560 /* And for the wait IRPs */
562 Oplock->Flags = NO_OPLOCK;
563 }
565 {
566 /* In case of abnormal termination, it means either OPLOCK or FAST_MUTEX allocation failed */
568 {
569 /* That FAST_MUTEX, free OPLOCK */
570 if (Oplock != NULL)
571 {
573 Oplock = NULL;
574 }
575 }
576 }
577 _SEH2_END;
578
579 return Oplock;
580}
581
582VOID
583NTAPI
585 IN PIRP Irp)
586{
587 PINTERNAL_OPLOCK IntOplock;
588 PLIST_ENTRY NextEntry;
589 PWAIT_CONTEXT WaitCtx;
590
591 DPRINT("FsRtlCancelExclusiveIrp(%p, %p)\n", DeviceObject, Irp);
592
593 /* Get the associated oplock */
594 IntOplock = (PINTERNAL_OPLOCK)Irp->IoStatus.Information;
595
596 /* Remove the cancel routine (us!) and release the cancel spinlock */
598 IoReleaseCancelSpinLock(Irp->CancelIrql);
599
600 /* Acquire our internal FAST_MUTEX */
601 ExAcquireFastMutex(IntOplock->IntLock);
602 /* If we had an exclusive IRP */
603 if (IntOplock->ExclusiveIrp != NULL && IntOplock->ExclusiveIrp->Cancel)
604 {
605 /* Cancel it, and remove it from the oplock */
608 IntOplock->ExclusiveIrp = NULL;
609
610 /* Dereference the fileobject and remove the oplock */
612 IntOplock->FileObject = NULL;
613 IntOplock->Flags = NO_OPLOCK;
614
615 /* And complete any waiting IRP */
616 for (NextEntry = IntOplock->WaitListHead.Flink;
617 NextEntry != &IntOplock->WaitListHead;
618 NextEntry = NextEntry->Flink)
619 {
620 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
622 }
623 }
624
625 /* Done! */
627}
628
632 IN PIRP Irp,
633 IN ULONG Flags)
634{
635 PINTERNAL_OPLOCK IntOplock;
636 PIRP ListIrp;
639
640 DPRINT("FsRtlRequestExclusiveOplock(%p, %p, %p, %lu)\n", Oplock, Stack, Irp, Flags);
641
642 IntOplock = *Oplock;
643 Locked = FALSE;
645
646 /* Time to work! */
648 {
649 /* Was the oplock already allocated? If not, do it now! */
650 if (IntOplock == NULL)
651 {
652 *Oplock = FsRtlAllocateOplock();
653 IntOplock = *Oplock;
654 }
655
656 /* Acquire our internal lock */
658 Locked = TRUE;
659
660 /* If we request exclusiveness, a filter or a pending oplock, grant it */
662 {
663 /* Either no oplock, or pending */
665 IntOplock->ExclusiveIrp = Irp;
666 IntOplock->FileObject = Stack->FileObject;
668 }
669 else
670 {
671 /* Otherwise, shared or no effective oplock */
673 {
674 /* The shared IRPs list should contain a single entry! */
675 if (IntOplock->Flags == LEVEL_2_OPLOCK)
676 {
677 ListIrp = CONTAINING_RECORD(IntOplock->SharedListHead.Flink, IRP, Tail.Overlay.ListEntry);
678 ASSERT(IntOplock->SharedListHead.Flink == IntOplock->SharedListHead.Blink);
680 }
681
682 /* Set the exclusiveness */
683 IntOplock->ExclusiveIrp = Irp;
684 IntOplock->FileObject = Stack->FileObject;
685 IntOplock->Flags = Flags;
686
687 /* Mark the IRP pending and reference our file object */
689 ObReferenceObject(Stack->FileObject);
690 Irp->IoStatus.Information = (ULONG_PTR)IntOplock;
691
692 /* Now, set ourselves as cancel routine */
693 IoAcquireCancelSpinLock(&Irp->CancelIrql);
694 /* Unless IRP got canceled, then, just give up */
695 if (Irp->Cancel)
696 {
698 Locked = FALSE;
701 }
702 else
703 {
705 IoReleaseCancelSpinLock(Irp->CancelIrql);
706 }
707 }
708 /* Cannot set exclusiveness, fail */
709 else
710 {
711 if (Irp != NULL)
712 {
713 Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
716 }
717 }
718 }
719 }
720 /* If locked, release */
722 {
723 if (Locked)
724 {
726 }
727 }
728 _SEH2_END;
729
730 return Status;
731}
732
736 IN PIRP Irp)
737{
740 PINTERNAL_OPLOCK IntOplock;
741
742 DPRINT("FsRtlRequestOplockII(%p, %p, %p)\n", Oplock, Stack, Irp);
743
744 IntOplock = *Oplock;
745 Locked = FALSE;
747
749 {
750 /* No oplock yet? Allocate it */
751 if (IntOplock == NULL)
752 {
753 *Oplock = FsRtlAllocateOplock();
754 IntOplock = *Oplock;
755 }
756
757 /* Acquire the oplock */
759 Locked = TRUE;
760
761 /* If already shared, or no oplock that's fine! */
762 if (BooleanFlagOn(IntOplock->Flags, (LEVEL_2_OPLOCK | NO_OPLOCK)))
763 {
765 /* Granted! */
766 Irp->IoStatus.Status = STATUS_SUCCESS;
767
768 /* Insert in the shared list */
769 InsertTailList(&IntOplock->SharedListHead, &Irp->Tail.Overlay.ListEntry);
770
771 /* Save the associated oplock */
772 Irp->IoStatus.Information = (ULONG_PTR)IntOplock;
773
774 /* The oplock is shared */
775 IntOplock->Flags = LEVEL_2_OPLOCK;
776
777 /* Reference the fileobject */
778 ObReferenceObject(Stack->FileObject);
779
780 /* Set our cancel routine, unless the IRP got canceled in-between */
781 IoAcquireCancelSpinLock(&Irp->CancelIrql);
782 if (Irp->Cancel)
783 {
785 Locked = FALSE;
788 }
789 else
790 {
792 IoReleaseCancelSpinLock(Irp->CancelIrql);
793 }
794 }
795 /* Otherwise, just fail */
796 else
797 {
798 Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
801 }
802 }
804 {
805 if (Locked)
806 {
808 }
809 }
810 _SEH2_END;
811
812 return Status;
813}
814
815VOID
818{
819 PIO_STACK_LOCATION ListStack;
820 PLIST_ENTRY NextEntry;
821 PIRP ListIrp;
822 PWAIT_CONTEXT WaitCtx;
823
824 DPRINT("FsRtlOplockCleanup(%p, %p)\n", Oplock, Stack);
825
826 ExAcquireFastMutexUnsafe(Oplock->IntLock);
827 /* oplock cleaning only makes sense if there's an oplock */
828 if (Oplock->Flags != NO_OPLOCK)
829 {
830 /* Shared lock */
831 if (Oplock->Flags == LEVEL_2_OPLOCK)
832 {
833 /* Complete any associated IRP */
834 for (NextEntry = Oplock->SharedListHead.Flink;
835 NextEntry != &Oplock->SharedListHead;
836 NextEntry = NextEntry->Flink)
837 {
838 ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
839 ListStack = IoGetCurrentIrpStackLocation(ListIrp);
840
841 if (Stack->FileObject == ListStack->FileObject)
842 {
844 }
845 }
846
847 /* If, in the end, no IRP is left, then the lock is gone */
848 if (IsListEmpty(&Oplock->SharedListHead))
849 {
850 Oplock->Flags = NO_OPLOCK;
851 }
852 }
853 else
854 {
855 /* If we have matching file */
856 if (Oplock->FileObject == Stack->FileObject)
857 {
858 /* Oplock wasn't broken (still exclusive), easy case */
859 if (!BooleanFlagOn(Oplock->Flags, (BROKEN_ANY | PENDING_LOCK)))
860 {
861 /* Remove the cancel routine we set previously */
862 IoAcquireCancelSpinLock(&Oplock->ExclusiveIrp->CancelIrql);
863 IoSetCancelRoutine(Oplock->ExclusiveIrp, NULL);
864 IoReleaseCancelSpinLock(Oplock->ExclusiveIrp->CancelIrql);
865
866 /* And return the fact we broke the oplock to no oplock */
867 Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
868 Oplock->ExclusiveIrp->IoStatus.Status = STATUS_SUCCESS;
869
870 /* And complete! */
871 IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
872 Oplock->ExclusiveIrp = NULL;
873 }
874
875 /* If no pending, we can safely dereference the file object */
876 if (!BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
877 {
878 ObDereferenceObject(Oplock->FileObject);
879 }
880
881 /* Now, remove the oplock */
882 Oplock->FileObject = NULL;
883 Oplock->Flags = NO_OPLOCK;
884
885 /* And complete any waiting IRP */
886 for (NextEntry = Oplock->WaitListHead.Flink;
887 NextEntry != &Oplock->WaitListHead;
888 NextEntry = NextEntry->Flink)
889 {
890 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
892 }
893 }
894 }
895 }
896 ExReleaseFastMutexUnsafe(Oplock->IntLock);
897}
898
900NTAPI
903 IN PIRP Irp,
907{
908 PLIST_ENTRY NextEntry;
909 PWAIT_CONTEXT WaitCtx;
910 PIRP ListIrp;
911 KEVENT WaitEvent;
912
913 DPRINT("FsRtlOplockBreakToNone(%p, %p, %p, %p, %p, %p)\n", Oplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine);
914
915 ExAcquireFastMutexUnsafe(Oplock->IntLock);
916
917 /* No oplock to break! */
918 if (Oplock->Flags == NO_OPLOCK)
919 {
920 ExReleaseFastMutexUnsafe(Oplock->IntLock);
921 return STATUS_SUCCESS;
922 }
923
924 /* Not broken yet, but set... Let's do it!
925 * Also, we won't break a shared oplock
926 */
927 if (!BooleanFlagOn(Oplock->Flags, (BROKEN_ANY | PENDING_LOCK | LEVEL_2_OPLOCK)))
928 {
929 /* Remove our cancel routine, no longer needed */
930 IoAcquireCancelSpinLock(&Oplock->ExclusiveIrp->CancelIrql);
931 IoSetCancelRoutine(Oplock->ExclusiveIrp, NULL);
932 IoReleaseCancelSpinLock(Oplock->ExclusiveIrp->CancelIrql);
933
934 /* If the IRP got canceled, we need to cleanup a bit */
935 if (Oplock->ExclusiveIrp->Cancel)
936 {
937 /* Return cancelation */
938 Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
939 Oplock->ExclusiveIrp->IoStatus.Status = STATUS_CANCELLED;
940 IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
941
942 /* No oplock left */
943 Oplock->Flags = NO_OPLOCK;
944 Oplock->ExclusiveIrp = NULL;
945
946 /* No need for the FO anymore */
947 ObDereferenceObject(Oplock->FileObject);
948 Oplock->FileObject = NULL;
949
950 /* And complete any waiting IRP */
951 for (NextEntry = Oplock->WaitListHead.Flink;
952 NextEntry != &Oplock->WaitListHead;
953 NextEntry = NextEntry->Flink)
954 {
955 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
957 }
958
959 /* Done! */
960 ExReleaseFastMutexUnsafe(Oplock->IntLock);
961
962 return STATUS_SUCCESS;
963 }
964
965 /* Easier break, just complete :-) */
966 Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
967 Oplock->ExclusiveIrp->IoStatus.Status = STATUS_SUCCESS;
968 IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
969
970 /* And remove our exclusive IRP */
971 Oplock->ExclusiveIrp = NULL;
972 SetFlag(Oplock->Flags, BROKEN_TO_NONE);
973 }
974 /* Shared lock */
975 else if (Oplock->Flags == LEVEL_2_OPLOCK)
976 {
977 /* Complete any IRP in the shared lock */
978 for (NextEntry = Oplock->SharedListHead.Flink;
979 NextEntry != &Oplock->SharedListHead;
980 NextEntry = NextEntry->Flink)
981 {
982 ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
984 }
985
986 /* No lock left */
987 Oplock->Flags = NO_OPLOCK;
988
989 /* Done */
990 ExReleaseFastMutexUnsafe(Oplock->IntLock);
991 return STATUS_SUCCESS;
992 }
993 /* If it was broken to level 2, break it to none from level 2 */
994 else if (Oplock->Flags & BROKEN_TO_LEVEL_2)
995 {
996 ClearFlag(Oplock->Flags, BROKEN_TO_LEVEL_2);
997 SetFlag(Oplock->Flags, BROKEN_TO_NONE_FROM_LEVEL_2);
998 }
999 /* If it was pending, just drop the lock */
1000 else if (BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
1001 {
1002 Oplock->Flags = NO_OPLOCK;
1003 Oplock->FileObject = NULL;
1004
1005 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1006 return STATUS_SUCCESS;
1007 }
1008
1009 /* If that's ours, job done */
1010 if (Oplock->FileObject == Stack->FileObject)
1011 {
1012 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1013 return STATUS_SUCCESS;
1014 }
1015
1016 /* Otherwise, wait on the IRP */
1017 if (Stack->MajorFunction != IRP_MJ_CREATE || !BooleanFlagOn(Stack->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED))
1018 {
1020
1021 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1022
1023 return STATUS_SUCCESS;
1024 }
1025 /* Done */
1026 else
1027 {
1028 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1030 }
1031}
1032
1034NTAPI
1037 IN PIRP Irp,
1041{
1042 PLIST_ENTRY NextEntry;
1043 PWAIT_CONTEXT WaitCtx;
1044 KEVENT WaitEvent;
1045
1046 DPRINT("FsRtlOplockBreakToII(%p, %p, %p, %p, %p, %p)\n", Oplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine);
1047
1048 ExAcquireFastMutexUnsafe(Oplock->IntLock);
1049
1050 /* If our lock, or if not exclusively locked, nothing to break! */
1051 if (!BooleanFlagOn(Oplock->Flags, EXCLUSIVE_LOCK) || Oplock->FileObject == Stack->FileObject)
1052 {
1053 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1054 return STATUS_SUCCESS;
1055 }
1056
1057 /* If already broken or not set yet */
1058 if (BooleanFlagOn(Oplock->Flags, (BROKEN_ANY | PENDING_LOCK)))
1059 {
1060 /* Drop oplock if pending */
1061 if (BooleanFlagOn(Oplock->Flags, PENDING_LOCK))
1062 {
1063 Oplock->Flags = NO_OPLOCK;
1064 Oplock->FileObject = NULL;
1065
1066 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1067 return STATUS_SUCCESS;
1068 }
1069
1070 }
1071 /* To break! */
1072 else
1073 {
1074 /* Drop the cancel routine of the exclusive IRP */
1075 IoAcquireCancelSpinLock(&Oplock->ExclusiveIrp->CancelIrql);
1076 IoSetCancelRoutine(Oplock->ExclusiveIrp, NULL);
1077 IoReleaseCancelSpinLock(Oplock->ExclusiveIrp->CancelIrql);
1078
1079 /* If it was canceled in between, break to no oplock */
1080 if (Oplock->ExclusiveIrp->Cancel)
1081 {
1082 /* Complete the IRP with cancellation */
1083 Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
1084 Oplock->ExclusiveIrp->IoStatus.Status = STATUS_CANCELLED;
1085 IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
1086
1087 /* And mark we have no longer lock */
1088 Oplock->Flags = NO_OPLOCK;
1089 Oplock->ExclusiveIrp = NULL;
1090 ObDereferenceObject(Oplock->FileObject);
1091 Oplock->FileObject = NULL;
1092
1093 /* Finally, complete any waiter */
1094 for (NextEntry = Oplock->WaitListHead.Flink;
1095 NextEntry != &Oplock->WaitListHead;
1096 NextEntry = NextEntry->Flink)
1097 {
1098 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
1100 }
1101
1102 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1103
1104 return STATUS_SUCCESS;
1105 }
1106
1107 /* It wasn't canceled, so break to shared unless we were alone, in that case we break to no lock! */
1108 Oplock->ExclusiveIrp->IoStatus.Status = STATUS_SUCCESS;
1109 if (BooleanFlagOn(Oplock->Flags, (BATCH_OPLOCK | LEVEL_1_OPLOCK)))
1110 {
1111 SetFlag(Oplock->Flags, BROKEN_TO_LEVEL_2);
1112 Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_LEVEL_2;
1113 }
1114 else
1115 {
1116 SetFlag(Oplock->Flags, BROKEN_TO_NONE);
1117 Oplock->ExclusiveIrp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
1118 }
1119 /* And complete */
1120 IoCompleteRequest(Oplock->ExclusiveIrp, IO_DISK_INCREMENT);
1121 Oplock->ExclusiveIrp = NULL;
1122 }
1123
1124 /* Wait if required */
1125 if (Stack->MajorFunction != IRP_MJ_CREATE || !BooleanFlagOn(Stack->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED))
1126 {
1128
1129 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1130
1131 return STATUS_SUCCESS;
1132 }
1133 else
1134 {
1135 ExReleaseFastMutexUnsafe(Oplock->IntLock);
1137 }
1138}
1139
1140/* PUBLIC FUNCTIONS **********************************************************/
1141
1142/*++
1143 * @name FsRtlCheckOplock
1144 * @unimplemented
1145 *
1146 * FILLME
1147 *
1148 * @param Oplock
1149 * FILLME
1150 *
1151 * @param Irp
1152 * FILLME
1153 *
1154 * @param Context
1155 * FILLME
1156 *
1157 * @param CompletionRoutine
1158 * FILLME
1159 *
1160 * @param PostIrpRoutine
1161 * FILLME
1162 *
1163 * @return None
1164 *
1165 * @remarks None
1166 *
1167 *--*/
1169NTAPI
1171 IN PIRP Irp,
1175{
1176 PINTERNAL_OPLOCK IntOplock;
1181
1182#define BreakToIIIfRequired \
1183 if (IntOplock->Flags != LEVEL_2_OPLOCK || IntOplock->FileObject != Stack->FileObject) \
1184 return FsRtlOplockBreakToII(IntOplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine)
1185
1186#define BreakToNoneIfRequired \
1187 if (IntOplock->Flags == LEVEL_2_OPLOCK || IntOplock->FileObject != Stack->FileObject) \
1188 return FsRtlOplockBreakToNone(IntOplock, Stack, Irp, Context, CompletionRoutine, PostIrpRoutine)
1189
1190 DPRINT("FsRtlCheckOplock(%p, %p, %p, %p, %p)\n", Oplock, Irp, Context, CompletionRoutine, PostIrpRoutine);
1191
1192 IntOplock = *Oplock;
1193
1194 /* No oplock, easy! */
1195 if (IntOplock == NULL)
1196 {
1197 return STATUS_SUCCESS;
1198 }
1199
1200 /* No sense on paging */
1201 if (Irp->Flags & IRP_PAGING_IO)
1202 {
1203 return STATUS_SUCCESS;
1204 }
1205
1206 /* No oplock, easy (bis!) */
1207 if (IntOplock->Flags == NO_OPLOCK)
1208 {
1209 return STATUS_SUCCESS;
1210 }
1211
1213
1214 /* If cleanup, cleanup the associated oplock & return */
1215 if (Stack->MajorFunction == IRP_MJ_CLEANUP)
1216 {
1217 FsRtlOplockCleanup(IntOplock, Stack);
1218 return STATUS_SUCCESS;
1219 }
1220 else if (Stack->MajorFunction == IRP_MJ_LOCK_CONTROL)
1221 {
1222 /* OK for filter */
1223 if (BooleanFlagOn(IntOplock->Flags, FILTER_OPLOCK))
1224 {
1225 return STATUS_SUCCESS;
1226 }
1227
1228 /* Lock operation, we will have to break to no lock if shared or not us */
1230
1231 return STATUS_SUCCESS;
1232 }
1233 else if (Stack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL)
1234 {
1235 /* FSCTL should be safe, unless user wants a write FSCTL */
1236 if (Stack->Parameters.FileSystemControl.FsControlCode != FSCTL_SET_ZERO_DATA)
1237 {
1238 return STATUS_SUCCESS;
1239 }
1240
1241 /* We will have to break for write if shared or not us! */
1243
1244 return STATUS_SUCCESS;
1245 }
1246 else if (Stack->MajorFunction == IRP_MJ_WRITE)
1247 {
1248 /* Write operation, we will have to break if shared or not us */
1250
1251 return STATUS_SUCCESS;
1252 }
1253 else if (Stack->MajorFunction == IRP_MJ_READ)
1254 {
1255 /* If that's filter oplock, it's alright */
1256 if (BooleanFlagOn(IntOplock->Flags, FILTER_OPLOCK))
1257 {
1258 return STATUS_SUCCESS;
1259 }
1260
1261 /* Otherwise, we need to break to shared oplock */
1263
1264 return STATUS_SUCCESS;
1265 }
1266 else if (Stack->MajorFunction == IRP_MJ_CREATE)
1267 {
1268 DesiredAccess = Stack->Parameters.Create.SecurityContext->DesiredAccess;
1269
1270 /* If that's just for reading, the oplock is fine */
1273 {
1274 return STATUS_SUCCESS;
1275 }
1276
1277 /* Otherwise, check the disposition */
1278 CreateDisposition = (Stack->Parameters.Create.Options >> 24) & 0x000000FF;
1281 BooleanFlagOn(Stack->Parameters.Create.Options, FILE_RESERVE_OPFILTER))
1282 {
1283 /* Not us, we have to break the oplock! */
1285
1286 return STATUS_SUCCESS;
1287 }
1288
1289 /* It's fine, we can have the oplock shared */
1291
1292 return STATUS_SUCCESS;
1293 }
1294 else if (Stack->MajorFunction == IRP_MJ_FLUSH_BUFFERS)
1295 {
1296 /* We need to share the lock, if not done yet! */
1298
1299 return STATUS_SUCCESS;
1300 }
1301 else if (Stack->MajorFunction == IRP_MJ_SET_INFORMATION)
1302 {
1303 /* Only deal with really specific classes */
1304 FileInfo = Stack->Parameters.SetFile.FileInformationClass;
1307 {
1308 /* No need to break */
1309 if (!BooleanFlagOn(IntOplock->Flags, (FILTER_OPLOCK | BATCH_OPLOCK)))
1310 {
1311 return STATUS_SUCCESS;
1312 }
1313 /* Otherwise break to none */
1314 else
1315 {
1317
1318 return STATUS_SUCCESS;
1319 }
1320 }
1322 {
1324
1325 return STATUS_SUCCESS;
1326 }
1328 {
1329 /* Advance only, nothing to do */
1330 if (Stack->Parameters.SetFile.AdvanceOnly)
1331 {
1332 return STATUS_SUCCESS;
1333 }
1334
1335 /* Otherwise, attempt to break to none */
1337
1338 return STATUS_SUCCESS;
1339 }
1340 }
1341
1342#undef BreakToIIIfRequired
1343#undef BreakToNoneIfRequired
1344
1345 return STATUS_SUCCESS;
1346}
1347
1348/*++
1349 * @name FsRtlCurrentBatchOplock
1350 * @implemented
1351 *
1352 * FILLME
1353 *
1354 * @param Oplock
1355 * FILLME
1356 *
1357 * @return None
1358 *
1359 * @remarks None
1360 *
1361 *--*/
1362BOOLEAN
1363NTAPI
1365{
1366 PINTERNAL_OPLOCK IntOplock;
1367
1368 PAGED_CODE();
1369
1370 DPRINT("FsRtlCurrentBatchOplock(%p)\n", Oplock);
1371
1372 IntOplock = *Oplock;
1373
1374 /* Only return true if batch or filter oplock */
1375 if (IntOplock != NULL &&
1377 {
1378 return TRUE;
1379 }
1380
1381 return FALSE;
1382}
1383
1384/*++
1385 * @name FsRtlInitializeOplock
1386 * @implemented
1387 *
1388 * FILLME
1389 *
1390 * @param Oplock
1391 * FILLME
1392 *
1393 * @return None
1394 *
1395 * @remarks None
1396 *
1397 *--*/
1398VOID
1399NTAPI
1401{
1402 PAGED_CODE();
1403
1404 /* Nothing to do */
1405 DPRINT("FsRtlInitializeOplock(%p)\n", Oplock);
1406}
1407
1408/*++
1409 * @name FsRtlOplockFsctrl
1410 * @unimplemented
1411 *
1412 * FILLME
1413 *
1414 * @param Oplock
1415 * FILLME
1416 *
1417 * @param Irp
1418 * FILLME
1419 *
1420 * @param OpenCount
1421 * FILLME
1422 *
1423 * @return None
1424 *
1425 * @remarks None
1426 *
1427 *--*/
1429NTAPI
1431 IN PIRP Irp,
1433{
1435 PINTERNAL_OPLOCK IntOplock;
1436
1437 PAGED_CODE();
1438
1439 DPRINT("FsRtlOplockFsctrl(%p, %p, %lu)\n", Oplock, Irp, OpenCount);
1440
1441 IntOplock = *Oplock;
1443 /* Make sure it's not called on create */
1444 if (Stack->MajorFunction != IRP_MJ_CREATE)
1445 {
1446 switch (Stack->Parameters.FileSystemControl.FsControlCode)
1447 {
1449 return FsRtlOplockBreakNotify(IntOplock, Stack, Irp);
1450
1452 return FsRtlAcknowledgeOplockBreak(IntOplock, Stack, Irp, FALSE);
1453
1455 return FsRtlOpBatchBreakClosePending(IntOplock, Stack, Irp);
1456
1458 /* We can only grant level 1 if synchronous, and only a single handle to it
1459 * (plus, not a paging IO - obvious, and not cleanup done...)
1460 */
1461 if (OpenCount == 1 && !IoIsOperationSynchronous(Irp) &&
1463 {
1465 }
1466 /* Not matching, fail */
1467 else
1468 {
1469 Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1472 }
1473
1475 /* Shared can only be granted if no byte-range lock, and async operation
1476 * (plus, not a paging IO - obvious, and not cleanup done...)
1477 */
1478 if (OpenCount == 0 && !IoIsOperationSynchronous(Irp) &&
1480 {
1481 return FsRtlRequestOplockII(Oplock, Stack, Irp);
1482 }
1483 /* Not matching, fail */
1484 else
1485 {
1486 Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1489 }
1490
1492 return FsRtlAcknowledgeOplockBreak(IntOplock, Stack, Irp, TRUE);
1493
1495 /* Batch oplock can only be granted if there's a byte-range lock and async operation
1496 * (plus, not a paging IO - obvious, and not cleanup done...)
1497 */
1498 if (OpenCount == 1 && !IoIsOperationSynchronous(Irp) &&
1500 {
1502 }
1503 /* Not matching, fail */
1504 else
1505 {
1506 Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1509 }
1510
1512 /* Filter oplock can only be granted if there's a byte-range lock and async operation
1513 * (plus, not a paging IO - obvious, and not cleanup done...)
1514 */
1515 if (OpenCount == 1 && !IoIsOperationSynchronous(Irp) &&
1517 {
1519 }
1520 /* Not matching, fail */
1521 else
1522 {
1523 Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1526 }
1527
1528 default:
1529 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
1532 }
1533 }
1534
1535 /* That's a create operation! Only grant exclusive if there's a single user handle opened
1536 * and we're only performing reading operations.
1537 */
1538 if (OpenCount == 1 &&
1539 !(Stack->Parameters.Create.SecurityContext->DesiredAccess & ~(FILE_READ_ATTRIBUTES | FILE_READ_DATA)) &&
1540 (Stack->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS) == FILE_SHARE_VALID_FLAGS)
1541 {
1543 }
1544
1546}
1547
1548/*++
1549 * @name FsRtlOplockIsFastIoPossible
1550 * @implemented
1551 *
1552 * FILLME
1553 *
1554 * @param Oplock
1555 * FILLME
1556 *
1557 * @return None
1558 *
1559 * @remarks None
1560 *
1561 *--*/
1562BOOLEAN
1563NTAPI
1565{
1566 PINTERNAL_OPLOCK IntOplock;
1567
1568 PAGED_CODE();
1569
1570 DPRINT("FsRtlOplockIsFastIoPossible(%p)\n", Oplock);
1571
1572 IntOplock = *Oplock;
1573
1574 /* If there's a shared oplock or if it was used for write operation, deny FastIO */
1575 if (IntOplock != NULL &&
1577 {
1578 return FALSE;
1579 }
1580
1581 return TRUE;
1582}
1583
1584/*++
1585 * @name FsRtlUninitializeOplock
1586 * @implemented
1587 *
1588 * FILLME
1589 *
1590 * @param Oplock
1591 * FILLME
1592 *
1593 * @return None
1594 *
1595 * @remarks None
1596 *
1597 *--*/
1598VOID
1599NTAPI
1601{
1602 PINTERNAL_OPLOCK IntOplock;
1603 PLIST_ENTRY NextEntry;
1604 PWAIT_CONTEXT WaitCtx;
1605 PIRP Irp;
1607
1608 DPRINT("FsRtlUninitializeOplock(%p)\n", Oplock);
1609
1610 IntOplock = *Oplock;
1611
1612 /* No oplock, nothing to do */
1613 if (IntOplock == NULL)
1614 {
1615 return;
1616 }
1617
1618 /* Caller won't have the oplock anymore */
1619 *Oplock = NULL;
1620
1621 _SEH2_TRY
1622 {
1624
1625 /* If we had IRPs waiting for the lock, complete them */
1626 for (NextEntry = IntOplock->WaitListHead.Flink;
1627 NextEntry != &IntOplock->WaitListHead;
1628 NextEntry = NextEntry->Flink)
1629 {
1630 WaitCtx = CONTAINING_RECORD(NextEntry, WAIT_CONTEXT, WaitListEntry);
1631 Irp = WaitCtx->Irp;
1632
1633 RemoveEntryList(&WaitCtx->WaitListEntry);
1634 /* Remove the cancel routine */
1635 IoAcquireCancelSpinLock(&Irp->CancelIrql);
1637 IoReleaseCancelSpinLock(Irp->CancelIrql);
1638
1639 /* And complete */
1640 Irp->IoStatus.Information = 0;
1641 WaitCtx->CompletionRoutine(WaitCtx->CompletionContext, WaitCtx->Irp);
1642
1643 ExFreePoolWithTag(WaitCtx, TAG_OPLOCK);
1644 }
1645
1646 /* If we had shared IRPs (LEVEL_2), complete them */
1647 for (NextEntry = IntOplock->SharedListHead.Flink;
1648 NextEntry != &IntOplock->SharedListHead;
1649 NextEntry = NextEntry->Flink)
1650 {
1651 Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
1652
1653 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
1654
1655 /* Remvoe the cancel routine */
1656 IoAcquireCancelSpinLock(&Irp->CancelIrql);
1658 IoReleaseCancelSpinLock(Irp->CancelIrql);
1659
1660 /* Dereference the file object */
1662 ObDereferenceObject(Stack->FileObject);
1663
1664 /* And complete */
1665 Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
1666 Irp->IoStatus.Status = STATUS_SUCCESS;
1668 }
1669
1670 /* If we have an exclusive IRP, complete it */
1671 Irp = IntOplock->ExclusiveIrp;
1672 if (Irp != NULL)
1673 {
1674 /* Remvoe the cancel routine */
1675 IoAcquireCancelSpinLock(&Irp->CancelIrql);
1677 IoReleaseCancelSpinLock(Irp->CancelIrql);
1678
1679 /* And complete */
1680 Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
1681 Irp->IoStatus.Status = STATUS_SUCCESS;
1683 IntOplock->ExclusiveIrp = NULL;
1684
1685 /* If still referenced, dereference */
1686 if (IntOplock->FileObject != NULL)
1687 {
1688 ObDereferenceObject(IntOplock->FileObject);
1689 }
1690 }
1691 }
1693 {
1695 }
1696 _SEH2_END;
1697
1699 ExFreePoolWithTag(IntOplock, TAG_OPLOCK);
1700}
1701
static PIO_STACK_LOCATION IoGetCurrentIrpStackLocation(PIRP Irp)
#define PAGED_CODE()
unsigned char BOOLEAN
LONG NTSTATUS
Definition: precomp.h:26
#define FILE_COMPLETE_IF_OPLOCKED
Definition: constants.h:493
_In_ PIRP Irp
Definition: csq.h:116
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define FILE_SHARE_READ
Definition: compat.h:136
DWORD OpenCount
Definition: legacy.c:25
#define ULONG_PTR
Definition: config.h:101
#define RemoveEntryList(Entry)
Definition: env_spec_w32.h:986
#define InsertTailList(ListHead, Entry)
#define InsertHeadList(ListHead, Entry)
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define IsListEmpty(ListHead)
Definition: env_spec_w32.h:954
#define KeWaitForSingleObject(pEvt, foo, a, b, c)
Definition: env_spec_w32.h:478
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
#define PKEVENT
Definition: env_spec_w32.h:70
#define KeSetEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:476
#define NonPagedPool
Definition: env_spec_w32.h:307
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
#define PagedPool
Definition: env_spec_w32.h:308
#define ClearFlag(_F, _SF)
Definition: ext2fs.h:191
#define SetFlag(_F, _SF)
Definition: ext2fs.h:187
#define BooleanFlagOn(F, SF)
Definition: ext2fs.h:183
@ Removed
Definition: fbtusb.h:86
#define _SEH2_FINALLY
Definition: filesup.c:21
#define _SEH2_END
Definition: filesup.c:22
#define _SEH2_TRY
Definition: filesup.c:19
@ FileEndOfFileInformation
Definition: from_kernel.h:81
@ FileRenameInformation
Definition: from_kernel.h:71
@ FileLinkInformation
Definition: from_kernel.h:72
@ FileAllocationInformation
Definition: from_kernel.h:80
@ FileShortNameInformation
Definition: from_kernel.h:101
enum _FILE_INFORMATION_CLASS FILE_INFORMATION_CLASS
Definition: directory.c:44
#define FILE_OVERWRITE_IF
Definition: from_kernel.h:58
#define FILE_RESERVE_OPFILTER
Definition: from_kernel.h:45
#define FILE_OVERWRITE
Definition: from_kernel.h:57
_In_ PIRP _In_opt_ PVOID _In_opt_ POPLOCK_WAIT_COMPLETE_ROUTINE _In_opt_ POPLOCK_FS_PREPOST_IRP PostIrpRoutine
Definition: fsrtlfuncs.h:676
VOID(NTAPI * POPLOCK_FS_PREPOST_IRP)(_In_ PVOID Context, _In_ PIRP Irp)
Definition: fsrtltypes.h:258
VOID(NTAPI * POPLOCK_WAIT_COMPLETE_ROUTINE)(_In_ PVOID Context, _In_ PIRP Irp)
Definition: fsrtltypes.h:253
Status
Definition: gdiplustypes.h:25
VOID FASTCALL ExAcquireFastMutex(IN PFAST_MUTEX FastMutex)
Definition: fmutex.c:23
VOID FASTCALL ExReleaseFastMutex(IN PFAST_MUTEX FastMutex)
Definition: fmutex.c:31
#define KeGetCurrentThread
Definition: hal.h:55
@ Unknown
Definition: i8042prt.h:114
IoMarkIrpPending(Irp)
IoSetCancelRoutine(Irp, CancelRoutine)
#define ASSERT(a)
Definition: mode.c:44
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
#define KernelMode
Definition: asm.h:34
_In_ PMEMORY_AREA _In_ PVOID _In_ BOOLEAN Locked
Definition: newmm.h:209
#define SYNCHRONIZE
Definition: nt_native.h:61
#define FSCTL_OPLOCK_BREAK_NOTIFY
Definition: nt_native.h:831
#define FILE_WRITE_DATA
Definition: nt_native.h:631
#define FILE_READ_DATA
Definition: nt_native.h:628
#define FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
Definition: nt_native.h:829
#define FSCTL_REQUEST_OPLOCK_LEVEL_1
Definition: nt_native.h:826
ULONG ACCESS_MASK
Definition: nt_native.h:40
#define FILE_READ_ATTRIBUTES
Definition: nt_native.h:647
#define FSCTL_REQUEST_FILTER_OPLOCK
Definition: nt_native.h:849
#define FILE_READ_EA
Definition: nt_native.h:638
#define FILE_SHARE_VALID_FLAGS
Definition: nt_native.h:683
#define FILE_EXECUTE
Definition: nt_native.h:642
#define FILE_WRITE_ATTRIBUTES
Definition: nt_native.h:649
#define FSCTL_REQUEST_BATCH_OPLOCK
Definition: nt_native.h:828
#define READ_CONTROL
Definition: nt_native.h:58
#define FSCTL_OPBATCH_ACK_CLOSE_PENDING
Definition: nt_native.h:830
#define FSCTL_OPLOCK_BREAK_ACK_NO_2
Definition: nt_native.h:846
#define FSCTL_REQUEST_OPLOCK_LEVEL_2
Definition: nt_native.h:827
@ NotificationEvent
VOID FASTCALL ExReleaseFastMutexUnsafe(IN OUT PFAST_MUTEX FastMutex)
Definition: fmutex.c:86
VOID FASTCALL ExAcquireFastMutexUnsafe(IN OUT PFAST_MUTEX FastMutex)
Definition: fmutex.c:75
VOID FASTCALL IofCompleteRequest(IN PIRP Irp, IN CCHAR PriorityBoost)
Definition: irp.c:1308
#define IoCompleteRequest
Definition: irp.c:1240
BOOLEAN NTAPI IoIsOperationSynchronous(IN PIRP Irp)
Definition: irp.c:1882
VOID NTAPI IoReleaseCancelSpinLock(IN KIRQL Irql)
Definition: util.c:150
VOID NTAPI IoAcquireCancelSpinLock(OUT PKIRQL Irql)
Definition: util.c:56
#define STATUS_OPLOCK_BREAK_IN_PROGRESS
Definition: ntstatus.h:87
#define STATUS_INVALID_OPLOCK_PROTOCOL
Definition: ntstatus.h:463
#define STATUS_OPLOCK_NOT_GRANTED
Definition: ntstatus.h:462
BOOLEAN NTAPI FsRtlCurrentBatchOplock(IN POPLOCK Oplock)
Definition: oplock.c:1364
#define BATCH_OPLOCK
Definition: oplock.c:17
VOID FsRtlOplockCleanup(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack)
Definition: oplock.c:816
#define PENDING_LOCK
Definition: oplock.c:22
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)
Definition: oplock.c:901
#define BROKEN_TO_NONE
Definition: oplock.c:25
#define BROKEN_ANY
Definition: oplock.c:28
NTSTATUS NTAPI FsRtlOplockFsctrl(IN POPLOCK Oplock, IN PIRP Irp, IN ULONG OpenCount)
Definition: oplock.c:1430
NTSTATUS FsRtlOpBatchBreakClosePending(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp)
Definition: oplock.c:470
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)
Definition: oplock.c:148
#define NO_OPLOCK
Definition: oplock.c:15
VOID FsRtlRemoveAndCompleteIrp(IN PIRP Irp)
Definition: oplock.c:283
#define FILTER_OPLOCK
Definition: oplock.c:18
struct _INTERNAL_OPLOCK * PINTERNAL_OPLOCK
BOOLEAN NTAPI FsRtlOplockIsFastIoPossible(IN POPLOCK Oplock)
Definition: oplock.c:1564
VOID NTAPI FsRtlUninitializeOplock(IN POPLOCK Oplock)
Definition: oplock.c:1600
NTSTATUS FsRtlOplockBreakNotify(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp)
Definition: oplock.c:239
#define BROKEN_TO_NONE_FROM_LEVEL_2
Definition: oplock.c:26
#define BreakToNoneIfRequired
VOID NTAPI FsRtlCancelWaitIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
Definition: oplock.c:114
#define BROKEN_TO_LEVEL_2
Definition: oplock.c:24
NTSTATUS FsRtlAcknowledgeOplockBreak(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp, IN BOOLEAN SwitchToLevel2)
Definition: oplock.c:356
VOID NTAPI FsRtlCompletionRoutinePriv(IN PVOID Context, IN PIRP Irp)
Definition: oplock.c:69
struct _INTERNAL_OPLOCK INTERNAL_OPLOCK
VOID FsRtlRemoveAndCompleteWaitIrp(IN PWAIT_CONTEXT WaitCtx)
Definition: oplock.c:84
#define BreakToIIIfRequired
VOID NTAPI FsRtlCancelExclusiveIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
Definition: oplock.c:584
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)
Definition: oplock.c:1170
#define LEVEL_1_OPLOCK
Definition: oplock.c:16
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)
Definition: oplock.c:1035
#define BROKEN_TO_CLOSE_PENDING
Definition: oplock.c:27
NTSTATUS FsRtlRequestExclusiveOplock(IN POPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp, IN ULONG Flags)
Definition: oplock.c:630
VOID NTAPI FsRtlNotifyCompletion(IN PVOID Context, IN PIRP Irp)
Definition: oplock.c:56
VOID NTAPI FsRtlCancelOplockIIIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
Definition: oplock.c:311
PINTERNAL_OPLOCK FsRtlAllocateOplock(VOID)
Definition: oplock.c:542
#define LEVEL_2_OPLOCK
Definition: oplock.c:19
#define EXCLUSIVE_LOCK
Definition: oplock.c:21
struct _WAIT_CONTEXT * PWAIT_CONTEXT
NTSTATUS FsRtlRequestOplockII(IN POPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp)
Definition: oplock.c:734
VOID NTAPI FsRtlInitializeOplock(IN OUT POPLOCK Oplock)
Definition: oplock.c:1400
struct _WAIT_CONTEXT WAIT_CONTEXT
#define _SEH2_AbnormalTermination()
Definition: pseh2_64.h:160
#define IRP_MJ_READ
Definition: rdpdr.c:46
#define IRP_MJ_LOCK_CONTROL
Definition: rdpdr.c:53
#define IRP_MJ_WRITE
Definition: rdpdr.c:47
#define IRP_MJ_SET_INFORMATION
Definition: rdpdr.c:49
#define IRP_MJ_CREATE
Definition: rdpdr.c:44
#define FSCTL_SET_ZERO_DATA
Definition: winioctl.h:141
#define STATUS_SUCCESS
Definition: shellext.h:65
#define DPRINT
Definition: sndvol32.h:71
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68
ULONG Flags
Definition: oplock.c:40
PIRP ExclusiveIrp
Definition: oplock.c:33
PFILE_OBJECT FileObject
Definition: oplock.c:35
LIST_ENTRY SharedListHead
Definition: oplock.c:37
LIST_ENTRY WaitListHead
Definition: oplock.c:39
PFAST_MUTEX IntLock
Definition: oplock.c:41
PFILE_OBJECT FileObject
Definition: iotypes.h:3169
IO_STATUS_BLOCK IoStatus
Definition: typedefs.h:120
struct _LIST_ENTRY * Blink
Definition: typedefs.h:122
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine
Definition: oplock.c:48
ULONG_PTR SavedInformation
Definition: oplock.c:51
ULONG Reserved
Definition: oplock.c:50
PVOID CompletionContext
Definition: oplock.c:49
PIRP Irp
Definition: oplock.c:47
LIST_ENTRY WaitListEntry
Definition: oplock.c:46
#define TAG_OPLOCK
Definition: tag.h:51
#define NTAPI
Definition: typedefs.h:36
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
uint32_t ULONG_PTR
Definition: typedefs.h:65
#define IN
Definition: typedefs.h:39
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
uint32_t ULONG
Definition: typedefs.h:59
#define OUT
Definition: typedefs.h:40
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
#define STATUS_CANCELLED
Definition: udferr_usr.h:170
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
_Must_inspect_result_ _In_ WDFDEVICE _In_ ULONG _In_ ACCESS_MASK DesiredAccess
Definition: wdfdevice.h:2658
_Must_inspect_result_ _In_opt_ WDFKEY _In_ PCUNICODE_STRING _In_ ACCESS_MASK _In_ ULONG _Out_opt_ PULONG CreateDisposition
Definition: wdfregistry.h:120
_In_ WDFREQUEST _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE _In_opt_ __drv_aliasesMem WDFCONTEXT CompletionContext
Definition: wdfrequest.h:898
_In_ WDFREQUEST _In_ PIO_STACK_LOCATION Stack
Definition: wdfrequest.h:639
_In_ WDFREQUEST _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine
Definition: wdfrequest.h:895
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
FORCEINLINE VOID ExInitializeFastMutex(_Out_ PFAST_MUTEX FastMutex)
Definition: exfuncs.h:274
* PFAST_MUTEX
Definition: extypes.h:17
FAST_MUTEX
Definition: extypes.h:17
#define IO_NO_INCREMENT
Definition: iotypes.h:598
#define IRP_MJ_FILE_SYSTEM_CONTROL
#define IRP_PAGING_IO
#define POOL_COLD_ALLOCATION
#define FO_CLEANUP_COMPLETE
Definition: iotypes.h:1790
* PFILE_OBJECT
Definition: iotypes.h:1998
#define IRP_MJ_FLUSH_BUFFERS
#define IO_DISK_INCREMENT
Definition: iotypes.h:600
#define IRP_SYNCHRONOUS_PAGING_IO
#define FILE_OPLOCK_BROKEN_TO_LEVEL_2
#define POOL_RAISE_IF_ALLOCATION_FAILURE
#define FILE_OPLOCK_BROKEN_TO_NONE
#define IRP_MJ_CLEANUP
@ Executive
Definition: ketypes.h:415
#define ObDereferenceObject
Definition: obfuncs.h:203
#define ObReferenceObject
Definition: obfuncs.h:204