[NTOSKRNL] Implement FsRtlCheckOplock(), FsRtlCurrentBatchOplock(), FsRtlInitializeOp...
[reactos.git] / ntoskrnl / fsrtl / oplock.c
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 return 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 ObfDereferenceObject(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 (_abnormal_termination())
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