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