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