ReactOS  0.4.15-dev-3745-g356babc
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 
30 typedef struct _INTERNAL_OPLOCK
31 {
32  /* Level I IRP */
34  /* Level I FILE_OBJECT */
36  /* Level II IRPs */
38  /* IRPs waiting on level I */
43 
44 typedef struct _WAIT_CONTEXT
45 {
53 
54 VOID
55 NTAPI
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 
67 VOID
68 NTAPI
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 
83 VOID
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 */
109  ExFreePoolWithTag(WaitCtx, TAG_OPLOCK);
110 }
111 
112 VOID
113 NTAPI
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 */
132  ExAcquireFastMutex(Oplock->IntLock);
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  }
144  ExReleaseFastMutex(Oplock->IntLock);
145 }
146 
147 VOID
149  IN PIRP Irp,
153  IN PKEVENT WaitEvent)
154 {
155  BOOLEAN Locked;
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 
238 NTSTATUS
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 
282 VOID
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 
309 VOID
310 NTAPI
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;
330  ExAcquireFastMutex(Oplock->IntLock);
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  {
341  FsRtlRemoveAndCompleteIrp(ListIrp);
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 */
352  ExReleaseFastMutex(Oplock->IntLock);
353 }
354 
355 NTSTATUS
358  IN PIRP Irp,
359  IN BOOLEAN SwitchToLevel2)
360 {
361  PLIST_ENTRY NextEntry;
362  PWAIT_CONTEXT WaitCtx;
363  BOOLEAN Deref;
364  BOOLEAN Locked;
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 
469 NTSTATUS
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 
550  _SEH2_TRY
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  {
572  ExFreePoolWithTag(Oplock, TAG_OPLOCK);
573  Oplock = NULL;
574  }
575  }
576  }
577  _SEH2_END;
578 
579  return Oplock;
580 }
581 
582 VOID
583 NTAPI
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 */
611  ObDereferenceObject(IntOplock->FileObject);
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! */
626  ExReleaseFastMutexUnsafe(IntOplock->IntLock);
627 }
628 
629 NTSTATUS
632  IN PIRP Irp,
633  IN ULONG Flags)
634 {
635  PINTERNAL_OPLOCK IntOplock;
636  PIRP ListIrp;
637  BOOLEAN Locked;
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! */
647  _SEH2_TRY
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 */
657  ExAcquireFastMutexUnsafe(IntOplock->IntLock);
658  Locked = TRUE;
659 
660  /* If we request exclusiveness, a filter or a pending oplock, grant it */
662  {
663  /* Either no oplock, or pending */
664  ASSERT(BooleanFlagOn(IntOplock->Flags, (NO_OPLOCK | PENDING_LOCK)));
665  IntOplock->ExclusiveIrp = Irp;
666  IntOplock->FileObject = Stack->FileObject;
667  IntOplock->Flags = (EXCLUSIVE_LOCK | PENDING_LOCK | FILTER_OPLOCK);
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);
679  FsRtlRemoveAndCompleteIrp(ListIrp);
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  {
697  ExReleaseFastMutexUnsafe(IntOplock->IntLock);
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  {
725  ExReleaseFastMutexUnsafe(IntOplock->IntLock);
726  }
727  }
728  _SEH2_END;
729 
730  return Status;
731 }
732 
733 NTSTATUS
736  IN PIRP Irp)
737 {
738  BOOLEAN Locked;
740  PINTERNAL_OPLOCK IntOplock;
741 
742  DPRINT("FsRtlRequestOplockII(%p, %p, %p)\n", Oplock, Stack, Irp);
743 
744  IntOplock = *Oplock;
745  Locked = FALSE;
747 
748  _SEH2_TRY
749  {
750  /* No oplock yet? Allocate it */
751  if (IntOplock == NULL)
752  {
753  *Oplock = FsRtlAllocateOplock();
754  IntOplock = *Oplock;
755  }
756 
757  /* Acquire the oplock */
758  ExAcquireFastMutexUnsafe(IntOplock->IntLock);
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  {
784  ExReleaseFastMutexUnsafe(IntOplock->IntLock);
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  {
807  ExReleaseFastMutexUnsafe(IntOplock->IntLock);
808  }
809  }
810  _SEH2_END;
811 
812  return Status;
813 }
814 
815 VOID
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  {
843  FsRtlRemoveAndCompleteIrp(ListIrp);
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 
899 NTSTATUS
900 NTAPI
903  IN PIRP Irp,
904  IN PVOID Context,
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);
983  FsRtlRemoveAndCompleteIrp(ListIrp);
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  {
1019  FsRtlWaitOnIrp(Oplock, Irp, Context, CompletionRoutine, PostIrpRoutine, &WaitEvent);
1020 
1021  ExReleaseFastMutexUnsafe(Oplock->IntLock);
1022 
1023  return STATUS_SUCCESS;
1024  }
1025  /* Done */
1026  else
1027  {
1028  ExReleaseFastMutexUnsafe(Oplock->IntLock);
1030  }
1031 }
1032 
1033 NTSTATUS
1034 NTAPI
1037  IN PIRP Irp,
1038  IN PVOID Context,
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  {
1127  FsRtlWaitOnIrp(Oplock, Irp, Context, CompletionRoutine, PostIrpRoutine, &WaitEvent);
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  *--*/
1168 NTSTATUS
1169 NTAPI
1171  IN PIRP Irp,
1172  IN PVOID Context,
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  }
1321  else if (FileInfo == FileAllocationInformation)
1322  {
1324 
1325  return STATUS_SUCCESS;
1326  }
1327  else if (FileInfo == FileEndOfFileInformation)
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  *--*/
1362 BOOLEAN
1363 NTAPI
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 &&
1376  BooleanFlagOn(IntOplock->Flags, (FILTER_OPLOCK | BATCH_OPLOCK)))
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  *--*/
1398 VOID
1399 NTAPI
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  *--*/
1428 NTSTATUS
1429 NTAPI
1431  IN PIRP Irp,
1432  IN ULONG OpenCount)
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;
1531  return 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  *--*/
1562 BOOLEAN
1563 NTAPI
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 &&
1576  BooleanFlagOn(IntOplock->Flags, (BROKEN_ANY | LEVEL_2_OPLOCK)))
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  *--*/
1598 VOID
1599 NTAPI
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  {
1623  ExAcquireFastMutexUnsafe(IntOplock->IntLock);
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  {
1694  ExReleaseFastMutexUnsafe(IntOplock->IntLock);
1695  }
1696  _SEH2_END;
1697 
1698  ExFreePoolWithTag(IntOplock->IntLock, TAG_OPLOCK);
1699  ExFreePoolWithTag(IntOplock, TAG_OPLOCK);
1700 }
1701 
PFILE_OBJECT FileObject
Definition: oplock.c:35
PVOID CompletionContext
Definition: oplock.c:49
PFAST_MUTEX IntLock
Definition: oplock.c:41
#define IN
Definition: typedefs.h:39
_Must_inspect_result_ _In_ WDFDEVICE _In_ ULONG _In_ ACCESS_MASK DesiredAccess
Definition: wdfdevice.h:2654
struct _KEVENT * PKEVENT
#define PENDING_LOCK
Definition: oplock.c:22
#define IRP_MJ_CREATE
Definition: rdpdr.c:44
#define BATCH_OPLOCK
Definition: oplock.c:17
#define IRP_MJ_FLUSH_BUFFERS
_In_ WDFREQUEST _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE _In_opt_ __drv_aliasesMem WDFCONTEXT CompletionContext
Definition: wdfrequest.h:893
NTSTATUS FsRtlOpBatchBreakClosePending(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp)
Definition: oplock.c:470
LIST_ENTRY SharedListHead
Definition: oplock.c:37
#define TRUE
Definition: types.h:120
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
#define BooleanFlagOn(F, SF)
Definition: ext2fs.h:183
struct _LIST_ENTRY * Blink
Definition: typedefs.h:122
FORCEINLINE VOID InsertHeadList(_Inout_ PLIST_ENTRY ListHead, _Inout_ __drv_aliasesMem PLIST_ENTRY Entry)
Definition: rtlfuncs.h:201
struct _INTERNAL_OPLOCK INTERNAL_OPLOCK
#define FILTER_OPLOCK
Definition: oplock.c:18
LONG NTSTATUS
Definition: precomp.h:26
NTSTATUS FsRtlAcknowledgeOplockBreak(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp, IN BOOLEAN SwitchToLevel2)
Definition: oplock.c:356
VOID NTAPI IoAcquireCancelSpinLock(OUT PKIRQL Irql)
Definition: util.c:56
#define FILE_OPLOCK_BROKEN_TO_LEVEL_2
#define FSCTL_OPLOCK_BREAK_ACK_NO_2
Definition: nt_native.h:846
#define POOL_COLD_ALLOCATION
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 FILE_OVERWRITE
Definition: from_kernel.h:57
struct _WAIT_CONTEXT * PWAIT_CONTEXT
LIST_ENTRY WaitListHead
Definition: oplock.c:39
#define STATUS_INVALID_OPLOCK_PROTOCOL
Definition: ntstatus.h:463
#define FSCTL_OPBATCH_ACK_CLOSE_PENDING
Definition: nt_native.h:830
#define TAG_OPLOCK
Definition: tag.h:49
IO_STATUS_BLOCK IoStatus
VOID NTAPI FsRtlUninitializeOplock(IN POPLOCK Oplock)
Definition: oplock.c:1600
BOOLEAN NTAPI IoIsOperationSynchronous(IN PIRP Irp)
Definition: irp.c:1882
#define InsertTailList(ListHead, Entry)
#define BROKEN_TO_NONE
Definition: oplock.c:25
IoSetCancelRoutine(Irp, CancelRoutine)
#define BreakToIIIfRequired
LONG NTAPI KeSetEvent(IN PKEVENT Event, IN KPRIORITY Increment, IN BOOLEAN Wait)
Definition: eventobj.c:159
NTSTATUS NTAPI KeWaitForSingleObject(IN PVOID Object, IN KWAIT_REASON WaitReason, IN KPROCESSOR_MODE WaitMode, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL)
Definition: wait.c:416
#define FSCTL_REQUEST_BATCH_OPLOCK
Definition: nt_native.h:828
_Must_inspect_result_ FORCEINLINE BOOLEAN IsListEmpty(_In_ const LIST_ENTRY *ListHead)
Definition: rtlfuncs.h:57
#define FILE_RESERVE_OPFILTER
Definition: from_kernel.h:45
VOID FASTCALL ExReleaseFastMutex(IN PFAST_MUTEX FastMutex)
Definition: fmutex.c:31
#define FSCTL_SET_ZERO_DATA
Definition: winioctl.h:141
#define FILE_WRITE_ATTRIBUTES
Definition: nt_native.h:649
VOID FASTCALL ExReleaseFastMutexUnsafe(IN OUT PFAST_MUTEX FastMutex)
Definition: fmutex.c:86
#define FILE_SHARE_READ
Definition: compat.h:136
VOID NTAPI FsRtlCancelExclusiveIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
Definition: oplock.c:584
_SEH2_TRY
Definition: create.c:4226
ULONG_PTR SavedInformation
Definition: oplock.c:51
uint32_t ULONG_PTR
Definition: typedefs.h:65
FORCEINLINE BOOLEAN RemoveEntryList(_In_ PLIST_ENTRY Entry)
Definition: rtlfuncs.h:105
_In_ WDFREQUEST _In_ PIO_STACK_LOCATION Stack
Definition: wdfrequest.h:636
NTSTATUS NTAPI FsRtlOplockFsctrl(IN POPLOCK Oplock, IN PIRP Irp, IN ULONG OpenCount)
Definition: oplock.c:1430
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
#define IO_DISK_INCREMENT
Definition: iotypes.h:600
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
#define LEVEL_2_OPLOCK
Definition: oplock.c:19
PIRP ExclusiveIrp
Definition: oplock.c:33
#define FALSE
Definition: types.h:117
#define FSCTL_OPLOCK_BREAK_NOTIFY
Definition: nt_native.h:831
_In_ PIRP Irp
Definition: csq.h:116
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
_In_ PMEMORY_AREA _In_ PVOID _In_ BOOLEAN Locked
Definition: newmm.h:207
#define FILE_READ_DATA
Definition: nt_native.h:628
VOID NTAPI FsRtlCancelWaitIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
Definition: oplock.c:114
#define BROKEN_ANY
Definition: oplock.c:28
NTSTATUS FsRtlOplockBreakNotify(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp)
Definition: oplock.c:239
VOID(NTAPI * POPLOCK_WAIT_COMPLETE_ROUTINE)(_In_ PVOID Context, _In_ PIRP Irp)
Definition: fsrtltypes.h:253
unsigned char BOOLEAN
#define FILE_OVERWRITE_IF
Definition: from_kernel.h:58
#define _SEH2_AbnormalTermination()
Definition: pseh2_64.h:166
#define STATUS_OPLOCK_NOT_GRANTED
Definition: ntstatus.h:462
VOID FsRtlRemoveAndCompleteWaitIrp(IN PWAIT_CONTEXT WaitCtx)
Definition: oplock.c:84
#define IoCompleteRequest
Definition: irp.c:1240
NTSTATUS FsRtlRequestOplockII(IN POPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp)
Definition: oplock.c:734
#define FILE_WRITE_DATA
Definition: nt_native.h:631
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
#define FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
Definition: nt_native.h:829
Status
Definition: gdiplustypes.h:24
FAST_MUTEX
Definition: extypes.h:17
VOID FsRtlOplockCleanup(IN PINTERNAL_OPLOCK Oplock, IN PIO_STACK_LOCATION Stack)
Definition: oplock.c:816
VOID FASTCALL IofCompleteRequest(IN PIRP Irp, IN CCHAR PriorityBoost)
Definition: irp.c:1308
enum _FILE_INFORMATION_CLASS FILE_INFORMATION_CLASS
Definition: directory.c:44
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
struct _INTERNAL_OPLOCK * PINTERNAL_OPLOCK
_Must_inspect_result_ _In_opt_ WDFKEY _In_ PCUNICODE_STRING _In_ ACCESS_MASK _In_ ULONG _Out_opt_ PULONG CreateDisposition
Definition: wdfregistry.h:112
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
#define ASSERT(a)
Definition: mode.c:44
#define FSCTL_REQUEST_OPLOCK_LEVEL_2
Definition: nt_native.h:827
VOID FASTCALL ExAcquireFastMutexUnsafe(IN OUT PFAST_MUTEX FastMutex)
Definition: fmutex.c:75
LIST_ENTRY WaitListEntry
Definition: oplock.c:46
struct _WAIT_CONTEXT WAIT_CONTEXT
VOID NTAPI FsRtlInitializeOplock(IN OUT POPLOCK Oplock)
Definition: oplock.c:1400
#define IRP_MJ_FILE_SYSTEM_CONTROL
FORCEINLINE VOID ExInitializeFastMutex(_Out_ PFAST_MUTEX FastMutex)
Definition: exfuncs.h:274
#define BROKEN_TO_CLOSE_PENDING
Definition: oplock.c:27
#define ObDereferenceObject
Definition: obfuncs.h:203
#define STATUS_CANCELLED
Definition: udferr_usr.h:170
NTSTATUS FsRtlRequestExclusiveOplock(IN POPLOCK Oplock, IN PIO_STACK_LOCATION Stack, IN PIRP Irp, IN ULONG Flags)
Definition: oplock.c:630
BOOLEAN NTAPI FsRtlCurrentBatchOplock(IN POPLOCK Oplock)
Definition: oplock.c:1364
* PFILE_OBJECT
Definition: iotypes.h:1998
#define READ_CONTROL
Definition: nt_native.h:58
#define FILE_READ_ATTRIBUTES
Definition: nt_native.h:647
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
VOID FsRtlRemoveAndCompleteIrp(IN PIRP Irp)
Definition: oplock.c:283
#define BROKEN_TO_LEVEL_2
Definition: oplock.c:24
#define FILE_EXECUTE
Definition: nt_native.h:642
#define BreakToNoneIfRequired
Definition: typedefs.h:119
POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine
Definition: oplock.c:48
#define FSCTL_REQUEST_OPLOCK_LEVEL_1
Definition: nt_native.h:826
#define SYNCHRONIZE
Definition: nt_native.h:61
VOID NTAPI FsRtlCompletionRoutinePriv(IN PVOID Context, IN PIRP Irp)
Definition: oplock.c:69
ClearFlag(Dirent->Flags, DIRENT_FLAG_NOT_PERSISTENT)
#define FILE_SHARE_VALID_FLAGS
Definition: nt_native.h:683
VOID NTAPI IoReleaseCancelSpinLock(IN KIRQL Irql)
Definition: util.c:150
#define NO_OPLOCK
Definition: oplock.c:15
#define EXCLUSIVE_LOCK
Definition: oplock.c:21
__drv_aliasesMem FORCEINLINE PIO_STACK_LOCATION IoGetCurrentIrpStackLocation(_In_ PIRP Irp)
Definition: iofuncs.h:2793
#define LEVEL_1_OPLOCK
Definition: oplock.c:16
#define SetFlag(_F, _SF)
Definition: ext2fs.h:187
PFILE_OBJECT FileObject
Definition: iotypes.h:3169
_SEH2_END
Definition: create.c:4400
* PFAST_MUTEX
Definition: extypes.h:17
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
VOID FASTCALL ExAcquireFastMutex(IN PFAST_MUTEX FastMutex)
Definition: fmutex.c:23
#define FO_CLEANUP_COMPLETE
Definition: iotypes.h:1790
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
_In_ WDFREQUEST _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine
Definition: wdfrequest.h:893
#define FSCTL_REQUEST_FILTER_OPLOCK
Definition: nt_native.h:849
_SEH2_FINALLY
Definition: create.c:4371
#define FILE_READ_EA
Definition: nt_native.h:638
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 NULL
Definition: types.h:112
PIRP Irp
Definition: oplock.c:47
DWORD OpenCount
Definition: legacy.c:25
#define IRP_PAGING_IO
#define STATUS_OPLOCK_BREAK_IN_PROGRESS
Definition: ntstatus.h:87
#define IRP_MJ_LOCK_CONTROL
Definition: rdpdr.c:53
#define IRP_MJ_READ
Definition: rdpdr.c:46
#define IRP_MJ_CLEANUP
#define OUT
Definition: typedefs.h:40
#define ObReferenceObject
Definition: obfuncs.h:204
struct FileInfo FileInfo
#define IRP_MJ_SET_INFORMATION
Definition: rdpdr.c:49
unsigned int ULONG
Definition: retypes.h:1
#define IO_NO_INCREMENT
Definition: iotypes.h:598
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
#define BROKEN_TO_NONE_FROM_LEVEL_2
Definition: oplock.c:26
#define ULONG_PTR
Definition: config.h:101
#define IRP_MJ_WRITE
Definition: rdpdr.c:47
Definition: fbtusb.h:86
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 POOL_RAISE_IF_ALLOCATION_FAILURE
#define STATUS_SUCCESS
Definition: shellext.h:65
#define FILE_OPLOCK_BROKEN_TO_NONE
#define DPRINT
Definition: sndvol32.h:71
#define KeGetCurrentThread
Definition: hal.h:55
VOID NTAPI FsRtlNotifyCompletion(IN PVOID Context, IN PIRP Irp)
Definition: oplock.c:56
IoMarkIrpPending(Irp)
VOID(NTAPI * POPLOCK_FS_PREPOST_IRP)(_In_ PVOID Context, _In_ PIRP Irp)
Definition: fsrtltypes.h:258
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1099
VOID NTAPI FsRtlCancelOplockIIIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
Definition: oplock.c:311
#define FILE_COMPLETE_IF_OPLOCKED
Definition: constants.h:493
ULONG ACCESS_MASK
Definition: nt_native.h:40
ULONG Reserved
Definition: oplock.c:50
_In_ PIRP _In_opt_ PVOID _In_opt_ POPLOCK_WAIT_COMPLETE_ROUTINE _In_opt_ POPLOCK_FS_PREPOST_IRP PostIrpRoutine
Definition: fsrtlfuncs.h:673
BOOLEAN NTAPI FsRtlOplockIsFastIoPossible(IN POPLOCK Oplock)
Definition: oplock.c:1564
#define IRP_SYNCHRONOUS_PAGING_IO
PINTERNAL_OPLOCK FsRtlAllocateOplock(VOID)
Definition: oplock.c:542
#define PAGED_CODE()
ULONG Flags
Definition: oplock.c:40
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68