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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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