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