xref: /reactos/ntoskrnl/fsrtl/oplock.c (revision 3bc2d590)
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 */
33     PIRP ExclusiveIrp;
34     /* Level I FILE_OBJECT */
35     PFILE_OBJECT FileObject;
36     /* Level II IRPs */
37     LIST_ENTRY SharedListHead;
38     /* IRPs waiting on level I */
39     LIST_ENTRY WaitListHead;
40     ULONG Flags;
41     PFAST_MUTEX IntLock;
42 } INTERNAL_OPLOCK, *PINTERNAL_OPLOCK;
43 
44 typedef struct _WAIT_CONTEXT
45 {
46     LIST_ENTRY WaitListEntry;
47     PIRP Irp;
48     POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine;
49     PVOID CompletionContext;
50     ULONG Reserved;
51     ULONG_PTR SavedInformation;
52 } WAIT_CONTEXT, *PWAIT_CONTEXT;
53 
54 VOID
55 NTAPI
FsRtlNotifyCompletion(IN PVOID Context,IN PIRP Irp)56 FsRtlNotifyCompletion(IN PVOID Context,
57                       IN PIRP Irp)
58 {
59     PAGED_CODE();
60 
61     DPRINT("FsRtlNotifyCompletion(%p, %p)\n", Context, Irp);
62 
63     /* Just complete the IRP */
64     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
65 }
66 
67 VOID
68 NTAPI
FsRtlCompletionRoutinePriv(IN PVOID Context,IN PIRP Irp)69 FsRtlCompletionRoutinePriv(IN PVOID Context,
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
FsRtlRemoveAndCompleteWaitIrp(IN PWAIT_CONTEXT WaitCtx)84 FsRtlRemoveAndCompleteWaitIrp(IN PWAIT_CONTEXT WaitCtx)
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);
97     IoSetCancelRoutine(Irp, NULL);
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
FsRtlCancelWaitIrp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)114 FsRtlCancelWaitIrp(IN PDEVICE_OBJECT DeviceObject,
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!) */
127     IoSetCancelRoutine(Irp, NULL);
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         {
141             FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
142         }
143     }
144     ExReleaseFastMutex(Oplock->IntLock);
145 }
146 
147 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)148 FsRtlWaitOnIrp(IN PINTERNAL_OPLOCK Oplock,
149                IN PIRP Irp,
150                IN PVOID CompletionContext,
151                IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine,
152                IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine,
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 */
166     WaitCtx = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, sizeof(WAIT_CONTEXT), TAG_OPLOCK);
167     WaitCtx->Irp = Irp;
168     WaitCtx->SavedInformation = Irp->IoStatus.Information;
169     /* If caller provided everything required, us it */
170     if (CompletionRoutine != NULL)
171     {
172         WaitCtx->CompletionRoutine = CompletionRoutine;
173         WaitCtx->CompletionContext = CompletionContext;
174     }
175     /* Otherwise, put ourselves */
176     else
177     {
178         WaitCtx->CompletionRoutine = FsRtlCompletionRoutinePriv;
179         WaitCtx->CompletionContext = WaitEvent;
180         KeInitializeEvent(WaitEvent, NotificationEvent, FALSE);
181     }
182 
183     /* If we got a prepost routine, call it now! */
184     if (PostIrpRoutine != NULL)
185     {
186         PostIrpRoutine(CompletionContext, Irp);
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         {
210             IoMarkIrpPending(Irp);
211         }
212         FsRtlCancelWaitIrp(NULL, Irp);
213     }
214     /* Otherwise, put ourselves as the cancel routine and start waiting */
215     else
216     {
217         IoSetCancelRoutine(Irp, FsRtlCancelWaitIrp);
218         IoReleaseCancelSpinLock(Irp->CancelIrql);
219         if (CompletionRoutine != NULL)
220         {
221             IoMarkIrpPending(Irp);
222         }
223         else
224         {
225             ExReleaseFastMutexUnsafe(Oplock->IntLock);
226             Locked = FALSE;
227             KeWaitForSingleObject(WaitEvent, Executive, KernelMode, FALSE, NULL);
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
FsRtlOplockBreakNotify(IN PINTERNAL_OPLOCK Oplock,IN PIO_STACK_LOCATION Stack,IN PIRP Irp)239 FsRtlOplockBreakNotify(IN PINTERNAL_OPLOCK Oplock,
240                        IN PIO_STACK_LOCATION Stack,
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;
251         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
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;
260         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
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;
271         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
272         ExReleaseFastMutexUnsafe(Oplock->IntLock);
273         return STATUS_SUCCESS;
274     }
275 
276     /* Otherwise, wait on the IRP */
277     Irp->IoStatus.Status = STATUS_SUCCESS;
278     FsRtlWaitOnIrp(Oplock, Irp, NULL, FsRtlNotifyCompletion, NULL, NULL);
279     return STATUS_SUCCESS;
280 }
281 
282 VOID
FsRtlRemoveAndCompleteIrp(IN PIRP Irp)283 FsRtlRemoveAndCompleteIrp(IN PIRP Irp)
284 {
285     PIO_STACK_LOCATION Stack;
286 
287     DPRINT("FsRtlRemoveAndCompleteIrp(%p)\n", Irp);
288 
289     Stack = IoGetCurrentIrpStackLocation(Irp);
290 
291     /* Remove our extra ref */
292     ObDereferenceObject(Stack->FileObject);
293 
294     /* Remove our cancel routine */
295     IoAcquireCancelSpinLock(&Irp->CancelIrql);
296     IoSetCancelRoutine(Irp, NULL);
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 
306     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
307 }
308 
309 VOID
310 NTAPI
FsRtlCancelOplockIIIrp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)311 FsRtlCancelOplockIIIrp(IN PDEVICE_OBJECT DeviceObject,
312                        IN PIRP Irp)
313 {
314     PINTERNAL_OPLOCK Oplock;
315     PLIST_ENTRY NextEntry;
316     PIRP ListIrp;
317     BOOLEAN Removed;
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! )*/
325     IoSetCancelRoutine(Irp, NULL);
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
FsRtlAcknowledgeOplockBreak(IN PINTERNAL_OPLOCK Oplock,IN PIO_STACK_LOCATION Stack,IN PIRP Irp,IN BOOLEAN SwitchToLevel2)356 FsRtlAcknowledgeOplockBreak(IN PINTERNAL_OPLOCK Oplock,
357                             IN PIO_STACK_LOCATION Stack,
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;
372         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
373         return 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;
383         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
384         ExReleaseFastMutexUnsafe(Oplock->IntLock);
385         return STATUS_INVALID_OPLOCK_PROTOCOL;
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 */
397         ASSERT(!IoIsOperationSynchronous(Irp));
398 
399         /* Mark the IRP pending, and queue it for the shared IRPs */
400         IoMarkIrpPending(Irp);
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;
418             FsRtlCancelOplockIIIrp(NULL, Irp);
419         }
420         /* Otherwise, just set our cancel routine */
421         else
422         {
423             IoSetCancelRoutine(Irp, FsRtlCancelOplockIIIrp);
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;
431         IofCompleteRequest(Irp, IO_DISK_INCREMENT);
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;
439         IofCompleteRequest(Irp, IO_DISK_INCREMENT);
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);
449         FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
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
FsRtlOpBatchBreakClosePending(IN PINTERNAL_OPLOCK Oplock,IN PIO_STACK_LOCATION Stack,IN PIRP Irp)470 FsRtlOpBatchBreakClosePending(IN PINTERNAL_OPLOCK Oplock,
471                               IN PIO_STACK_LOCATION Stack,
472                               IN PIRP Irp)
473 {
474     NTSTATUS Status;
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;
486         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
487         return STATUS_INVALID_OPLOCK_PROTOCOL;
488     }
489 
490     Status = STATUS_SUCCESS;
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);
517                 FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
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     {
530         Status = STATUS_INVALID_OPLOCK_PROTOCOL;
531     }
532 
533     /* And complete */
534     Irp->IoStatus.Status = Status;
535     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
536     ExReleaseFastMutexUnsafe(Oplock->IntLock);
537 
538     return Status;
539 }
540 
541 PINTERNAL_OPLOCK
FsRtlAllocateOplock(VOID)542 FsRtlAllocateOplock(VOID)
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 */
553         Oplock = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE | POOL_COLD_ALLOCATION, sizeof(INTERNAL_OPLOCK), TAG_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) */
556         Oplock->IntLock = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, sizeof(FAST_MUTEX), TAG_OPLOCK);
557         ExInitializeFastMutex(Oplock->IntLock);
558         /* Initialize the IRP list for level 2 oplock */
559         InitializeListHead(&Oplock->SharedListHead);
560         /* And for the wait IRPs */
561         InitializeListHead(&Oplock->WaitListHead);
562         Oplock->Flags = NO_OPLOCK;
563     }
564     _SEH2_FINALLY
565     {
566         /* In case of abnormal termination, it means either OPLOCK or FAST_MUTEX allocation failed */
567         if (_SEH2_AbnormalTermination())
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
FsRtlCancelExclusiveIrp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)584 FsRtlCancelExclusiveIrp(IN PDEVICE_OBJECT DeviceObject,
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 */
597     IoSetCancelRoutine(Irp, NULL);
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 */
606         IntOplock->ExclusiveIrp->IoStatus.Status = STATUS_CANCELLED;
607         IoCompleteRequest(IntOplock->ExclusiveIrp, IO_DISK_INCREMENT);
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);
621             FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
622         }
623     }
624 
625     /* Done! */
626     ExReleaseFastMutexUnsafe(IntOplock->IntLock);
627 }
628 
629 NTSTATUS
FsRtlRequestExclusiveOplock(IN POPLOCK Oplock,IN PIO_STACK_LOCATION Stack,IN PIRP Irp,IN ULONG Flags)630 FsRtlRequestExclusiveOplock(IN POPLOCK Oplock,
631                             IN PIO_STACK_LOCATION Stack,
632                             IN PIRP Irp,
633                             IN ULONG Flags)
634 {
635     PINTERNAL_OPLOCK IntOplock;
636     PIRP ListIrp;
637     BOOLEAN Locked;
638     NTSTATUS Status;
639 
640     DPRINT("FsRtlRequestExclusiveOplock(%p, %p, %p, %lu)\n", Oplock, Stack, Irp, Flags);
641 
642     IntOplock = *Oplock;
643     Locked = FALSE;
644     Status = STATUS_SUCCESS;
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 */
661         if (Flags == (EXCLUSIVE_LOCK | PENDING_LOCK | FILTER_OPLOCK))
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 */
672             if (BooleanFlagOn(IntOplock->Flags, (LEVEL_2_OPLOCK | PENDING_LOCK | NO_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 */
688                 IoMarkIrpPending(Irp);
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;
699                     FsRtlCancelExclusiveIrp(NULL, Irp);
700                     Status = STATUS_CANCELLED;
701                 }
702                 else
703                 {
704                     IoSetCancelRoutine(Irp, FsRtlCancelExclusiveIrp);
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;
714                     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
715                     Status = STATUS_OPLOCK_NOT_GRANTED;
716                 }
717             }
718         }
719     }
720     /* If locked, release */
721     _SEH2_FINALLY
722     {
723         if (Locked)
724         {
725             ExReleaseFastMutexUnsafe(IntOplock->IntLock);
726         }
727     }
728     _SEH2_END;
729 
730     return Status;
731 }
732 
733 NTSTATUS
FsRtlRequestOplockII(IN POPLOCK Oplock,IN PIO_STACK_LOCATION Stack,IN PIRP Irp)734 FsRtlRequestOplockII(IN POPLOCK Oplock,
735                      IN PIO_STACK_LOCATION Stack,
736                      IN PIRP Irp)
737 {
738     BOOLEAN Locked;
739     NTSTATUS Status;
740     PINTERNAL_OPLOCK IntOplock;
741 
742     DPRINT("FsRtlRequestOplockII(%p, %p, %p)\n", Oplock, Stack, Irp);
743 
744     IntOplock = *Oplock;
745     Locked = FALSE;
746     Status = STATUS_SUCCESS;
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         {
764             IoMarkIrpPending(Irp);
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;
786                 FsRtlCancelOplockIIIrp(NULL, Irp);
787                 Status = STATUS_CANCELLED;
788             }
789             else
790             {
791                 IoSetCancelRoutine(Irp, FsRtlCancelOplockIIIrp);
792                 IoReleaseCancelSpinLock(Irp->CancelIrql);
793             }
794         }
795         /* Otherwise, just fail */
796         else
797         {
798             Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
799             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
800             Status = STATUS_OPLOCK_NOT_GRANTED;
801         }
802     }
803     _SEH2_FINALLY
804     {
805         if (Locked)
806         {
807             ExReleaseFastMutexUnsafe(IntOplock->IntLock);
808         }
809     }
810     _SEH2_END;
811 
812     return Status;
813 }
814 
815 VOID
FsRtlOplockCleanup(IN PINTERNAL_OPLOCK Oplock,IN PIO_STACK_LOCATION Stack)816 FsRtlOplockCleanup(IN PINTERNAL_OPLOCK Oplock,
817                    IN PIO_STACK_LOCATION Stack)
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);
891                     FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
892                 }
893             }
894         }
895     }
896     ExReleaseFastMutexUnsafe(Oplock->IntLock);
897 }
898 
899 NTSTATUS
900 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)901 FsRtlOplockBreakToNone(IN PINTERNAL_OPLOCK Oplock,
902                        IN PIO_STACK_LOCATION Stack,
903                        IN PIRP Irp,
904                        IN PVOID Context,
905                        IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
906                        IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL)
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);
956                 FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
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);
1029         return STATUS_OPLOCK_BREAK_IN_PROGRESS;
1030     }
1031 }
1032 
1033 NTSTATUS
1034 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)1035 FsRtlOplockBreakToII(IN PINTERNAL_OPLOCK Oplock,
1036                      IN PIO_STACK_LOCATION Stack,
1037                      IN PIRP Irp,
1038                      IN PVOID Context,
1039                      IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
1040                      IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL)
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);
1099                 FsRtlRemoveAndCompleteWaitIrp(WaitCtx);
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);
1136         return STATUS_OPLOCK_BREAK_IN_PROGRESS;
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
FsRtlCheckOplock(IN POPLOCK Oplock,IN PIRP Irp,IN PVOID Context,IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL)1170 FsRtlCheckOplock(IN POPLOCK Oplock,
1171                  IN PIRP Irp,
1172                  IN PVOID Context,
1173                  IN POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine OPTIONAL,
1174                  IN POPLOCK_FS_PREPOST_IRP PostIrpRoutine OPTIONAL)
1175 {
1176     PINTERNAL_OPLOCK IntOplock;
1177     PIO_STACK_LOCATION Stack;
1178     ACCESS_MASK DesiredAccess;
1179     FILE_INFORMATION_CLASS FileInfo;
1180     ULONG CreateDisposition;
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 
1212     Stack = IoGetCurrentIrpStackLocation(Irp);
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 */
1229         BreakToNoneIfRequired;
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! */
1242         BreakToNoneIfRequired;
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 */
1249         BreakToNoneIfRequired;
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 */
1262         BreakToIIIfRequired;
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 */
1271         if ((!(DesiredAccess & ~(SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_READ_DATA)) && !(Stack->Parameters.Create.Options & FILE_RESERVE_OPFILTER))
1272             || (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)))
1273         {
1274             return STATUS_SUCCESS;
1275         }
1276 
1277         /* Otherwise, check the disposition */
1278         CreateDisposition = (Stack->Parameters.Create.Options >> 24) & 0x000000FF;
1279         if (!CreateDisposition || CreateDisposition == FILE_OVERWRITE ||
1280             CreateDisposition == FILE_OVERWRITE_IF ||
1281             BooleanFlagOn(Stack->Parameters.Create.Options, FILE_RESERVE_OPFILTER))
1282         {
1283             /* Not us, we have to break the oplock! */
1284             BreakToNoneIfRequired;
1285 
1286             return STATUS_SUCCESS;
1287         }
1288 
1289         /* It's fine, we can have the oplock shared */
1290         BreakToIIIfRequired;
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! */
1297         BreakToIIIfRequired;
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;
1305         if (FileInfo == FileRenameInformation || FileInfo == FileLinkInformation ||
1306             FileInfo == FileShortNameInformation)
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             {
1316                 BreakToNoneIfRequired;
1317 
1318                 return STATUS_SUCCESS;
1319             }
1320         }
1321         else if (FileInfo == FileAllocationInformation)
1322         {
1323             BreakToNoneIfRequired;
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 */
1336             BreakToNoneIfRequired;
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
FsRtlCurrentBatchOplock(IN POPLOCK Oplock)1364 FsRtlCurrentBatchOplock(IN POPLOCK Oplock)
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
FsRtlInitializeOplock(IN OUT POPLOCK Oplock)1400 FsRtlInitializeOplock(IN OUT POPLOCK Oplock)
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
FsRtlOplockFsctrl(IN POPLOCK Oplock,IN PIRP Irp,IN ULONG OpenCount)1430 FsRtlOplockFsctrl(IN POPLOCK Oplock,
1431                   IN PIRP Irp,
1432                   IN ULONG OpenCount)
1433 {
1434     PIO_STACK_LOCATION Stack;
1435     PINTERNAL_OPLOCK IntOplock;
1436 
1437     PAGED_CODE();
1438 
1439     DPRINT("FsRtlOplockFsctrl(%p, %p, %lu)\n", Oplock, Irp, OpenCount);
1440 
1441     IntOplock = *Oplock;
1442     Stack = IoGetCurrentIrpStackLocation(Irp);
1443     /* Make sure it's not called on create */
1444     if (Stack->MajorFunction != IRP_MJ_CREATE)
1445     {
1446         switch (Stack->Parameters.FileSystemControl.FsControlCode)
1447         {
1448             case FSCTL_OPLOCK_BREAK_NOTIFY:
1449                 return FsRtlOplockBreakNotify(IntOplock, Stack, Irp);
1450 
1451             case FSCTL_OPLOCK_BREAK_ACK_NO_2:
1452                 return FsRtlAcknowledgeOplockBreak(IntOplock, Stack, Irp, FALSE);
1453 
1454             case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
1455                 return FsRtlOpBatchBreakClosePending(IntOplock, Stack, Irp);
1456 
1457             case FSCTL_REQUEST_OPLOCK_LEVEL_1:
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) &&
1462                     !BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
1463                 {
1464                     return FsRtlRequestExclusiveOplock(Oplock, Stack, Irp, EXCLUSIVE_LOCK | LEVEL_1_OPLOCK);
1465                 }
1466                 /* Not matching, fail */
1467                 else
1468                 {
1469                     Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1470                     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1471                     return STATUS_OPLOCK_NOT_GRANTED;
1472                 }
1473 
1474             case FSCTL_REQUEST_OPLOCK_LEVEL_2:
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) &&
1479                     !BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
1480                 {
1481                     return FsRtlRequestOplockII(Oplock, Stack, Irp);
1482                 }
1483                 /* Not matching, fail */
1484                 else
1485                 {
1486                     Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1487                     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1488                     return STATUS_OPLOCK_NOT_GRANTED;
1489                 }
1490 
1491             case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
1492                 return FsRtlAcknowledgeOplockBreak(IntOplock, Stack, Irp, TRUE);
1493 
1494             case FSCTL_REQUEST_BATCH_OPLOCK:
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) &&
1499                     !BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
1500                 {
1501                     return FsRtlRequestExclusiveOplock(Oplock, Stack, Irp, EXCLUSIVE_LOCK | BATCH_OPLOCK);
1502                 }
1503                 /* Not matching, fail */
1504                 else
1505                 {
1506                     Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1507                     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1508                     return STATUS_OPLOCK_NOT_GRANTED;
1509                 }
1510 
1511             case FSCTL_REQUEST_FILTER_OPLOCK:
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) &&
1516                     !BooleanFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !BooleanFlagOn(Stack->FileObject->Flags, FO_CLEANUP_COMPLETE))
1517                 {
1518                     return FsRtlRequestExclusiveOplock(Oplock, Stack, Irp, EXCLUSIVE_LOCK | FILTER_OPLOCK);
1519                 }
1520                 /* Not matching, fail */
1521                 else
1522                 {
1523                     Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
1524                     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1525                     return STATUS_OPLOCK_NOT_GRANTED;
1526                 }
1527 
1528             default:
1529                 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
1530                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
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     {
1542         return FsRtlRequestExclusiveOplock(Oplock, Stack, NULL, EXCLUSIVE_LOCK | PENDING_LOCK | FILTER_OPLOCK);
1543     }
1544 
1545     return STATUS_OPLOCK_NOT_GRANTED;
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
FsRtlOplockIsFastIoPossible(IN POPLOCK Oplock)1564 FsRtlOplockIsFastIoPossible(IN POPLOCK Oplock)
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
FsRtlUninitializeOplock(IN POPLOCK Oplock)1600 FsRtlUninitializeOplock(IN POPLOCK Oplock)
1601 {
1602     PINTERNAL_OPLOCK IntOplock;
1603     PLIST_ENTRY NextEntry;
1604     PWAIT_CONTEXT WaitCtx;
1605     PIRP Irp;
1606     PIO_STACK_LOCATION Stack;
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);
1636             IoSetCancelRoutine(Irp, NULL);
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);
1657             IoSetCancelRoutine(Irp, NULL);
1658             IoReleaseCancelSpinLock(Irp->CancelIrql);
1659 
1660             /* Dereference the file object */
1661             Stack = IoGetCurrentIrpStackLocation(Irp);
1662             ObDereferenceObject(Stack->FileObject);
1663 
1664             /* And complete */
1665             Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
1666             Irp->IoStatus.Status = STATUS_SUCCESS;
1667             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
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);
1676             IoSetCancelRoutine(Irp, NULL);
1677             IoReleaseCancelSpinLock(Irp->CancelIrql);
1678 
1679             /* And complete */
1680             Irp->IoStatus.Information = FILE_OPLOCK_BROKEN_TO_NONE;
1681             Irp->IoStatus.Status = STATUS_SUCCESS;
1682             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1683             IntOplock->ExclusiveIrp = NULL;
1684 
1685             /* If still referenced, dereference */
1686             if (IntOplock->FileObject != NULL)
1687             {
1688                 ObDereferenceObject(IntOplock->FileObject);
1689             }
1690         }
1691     }
1692     _SEH2_FINALLY
1693     {
1694         ExReleaseFastMutexUnsafe(IntOplock->IntLock);
1695     }
1696     _SEH2_END;
1697 
1698     ExFreePoolWithTag(IntOplock->IntLock, TAG_OPLOCK);
1699     ExFreePoolWithTag(IntOplock, TAG_OPLOCK);
1700 }
1701 
1702