1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/fsrtl/filelock.c 5 * PURPOSE: File Locking implementation for File System Drivers 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS *******************************************************************/ 16 17 PAGED_LOOKASIDE_LIST FsRtlFileLockLookasideList; 18 19 /* Note: this aligns the two types of lock entry structs so we can access the 20 FILE_LOCK_INFO part in common. Add elements after Shared if new stuff is needed. 21 */ 22 typedef union _COMBINED_LOCK_ELEMENT 23 { 24 struct 25 { 26 LIST_ENTRY dummy; 27 FILE_SHARED_LOCK_ENTRY Shared; 28 }; 29 FILE_EXCLUSIVE_LOCK_ENTRY Exclusive; 30 } 31 COMBINED_LOCK_ELEMENT, *PCOMBINED_LOCK_ELEMENT; 32 33 typedef struct _LOCK_INFORMATION 34 { 35 RTL_GENERIC_TABLE RangeTable; 36 IO_CSQ Csq; 37 KSPIN_LOCK CsqLock; 38 LIST_ENTRY CsqList; 39 PFILE_LOCK BelongsTo; 40 LIST_ENTRY SharedLocks; 41 ULONG Generation; 42 } 43 LOCK_INFORMATION, *PLOCK_INFORMATION; 44 45 typedef struct _LOCK_SHARED_RANGE 46 { 47 LIST_ENTRY Entry; 48 LARGE_INTEGER Start, End; 49 ULONG Key; 50 PVOID ProcessId; 51 } 52 LOCK_SHARED_RANGE, *PLOCK_SHARED_RANGE; 53 54 /* PRIVATE FUNCTIONS *********************************************************/ 55 56 VOID 57 NTAPI 58 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine, 59 IN PVOID Context, 60 IN PIRP Irp, 61 IN NTSTATUS Status, 62 OUT PNTSTATUS NewStatus, 63 IN PFILE_OBJECT FileObject OPTIONAL); 64 65 /* Generic table methods */ 66 67 static PVOID NTAPI LockAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes) 68 { 69 PVOID Result; 70 Result = ExAllocatePoolWithTag(NonPagedPool, Bytes, TAG_TABLE); 71 DPRINT("LockAllocate(%lu) => %p\n", Bytes, Result); 72 return Result; 73 } 74 75 static VOID NTAPI LockFree(PRTL_GENERIC_TABLE Table, PVOID Buffer) 76 { 77 DPRINT("LockFree(%p)\n", Buffer); 78 ExFreePoolWithTag(Buffer, TAG_TABLE); 79 } 80 81 static RTL_GENERIC_COMPARE_RESULTS NTAPI LockCompare 82 (PRTL_GENERIC_TABLE Table, PVOID PtrA, PVOID PtrB) 83 { 84 PCOMBINED_LOCK_ELEMENT A = PtrA, B = PtrB; 85 RTL_GENERIC_COMPARE_RESULTS Result; 86 #if 0 87 DPRINT("Starting to compare element %x to element %x\n", PtrA, PtrB); 88 #endif 89 /* Match if we overlap */ 90 if (((A->Exclusive.FileLock.StartingByte.QuadPart < 91 B->Exclusive.FileLock.EndingByte.QuadPart) && 92 (A->Exclusive.FileLock.StartingByte.QuadPart >= 93 B->Exclusive.FileLock.StartingByte.QuadPart)) || 94 ((B->Exclusive.FileLock.StartingByte.QuadPart < 95 A->Exclusive.FileLock.EndingByte.QuadPart) && 96 (B->Exclusive.FileLock.StartingByte.QuadPart >= 97 A->Exclusive.FileLock.StartingByte.QuadPart))) 98 return GenericEqual; 99 /* Otherwise, key on the starting byte */ 100 Result = 101 (A->Exclusive.FileLock.StartingByte.QuadPart < 102 B->Exclusive.FileLock.StartingByte.QuadPart) ? GenericLessThan : 103 (A->Exclusive.FileLock.StartingByte.QuadPart > 104 B->Exclusive.FileLock.StartingByte.QuadPart) ? GenericGreaterThan : 105 GenericEqual; 106 #if 0 107 DPRINT("Compare(%x:%x) %x-%x to %x-%x => %d\n", 108 A,B, 109 A->Exclusive.FileLock.StartingByte.LowPart, 110 A->Exclusive.FileLock.EndingByte.LowPart, 111 B->Exclusive.FileLock.StartingByte.LowPart, 112 B->Exclusive.FileLock.EndingByte.LowPart, 113 Result); 114 #endif 115 return Result; 116 } 117 118 /* CSQ methods */ 119 120 static NTSTATUS NTAPI LockInsertIrpEx 121 (PIO_CSQ Csq, 122 PIRP Irp, 123 PVOID InsertContext) 124 { 125 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); 126 InsertTailList(&LockInfo->CsqList, &Irp->Tail.Overlay.ListEntry); 127 return STATUS_SUCCESS; 128 } 129 130 static VOID NTAPI LockRemoveIrp(PIO_CSQ Csq, PIRP Irp) 131 { 132 RemoveEntryList(&Irp->Tail.Overlay.ListEntry); 133 } 134 135 static PIRP NTAPI LockPeekNextIrp(PIO_CSQ Csq, PIRP Irp, PVOID PeekContext) 136 { 137 // Context will be a COMBINED_LOCK_ELEMENT. We're looking for a 138 // lock that can be acquired, now that the lock matching PeekContext 139 // has been removed. 140 COMBINED_LOCK_ELEMENT LockElement; 141 PCOMBINED_LOCK_ELEMENT WhereUnlock = PeekContext; 142 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); 143 PLIST_ENTRY Following; 144 DPRINT("PeekNextIrp(IRP %p, Context %p)\n", Irp, PeekContext); 145 if (!Irp) 146 { 147 Following = LockInfo->CsqList.Flink; 148 } 149 else 150 Following = Irp->Tail.Overlay.ListEntry.Flink; 151 152 DPRINT("ListEntry %p Head %p\n", Following, &LockInfo->CsqList); 153 for (; 154 Following != &LockInfo->CsqList; 155 Following = Following->Flink) 156 { 157 PIO_STACK_LOCATION IoStack; 158 BOOLEAN Matching; 159 Irp = CONTAINING_RECORD(Following, IRP, Tail.Overlay.ListEntry); 160 DPRINT("Irp %p\n", Irp); 161 IoStack = IoGetCurrentIrpStackLocation(Irp); 162 LockElement.Exclusive.FileLock.StartingByte = 163 IoStack->Parameters.LockControl.ByteOffset; 164 LockElement.Exclusive.FileLock.EndingByte.QuadPart = 165 LockElement.Exclusive.FileLock.StartingByte.QuadPart + 166 IoStack->Parameters.LockControl.Length->QuadPart; 167 /* If a context was specified, it's a range to check to unlock */ 168 if (WhereUnlock) 169 { 170 Matching = LockCompare 171 (&LockInfo->RangeTable, &LockElement, WhereUnlock) != GenericEqual; 172 } 173 /* Else get any completable IRP */ 174 else 175 { 176 Matching = FALSE; 177 } 178 if (!Matching) 179 { 180 // This IRP is fine... 181 DPRINT("Returning the IRP %p\n", Irp); 182 return Irp; 183 } 184 } 185 DPRINT("Return NULL\n"); 186 return NULL; 187 } 188 189 static VOID NTAPI 190 LockAcquireQueueLock(PIO_CSQ Csq, PKIRQL Irql) 191 { 192 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); 193 KeAcquireSpinLock(&LockInfo->CsqLock, Irql); 194 } 195 196 static VOID NTAPI 197 LockReleaseQueueLock(PIO_CSQ Csq, KIRQL Irql) 198 { 199 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); 200 KeReleaseSpinLock(&LockInfo->CsqLock, Irql); 201 } 202 203 static VOID NTAPI 204 LockCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp) 205 { 206 NTSTATUS Status; 207 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); 208 DPRINT("Complete cancelled IRP %p Status %x\n", Irp, STATUS_CANCELLED); 209 FsRtlCompleteLockIrpReal 210 (LockInfo->BelongsTo->CompleteLockIrpRoutine, 211 NULL, 212 Irp, 213 STATUS_CANCELLED, 214 &Status, 215 NULL); 216 } 217 218 VOID 219 NTAPI 220 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine, 221 IN PVOID Context, 222 IN PIRP Irp, 223 IN NTSTATUS Status, 224 OUT PNTSTATUS NewStatus, 225 IN PFILE_OBJECT FileObject OPTIONAL) 226 { 227 /* Check if we have a complete routine */ 228 Irp->IoStatus.Information = 0; 229 if (CompleteRoutine) 230 { 231 /* Check if we have a file object */ 232 if (FileObject) FileObject->LastLock = NULL; 233 234 /* Set the I/O Status and do completion */ 235 Irp->IoStatus.Status = Status; 236 DPRINT("Calling completion routine %p Status %x\n", Irp, Status); 237 *NewStatus = CompleteRoutine(Context, Irp); 238 } 239 else 240 { 241 /* Otherwise do a normal I/O complete request */ 242 DPRINT("Completing IRP %p Status %x\n", Irp, Status); 243 FsRtlCompleteRequest(Irp, Status); 244 *NewStatus = Status; 245 } 246 } 247 248 /* PUBLIC FUNCTIONS **********************************************************/ 249 250 /* 251 * @implemented 252 */ 253 PFILE_LOCK_INFO 254 NTAPI 255 FsRtlGetNextFileLock(IN PFILE_LOCK FileLock, 256 IN BOOLEAN Restart) 257 { 258 PCOMBINED_LOCK_ELEMENT Entry; 259 if (!FileLock->LockInformation) return NULL; 260 Entry = RtlEnumerateGenericTable(FileLock->LockInformation, Restart); 261 if (!Entry) return NULL; 262 else return &Entry->Exclusive.FileLock; 263 } 264 265 VOID 266 NTAPI 267 FsRtlpExpandLockElement 268 (PCOMBINED_LOCK_ELEMENT ToExpand, 269 PCOMBINED_LOCK_ELEMENT Conflict) 270 { 271 if (ToExpand->Exclusive.FileLock.StartingByte.QuadPart > 272 Conflict->Exclusive.FileLock.StartingByte.QuadPart) 273 { 274 ToExpand->Exclusive.FileLock.StartingByte = 275 Conflict->Exclusive.FileLock.StartingByte; 276 } 277 if (ToExpand->Exclusive.FileLock.EndingByte.QuadPart < 278 Conflict->Exclusive.FileLock.EndingByte.QuadPart) 279 { 280 ToExpand->Exclusive.FileLock.EndingByte = 281 Conflict->Exclusive.FileLock.EndingByte; 282 } 283 } 284 285 /* This function expands the conflicting range Conflict by removing and reinserting it, 286 then adds a shared range of the same size */ 287 PCOMBINED_LOCK_ELEMENT 288 NTAPI 289 FsRtlpRebuildSharedLockRange 290 (PFILE_LOCK FileLock, 291 PLOCK_INFORMATION LockInfo, 292 PCOMBINED_LOCK_ELEMENT Conflict) 293 { 294 /* Starting at Conflict->StartingByte and going to Conflict->EndingByte 295 * capture and expand a shared range from the shared range list. 296 * Finish when we've incorporated all overlapping shared regions. 297 */ 298 BOOLEAN InsertedNew = FALSE, RemovedOld; 299 COMBINED_LOCK_ELEMENT NewElement = *Conflict; 300 PCOMBINED_LOCK_ELEMENT Entry; 301 while ((Entry = RtlLookupElementGenericTable 302 (FileLock->LockInformation, &NewElement))) 303 { 304 FsRtlpExpandLockElement(&NewElement, Entry); 305 RemovedOld = RtlDeleteElementGenericTable 306 (&LockInfo->RangeTable, 307 Entry); 308 ASSERT(RemovedOld); 309 } 310 Conflict = RtlInsertElementGenericTable 311 (&LockInfo->RangeTable, 312 &NewElement, 313 sizeof(NewElement), 314 &InsertedNew); 315 ASSERT(InsertedNew); 316 return Conflict; 317 } 318 319 /* 320 * @implemented 321 */ 322 BOOLEAN 323 NTAPI 324 FsRtlPrivateLock(IN PFILE_LOCK FileLock, 325 IN PFILE_OBJECT FileObject, 326 IN PLARGE_INTEGER FileOffset, 327 IN PLARGE_INTEGER Length, 328 IN PEPROCESS Process, 329 IN ULONG Key, 330 IN BOOLEAN FailImmediately, 331 IN BOOLEAN ExclusiveLock, 332 OUT PIO_STATUS_BLOCK IoStatus, 333 IN PIRP Irp OPTIONAL, 334 IN PVOID Context OPTIONAL, 335 IN BOOLEAN AlreadySynchronized) 336 { 337 NTSTATUS Status; 338 COMBINED_LOCK_ELEMENT ToInsert; 339 PCOMBINED_LOCK_ELEMENT Conflict; 340 PLOCK_INFORMATION LockInfo; 341 PLOCK_SHARED_RANGE NewSharedRange; 342 BOOLEAN InsertedNew; 343 ULARGE_INTEGER UnsignedStart; 344 ULARGE_INTEGER UnsignedEnd; 345 346 DPRINT("FsRtlPrivateLock(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x, FailImmediately %u, Exclusive %u)\n", 347 &FileObject->FileName, 348 FileOffset->HighPart, 349 FileOffset->LowPart, 350 (int)FileOffset->QuadPart, 351 Length->HighPart, 352 Length->LowPart, 353 (int)Length->QuadPart, 354 Key, 355 FailImmediately, 356 ExclusiveLock); 357 358 UnsignedStart.QuadPart = FileOffset->QuadPart; 359 UnsignedEnd.QuadPart = FileOffset->QuadPart + Length->QuadPart; 360 361 if (UnsignedEnd.QuadPart < UnsignedStart.QuadPart) 362 { 363 DPRINT("File offset out of range\n"); 364 IoStatus->Status = STATUS_INVALID_PARAMETER; 365 if (Irp) 366 { 367 DPRINT("Complete lock %p Status %x\n", Irp, IoStatus->Status); 368 FsRtlCompleteLockIrpReal 369 (FileLock->CompleteLockIrpRoutine, 370 Context, 371 Irp, 372 IoStatus->Status, 373 &Status, 374 FileObject); 375 } 376 return FALSE; 377 } 378 379 /* Initialize the lock, if necessary */ 380 if (!FileLock->LockInformation) 381 { 382 LockInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(LOCK_INFORMATION), TAG_FLOCK); 383 if (!LockInfo) 384 { 385 IoStatus->Status = STATUS_NO_MEMORY; 386 return FALSE; 387 } 388 FileLock->LockInformation = LockInfo; 389 390 LockInfo->BelongsTo = FileLock; 391 InitializeListHead(&LockInfo->SharedLocks); 392 393 RtlInitializeGenericTable 394 (&LockInfo->RangeTable, 395 LockCompare, 396 LockAllocate, 397 LockFree, 398 NULL); 399 400 KeInitializeSpinLock(&LockInfo->CsqLock); 401 InitializeListHead(&LockInfo->CsqList); 402 403 IoCsqInitializeEx 404 (&LockInfo->Csq, 405 LockInsertIrpEx, 406 LockRemoveIrp, 407 LockPeekNextIrp, 408 LockAcquireQueueLock, 409 LockReleaseQueueLock, 410 LockCompleteCanceledIrp); 411 } 412 413 LockInfo = FileLock->LockInformation; 414 ToInsert.Exclusive.FileLock.FileObject = FileObject; 415 ToInsert.Exclusive.FileLock.StartingByte = *FileOffset; 416 ToInsert.Exclusive.FileLock.EndingByte.QuadPart = FileOffset->QuadPart + Length->QuadPart; 417 ToInsert.Exclusive.FileLock.ProcessId = Process; 418 ToInsert.Exclusive.FileLock.Key = Key; 419 ToInsert.Exclusive.FileLock.ExclusiveLock = ExclusiveLock; 420 421 Conflict = RtlInsertElementGenericTable 422 (FileLock->LockInformation, 423 &ToInsert, 424 sizeof(ToInsert), 425 &InsertedNew); 426 427 if (Conflict && !InsertedNew) 428 { 429 if (Conflict->Exclusive.FileLock.ExclusiveLock || ExclusiveLock) 430 { 431 DPRINT("Conflict %08x%08x:%08x%08x Exc %u (Want Exc %u)\n", 432 Conflict->Exclusive.FileLock.StartingByte.HighPart, 433 Conflict->Exclusive.FileLock.StartingByte.LowPart, 434 Conflict->Exclusive.FileLock.EndingByte.HighPart, 435 Conflict->Exclusive.FileLock.EndingByte.LowPart, 436 Conflict->Exclusive.FileLock.ExclusiveLock, 437 ExclusiveLock); 438 if (FailImmediately) 439 { 440 DPRINT("STATUS_FILE_LOCK_CONFLICT\n"); 441 IoStatus->Status = STATUS_FILE_LOCK_CONFLICT; 442 if (Irp) 443 { 444 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n"); 445 FsRtlCompleteLockIrpReal 446 (FileLock->CompleteLockIrpRoutine, 447 Context, 448 Irp, 449 IoStatus->Status, 450 &Status, 451 FileObject); 452 } 453 return FALSE; 454 } 455 else 456 { 457 IoStatus->Status = STATUS_PENDING; 458 if (Irp) 459 { 460 Irp->IoStatus.Information = LockInfo->Generation; 461 IoMarkIrpPending(Irp); 462 IoCsqInsertIrpEx 463 (&LockInfo->Csq, 464 Irp, 465 NULL, 466 NULL); 467 } 468 } 469 return FALSE; 470 } 471 else 472 { 473 ULONG i; 474 /* We know of at least one lock in range that's shared. We need to 475 * find out if any more exist and any are exclusive. */ 476 for (i = 0; i < RtlNumberGenericTableElements(&LockInfo->RangeTable); i++) 477 { 478 Conflict = RtlGetElementGenericTable(&LockInfo->RangeTable, i); 479 480 /* The first argument will be inserted as a shared range */ 481 if (Conflict && (LockCompare(&LockInfo->RangeTable, Conflict, &ToInsert) == GenericEqual)) 482 { 483 if (Conflict->Exclusive.FileLock.ExclusiveLock) 484 { 485 /* Found an exclusive match */ 486 if (FailImmediately) 487 { 488 IoStatus->Status = STATUS_FILE_LOCK_CONFLICT; 489 DPRINT("STATUS_FILE_LOCK_CONFLICT\n"); 490 if (Irp) 491 { 492 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n"); 493 FsRtlCompleteLockIrpReal 494 (FileLock->CompleteLockIrpRoutine, 495 Context, 496 Irp, 497 IoStatus->Status, 498 &Status, 499 FileObject); 500 } 501 } 502 else 503 { 504 IoStatus->Status = STATUS_PENDING; 505 if (Irp) 506 { 507 IoMarkIrpPending(Irp); 508 IoCsqInsertIrpEx 509 (&LockInfo->Csq, 510 Irp, 511 NULL, 512 NULL); 513 } 514 } 515 return FALSE; 516 } 517 } 518 } 519 520 DPRINT("Overlapping shared lock %wZ %08x%08x %08x%08x\n", 521 &FileObject->FileName, 522 Conflict->Exclusive.FileLock.StartingByte.HighPart, 523 Conflict->Exclusive.FileLock.StartingByte.LowPart, 524 Conflict->Exclusive.FileLock.EndingByte.HighPart, 525 Conflict->Exclusive.FileLock.EndingByte.LowPart); 526 Conflict = FsRtlpRebuildSharedLockRange(FileLock, 527 LockInfo, 528 &ToInsert); 529 if (!Conflict) 530 { 531 IoStatus->Status = STATUS_NO_MEMORY; 532 if (Irp) 533 { 534 FsRtlCompleteLockIrpReal 535 (FileLock->CompleteLockIrpRoutine, 536 Context, 537 Irp, 538 IoStatus->Status, 539 &Status, 540 FileObject); 541 } 542 } 543 544 /* We got here because there were only overlapping shared locks */ 545 /* A shared lock is both a range *and* a list entry. Insert the 546 entry here. */ 547 548 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName); 549 NewSharedRange = 550 ExAllocatePoolWithTag(NonPagedPool, sizeof(*NewSharedRange), TAG_RANGE); 551 if (!NewSharedRange) 552 { 553 IoStatus->Status = STATUS_NO_MEMORY; 554 if (Irp) 555 { 556 FsRtlCompleteLockIrpReal 557 (FileLock->CompleteLockIrpRoutine, 558 Context, 559 Irp, 560 IoStatus->Status, 561 &Status, 562 FileObject); 563 } 564 return FALSE; 565 } 566 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName); 567 NewSharedRange->Start = *FileOffset; 568 NewSharedRange->End.QuadPart = FileOffset->QuadPart + Length->QuadPart; 569 NewSharedRange->Key = Key; 570 NewSharedRange->ProcessId = ToInsert.Exclusive.FileLock.ProcessId; 571 InsertTailList(&LockInfo->SharedLocks, &NewSharedRange->Entry); 572 573 DPRINT("Acquired shared lock %wZ %08x%08x %08x%08x\n", 574 &FileObject->FileName, 575 Conflict->Exclusive.FileLock.StartingByte.HighPart, 576 Conflict->Exclusive.FileLock.StartingByte.LowPart, 577 Conflict->Exclusive.FileLock.EndingByte.HighPart, 578 Conflict->Exclusive.FileLock.EndingByte.LowPart); 579 IoStatus->Status = STATUS_SUCCESS; 580 if (Irp) 581 { 582 FsRtlCompleteLockIrpReal 583 (FileLock->CompleteLockIrpRoutine, 584 Context, 585 Irp, 586 IoStatus->Status, 587 &Status, 588 FileObject); 589 } 590 return TRUE; 591 } 592 } 593 else if (!Conflict) 594 { 595 /* Conflict here is (or would be) the newly inserted element, but we ran 596 * out of space probably. */ 597 IoStatus->Status = STATUS_NO_MEMORY; 598 if (Irp) 599 { 600 FsRtlCompleteLockIrpReal 601 (FileLock->CompleteLockIrpRoutine, 602 Context, 603 Irp, 604 IoStatus->Status, 605 &Status, 606 FileObject); 607 } 608 return FALSE; 609 } 610 else 611 { 612 DPRINT("Inserted new lock %wZ %08x%08x %08x%08x exclusive %u\n", 613 &FileObject->FileName, 614 Conflict->Exclusive.FileLock.StartingByte.HighPart, 615 Conflict->Exclusive.FileLock.StartingByte.LowPart, 616 Conflict->Exclusive.FileLock.EndingByte.HighPart, 617 Conflict->Exclusive.FileLock.EndingByte.LowPart, 618 Conflict->Exclusive.FileLock.ExclusiveLock); 619 if (!ExclusiveLock) 620 { 621 NewSharedRange = 622 ExAllocatePoolWithTag(NonPagedPool, sizeof(*NewSharedRange), TAG_RANGE); 623 if (!NewSharedRange) 624 { 625 IoStatus->Status = STATUS_NO_MEMORY; 626 if (Irp) 627 { 628 FsRtlCompleteLockIrpReal 629 (FileLock->CompleteLockIrpRoutine, 630 Context, 631 Irp, 632 IoStatus->Status, 633 &Status, 634 FileObject); 635 } 636 return FALSE; 637 } 638 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName); 639 NewSharedRange->Start = *FileOffset; 640 NewSharedRange->End.QuadPart = FileOffset->QuadPart + Length->QuadPart; 641 NewSharedRange->Key = Key; 642 NewSharedRange->ProcessId = Process; 643 InsertTailList(&LockInfo->SharedLocks, &NewSharedRange->Entry); 644 } 645 646 /* Assume all is cool, and lock is set */ 647 IoStatus->Status = STATUS_SUCCESS; 648 649 if (Irp) 650 { 651 /* Complete the request */ 652 FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine, 653 Context, 654 Irp, 655 IoStatus->Status, 656 &Status, 657 FileObject); 658 659 /* Update the status */ 660 IoStatus->Status = Status; 661 } 662 } 663 664 return TRUE; 665 } 666 667 /* 668 * @implemented 669 */ 670 BOOLEAN 671 NTAPI 672 FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock, 673 IN PIRP Irp) 674 { 675 BOOLEAN Result; 676 PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); 677 COMBINED_LOCK_ELEMENT ToFind; 678 PCOMBINED_LOCK_ELEMENT Found; 679 DPRINT("CheckLockForReadAccess(%wZ, Offset %08x%08x, Length %x)\n", 680 &IoStack->FileObject->FileName, 681 IoStack->Parameters.Read.ByteOffset.HighPart, 682 IoStack->Parameters.Read.ByteOffset.LowPart, 683 IoStack->Parameters.Read.Length); 684 if (!FileLock->LockInformation) { 685 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName); 686 return TRUE; 687 } 688 ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Read.ByteOffset; 689 ToFind.Exclusive.FileLock.EndingByte.QuadPart = 690 ToFind.Exclusive.FileLock.StartingByte.QuadPart + 691 IoStack->Parameters.Read.Length; 692 Found = RtlLookupElementGenericTable 693 (FileLock->LockInformation, 694 &ToFind); 695 if (!Found) { 696 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName); 697 return TRUE; 698 } 699 Result = !Found->Exclusive.FileLock.ExclusiveLock || 700 IoStack->Parameters.Read.Key == Found->Exclusive.FileLock.Key; 701 DPRINT("CheckLockForReadAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE"); 702 return Result; 703 } 704 705 /* 706 * @implemented 707 */ 708 BOOLEAN 709 NTAPI 710 FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock, 711 IN PIRP Irp) 712 { 713 BOOLEAN Result; 714 PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); 715 COMBINED_LOCK_ELEMENT ToFind; 716 PCOMBINED_LOCK_ELEMENT Found; 717 PEPROCESS Process = Irp->Tail.Overlay.Thread->ThreadsProcess; 718 DPRINT("CheckLockForWriteAccess(%wZ, Offset %08x%08x, Length %x)\n", 719 &IoStack->FileObject->FileName, 720 IoStack->Parameters.Write.ByteOffset.HighPart, 721 IoStack->Parameters.Write.ByteOffset.LowPart, 722 IoStack->Parameters.Write.Length); 723 if (!FileLock->LockInformation) { 724 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName); 725 return TRUE; 726 } 727 ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Write.ByteOffset; 728 ToFind.Exclusive.FileLock.EndingByte.QuadPart = 729 ToFind.Exclusive.FileLock.StartingByte.QuadPart + 730 IoStack->Parameters.Write.Length; 731 Found = RtlLookupElementGenericTable 732 (FileLock->LockInformation, 733 &ToFind); 734 if (!Found) { 735 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName); 736 return TRUE; 737 } 738 Result = Process == Found->Exclusive.FileLock.ProcessId; 739 DPRINT("CheckLockForWriteAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE"); 740 return Result; 741 } 742 743 /* 744 * @implemented 745 */ 746 BOOLEAN 747 NTAPI 748 FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock, 749 IN PLARGE_INTEGER FileOffset, 750 IN PLARGE_INTEGER Length, 751 IN ULONG Key, 752 IN PFILE_OBJECT FileObject, 753 IN PVOID Process) 754 { 755 PEPROCESS EProcess = Process; 756 COMBINED_LOCK_ELEMENT ToFind; 757 PCOMBINED_LOCK_ELEMENT Found; 758 DPRINT("FsRtlFastCheckLockForRead(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n", 759 &FileObject->FileName, 760 FileOffset->HighPart, 761 FileOffset->LowPart, 762 Length->HighPart, 763 Length->LowPart, 764 Key); 765 ToFind.Exclusive.FileLock.StartingByte = *FileOffset; 766 ToFind.Exclusive.FileLock.EndingByte.QuadPart = 767 FileOffset->QuadPart + Length->QuadPart; 768 if (!FileLock->LockInformation) return TRUE; 769 Found = RtlLookupElementGenericTable 770 (FileLock->LockInformation, 771 &ToFind); 772 if (!Found || !Found->Exclusive.FileLock.ExclusiveLock) return TRUE; 773 return Found->Exclusive.FileLock.Key == Key && 774 Found->Exclusive.FileLock.ProcessId == EProcess; 775 } 776 777 /* 778 * @implemented 779 */ 780 BOOLEAN 781 NTAPI 782 FsRtlFastCheckLockForWrite(IN PFILE_LOCK FileLock, 783 IN PLARGE_INTEGER FileOffset, 784 IN PLARGE_INTEGER Length, 785 IN ULONG Key, 786 IN PFILE_OBJECT FileObject, 787 IN PVOID Process) 788 { 789 BOOLEAN Result; 790 PEPROCESS EProcess = Process; 791 COMBINED_LOCK_ELEMENT ToFind; 792 PCOMBINED_LOCK_ELEMENT Found; 793 DPRINT("FsRtlFastCheckLockForWrite(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n", 794 &FileObject->FileName, 795 FileOffset->HighPart, 796 FileOffset->LowPart, 797 Length->HighPart, 798 Length->LowPart, 799 Key); 800 ToFind.Exclusive.FileLock.StartingByte = *FileOffset; 801 ToFind.Exclusive.FileLock.EndingByte.QuadPart = 802 FileOffset->QuadPart + Length->QuadPart; 803 if (!FileLock->LockInformation) { 804 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject->FileName); 805 return TRUE; 806 } 807 Found = RtlLookupElementGenericTable 808 (FileLock->LockInformation, 809 &ToFind); 810 if (!Found) { 811 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject->FileName); 812 return TRUE; 813 } 814 Result = Found->Exclusive.FileLock.Key == Key && 815 Found->Exclusive.FileLock.ProcessId == EProcess; 816 DPRINT("CheckForWrite(%wZ) => %s\n", &FileObject->FileName, Result ? "TRUE" : "FALSE"); 817 return Result; 818 } 819 820 /* 821 * @implemented 822 */ 823 NTSTATUS 824 NTAPI 825 FsRtlFastUnlockSingle(IN PFILE_LOCK FileLock, 826 IN PFILE_OBJECT FileObject, 827 IN PLARGE_INTEGER FileOffset, 828 IN PLARGE_INTEGER Length, 829 IN PEPROCESS Process, 830 IN ULONG Key, 831 IN PVOID Context OPTIONAL, 832 IN BOOLEAN AlreadySynchronized) 833 { 834 BOOLEAN FoundShared = FALSE; 835 PLIST_ENTRY SharedEntry; 836 PLOCK_SHARED_RANGE SharedRange = NULL; 837 COMBINED_LOCK_ELEMENT Find; 838 PCOMBINED_LOCK_ELEMENT Entry; 839 PIRP NextMatchingLockIrp; 840 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation; 841 DPRINT("FsRtlFastUnlockSingle(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x)\n", 842 &FileObject->FileName, 843 FileOffset->HighPart, 844 FileOffset->LowPart, 845 (int)FileOffset->QuadPart, 846 Length->HighPart, 847 Length->LowPart, 848 (int)Length->QuadPart, 849 Key); 850 // The region to unlock must correspond exactly to a previously locked region 851 // -- msdn 852 // But Windows 2003 doesn't assert on it and simply ignores that parameter 853 // ASSERT(AlreadySynchronized); 854 Find.Exclusive.FileLock.StartingByte = *FileOffset; 855 Find.Exclusive.FileLock.EndingByte.QuadPart = 856 FileOffset->QuadPart + Length->QuadPart; 857 if (!InternalInfo) { 858 DPRINT("File not previously locked (ever)\n"); 859 return STATUS_RANGE_NOT_LOCKED; 860 } 861 Entry = RtlLookupElementGenericTable(&InternalInfo->RangeTable, &Find); 862 if (!Entry) { 863 DPRINT("Range not locked %wZ\n", &FileObject->FileName); 864 return STATUS_RANGE_NOT_LOCKED; 865 } 866 867 DPRINT("Found lock entry: Exclusive %u %08x%08x:%08x%08x %wZ\n", 868 Entry->Exclusive.FileLock.ExclusiveLock, 869 Entry->Exclusive.FileLock.StartingByte.HighPart, 870 Entry->Exclusive.FileLock.StartingByte.LowPart, 871 Entry->Exclusive.FileLock.EndingByte.HighPart, 872 Entry->Exclusive.FileLock.EndingByte.LowPart, 873 &FileObject->FileName); 874 875 if (Entry->Exclusive.FileLock.ExclusiveLock) 876 { 877 if (Entry->Exclusive.FileLock.Key != Key || 878 Entry->Exclusive.FileLock.ProcessId != Process || 879 Entry->Exclusive.FileLock.StartingByte.QuadPart != FileOffset->QuadPart || 880 Entry->Exclusive.FileLock.EndingByte.QuadPart != 881 FileOffset->QuadPart + Length->QuadPart) 882 { 883 DPRINT("Range not locked %wZ\n", &FileObject->FileName); 884 return STATUS_RANGE_NOT_LOCKED; 885 } 886 RtlCopyMemory(&Find, Entry, sizeof(Find)); 887 // Remove the old exclusive lock region 888 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry); 889 } 890 else 891 { 892 DPRINT("Shared lock %wZ Start %08x%08x End %08x%08x\n", 893 &FileObject->FileName, 894 Entry->Exclusive.FileLock.StartingByte.HighPart, 895 Entry->Exclusive.FileLock.StartingByte.LowPart, 896 Entry->Exclusive.FileLock.EndingByte.HighPart, 897 Entry->Exclusive.FileLock.EndingByte.LowPart); 898 for (SharedEntry = InternalInfo->SharedLocks.Flink; 899 SharedEntry != &InternalInfo->SharedLocks; 900 SharedEntry = SharedEntry->Flink) 901 { 902 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry); 903 if (SharedRange->Start.QuadPart == FileOffset->QuadPart && 904 SharedRange->End.QuadPart == FileOffset->QuadPart + Length->QuadPart && 905 SharedRange->Key == Key && 906 SharedRange->ProcessId == Process) 907 { 908 FoundShared = TRUE; 909 DPRINT("Found shared element to delete %wZ Start %08x%08x End %08x%08x Key %x\n", 910 &FileObject->FileName, 911 SharedRange->Start.HighPart, 912 SharedRange->Start.LowPart, 913 SharedRange->End.HighPart, 914 SharedRange->End.LowPart, 915 SharedRange->Key); 916 break; 917 } 918 } 919 if (FoundShared) 920 { 921 /* Remove the found range from the shared range lists */ 922 RemoveEntryList(&SharedRange->Entry); 923 ExFreePoolWithTag(SharedRange, TAG_RANGE); 924 /* We need to rebuild the list of shared ranges. */ 925 DPRINT("Removing the lock entry %wZ (%08x%08x:%08x%08x)\n", 926 &FileObject->FileName, 927 Entry->Exclusive.FileLock.StartingByte.HighPart, 928 Entry->Exclusive.FileLock.StartingByte.LowPart, 929 Entry->Exclusive.FileLock.EndingByte.HighPart, 930 Entry->Exclusive.FileLock.EndingByte.LowPart); 931 932 /* Remember what was in there and remove it from the table */ 933 Find = *Entry; 934 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, &Find); 935 /* Put shared locks back in place */ 936 for (SharedEntry = InternalInfo->SharedLocks.Flink; 937 SharedEntry != &InternalInfo->SharedLocks; 938 SharedEntry = SharedEntry->Flink) 939 { 940 COMBINED_LOCK_ELEMENT LockElement; 941 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry); 942 LockElement.Exclusive.FileLock.FileObject = FileObject; 943 LockElement.Exclusive.FileLock.StartingByte = SharedRange->Start; 944 LockElement.Exclusive.FileLock.EndingByte = SharedRange->End; 945 LockElement.Exclusive.FileLock.ProcessId = SharedRange->ProcessId; 946 LockElement.Exclusive.FileLock.Key = SharedRange->Key; 947 LockElement.Exclusive.FileLock.ExclusiveLock = FALSE; 948 949 if (LockCompare(&InternalInfo->RangeTable, &Find, &LockElement) != GenericEqual) 950 { 951 DPRINT("Skipping range %08x%08x:%08x%08x\n", 952 LockElement.Exclusive.FileLock.StartingByte.HighPart, 953 LockElement.Exclusive.FileLock.StartingByte.LowPart, 954 LockElement.Exclusive.FileLock.EndingByte.HighPart, 955 LockElement.Exclusive.FileLock.EndingByte.LowPart); 956 continue; 957 } 958 DPRINT("Re-creating range %08x%08x:%08x%08x\n", 959 LockElement.Exclusive.FileLock.StartingByte.HighPart, 960 LockElement.Exclusive.FileLock.StartingByte.LowPart, 961 LockElement.Exclusive.FileLock.EndingByte.HighPart, 962 LockElement.Exclusive.FileLock.EndingByte.LowPart); 963 FsRtlpRebuildSharedLockRange(FileLock, InternalInfo, &LockElement); 964 } 965 } 966 else 967 { 968 return STATUS_RANGE_NOT_LOCKED; 969 } 970 } 971 972 #ifndef NDEBUG 973 DPRINT("Lock still has:\n"); 974 for (SharedEntry = InternalInfo->SharedLocks.Flink; 975 SharedEntry != &InternalInfo->SharedLocks; 976 SharedEntry = SharedEntry->Flink) 977 { 978 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry); 979 DPRINT("Shared element %wZ Offset %08x%08x Length %08x%08x Key %x\n", 980 &FileObject->FileName, 981 SharedRange->Start.HighPart, 982 SharedRange->Start.LowPart, 983 SharedRange->End.HighPart, 984 SharedRange->End.LowPart, 985 SharedRange->Key); 986 } 987 #endif 988 989 // this is definitely the thing we want 990 InternalInfo->Generation++; 991 while ((NextMatchingLockIrp = IoCsqRemoveNextIrp(&InternalInfo->Csq, &Find))) 992 { 993 NTSTATUS Status; 994 if (NextMatchingLockIrp->IoStatus.Information == InternalInfo->Generation) 995 { 996 // We've already looked at this one, meaning that we looped. 997 // Put it back and exit. 998 IoCsqInsertIrpEx 999 (&InternalInfo->Csq, 1000 NextMatchingLockIrp, 1001 NULL, 1002 NULL); 1003 break; 1004 } 1005 // Got a new lock irp... try to do the new lock operation 1006 // Note that we pick an operation that would succeed at the time 1007 // we looked, but can't guarantee that it won't just be re-queued 1008 // because somebody else snatched part of the range in a new thread. 1009 DPRINT("Locking another IRP %p for %p %wZ\n", 1010 NextMatchingLockIrp, FileLock, &FileObject->FileName); 1011 Status = FsRtlProcessFileLock(InternalInfo->BelongsTo, NextMatchingLockIrp, NULL); 1012 if (!NT_SUCCESS(Status)) 1013 return Status; 1014 } 1015 1016 DPRINT("Success %wZ\n", &FileObject->FileName); 1017 return STATUS_SUCCESS; 1018 } 1019 1020 /* 1021 * @implemented 1022 */ 1023 NTSTATUS 1024 NTAPI 1025 FsRtlFastUnlockAll(IN PFILE_LOCK FileLock, 1026 IN PFILE_OBJECT FileObject, 1027 IN PEPROCESS Process, 1028 IN PVOID Context OPTIONAL) 1029 { 1030 PLIST_ENTRY ListEntry; 1031 PCOMBINED_LOCK_ELEMENT Entry; 1032 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation; 1033 DPRINT("FsRtlFastUnlockAll(%wZ)\n", &FileObject->FileName); 1034 // XXX Synchronize somehow 1035 if (!FileLock->LockInformation) { 1036 DPRINT("Not locked %wZ\n", &FileObject->FileName); 1037 return STATUS_RANGE_NOT_LOCKED; // no locks 1038 } 1039 for (ListEntry = InternalInfo->SharedLocks.Flink; 1040 ListEntry != &InternalInfo->SharedLocks;) 1041 { 1042 LARGE_INTEGER Length; 1043 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry); 1044 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart; 1045 ListEntry = ListEntry->Flink; 1046 if (Range->ProcessId != Process) 1047 continue; 1048 FsRtlFastUnlockSingle 1049 (FileLock, 1050 FileObject, 1051 &Range->Start, 1052 &Length, 1053 Range->ProcessId, 1054 Range->Key, 1055 Context, 1056 TRUE); 1057 } 1058 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE); 1059 Entry; 1060 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE)) 1061 { 1062 LARGE_INTEGER Length; 1063 // We'll take the first one to be the list head, and free the others first... 1064 Length.QuadPart = 1065 Entry->Exclusive.FileLock.EndingByte.QuadPart - 1066 Entry->Exclusive.FileLock.StartingByte.QuadPart; 1067 FsRtlFastUnlockSingle 1068 (FileLock, 1069 Entry->Exclusive.FileLock.FileObject, 1070 &Entry->Exclusive.FileLock.StartingByte, 1071 &Length, 1072 Entry->Exclusive.FileLock.ProcessId, 1073 Entry->Exclusive.FileLock.Key, 1074 Context, 1075 TRUE); 1076 } 1077 DPRINT("Done %wZ\n", &FileObject->FileName); 1078 return STATUS_SUCCESS; 1079 } 1080 1081 /* 1082 * @implemented 1083 */ 1084 NTSTATUS 1085 NTAPI 1086 FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock, 1087 IN PFILE_OBJECT FileObject, 1088 IN PEPROCESS Process, 1089 IN ULONG Key, 1090 IN PVOID Context OPTIONAL) 1091 { 1092 PLIST_ENTRY ListEntry; 1093 PCOMBINED_LOCK_ELEMENT Entry; 1094 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation; 1095 1096 DPRINT("FsRtlFastUnlockAllByKey(%wZ,Key %x)\n", &FileObject->FileName, Key); 1097 1098 // XXX Synchronize somehow 1099 if (!FileLock->LockInformation) return STATUS_RANGE_NOT_LOCKED; // no locks 1100 for (ListEntry = InternalInfo->SharedLocks.Flink; 1101 ListEntry != &InternalInfo->SharedLocks;) 1102 { 1103 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry); 1104 LARGE_INTEGER Length; 1105 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart; 1106 ListEntry = ListEntry->Flink; 1107 if (Range->ProcessId != Process || 1108 Range->Key != Key) 1109 continue; 1110 FsRtlFastUnlockSingle 1111 (FileLock, 1112 FileObject, 1113 &Range->Start, 1114 &Length, 1115 Range->ProcessId, 1116 Range->Key, 1117 Context, 1118 TRUE); 1119 } 1120 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE); 1121 Entry; 1122 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE)) 1123 { 1124 LARGE_INTEGER Length; 1125 // We'll take the first one to be the list head, and free the others first... 1126 Length.QuadPart = 1127 Entry->Exclusive.FileLock.EndingByte.QuadPart - 1128 Entry->Exclusive.FileLock.StartingByte.QuadPart; 1129 if (Entry->Exclusive.FileLock.Key == Key && 1130 Entry->Exclusive.FileLock.ProcessId == Process) 1131 { 1132 FsRtlFastUnlockSingle 1133 (FileLock, 1134 Entry->Exclusive.FileLock.FileObject, 1135 &Entry->Exclusive.FileLock.StartingByte, 1136 &Length, 1137 Entry->Exclusive.FileLock.ProcessId, 1138 Entry->Exclusive.FileLock.Key, 1139 Context, 1140 TRUE); 1141 } 1142 } 1143 1144 return STATUS_SUCCESS; 1145 } 1146 1147 /* 1148 * @implemented 1149 */ 1150 NTSTATUS 1151 NTAPI 1152 FsRtlProcessFileLock(IN PFILE_LOCK FileLock, 1153 IN PIRP Irp, 1154 IN PVOID Context OPTIONAL) 1155 { 1156 PIO_STACK_LOCATION IoStackLocation; 1157 NTSTATUS Status; 1158 IO_STATUS_BLOCK IoStatusBlock; 1159 1160 /* Get the I/O Stack location */ 1161 IoStackLocation = IoGetCurrentIrpStackLocation(Irp); 1162 ASSERT(IoStackLocation->MajorFunction == IRP_MJ_LOCK_CONTROL); 1163 1164 /* Clear the I/O status block and check what function this is */ 1165 IoStatusBlock.Information = 0; 1166 1167 DPRINT("FsRtlProcessFileLock(%wZ, MinorFunction %x)\n", 1168 &IoStackLocation->FileObject->FileName, 1169 IoStackLocation->MinorFunction); 1170 1171 switch(IoStackLocation->MinorFunction) 1172 { 1173 /* A lock */ 1174 case IRP_MN_LOCK: 1175 { 1176 /* Call the private lock routine */ 1177 BOOLEAN Result = FsRtlPrivateLock(FileLock, 1178 IoStackLocation->FileObject, 1179 &IoStackLocation->Parameters.LockControl.ByteOffset, 1180 IoStackLocation->Parameters.LockControl.Length, 1181 IoGetRequestorProcess(Irp), 1182 IoStackLocation->Parameters.LockControl.Key, 1183 IoStackLocation->Flags & SL_FAIL_IMMEDIATELY, 1184 IoStackLocation->Flags & SL_EXCLUSIVE_LOCK, 1185 &IoStatusBlock, 1186 Irp, 1187 Context, 1188 FALSE); 1189 /* FsRtlPrivateLock has _Must_inspect_result_. Just check this is consistent on debug builds */ 1190 NT_ASSERT(Result == NT_SUCCESS(IoStatusBlock.Status)); 1191 (void)Result; 1192 return IoStatusBlock.Status; 1193 } 1194 /* A single unlock */ 1195 case IRP_MN_UNLOCK_SINGLE: 1196 1197 /* Call fast unlock */ 1198 IoStatusBlock.Status = 1199 FsRtlFastUnlockSingle(FileLock, 1200 IoStackLocation->FileObject, 1201 &IoStackLocation->Parameters.LockControl. 1202 ByteOffset, 1203 IoStackLocation->Parameters.LockControl. 1204 Length, 1205 IoGetRequestorProcess(Irp), 1206 IoStackLocation->Parameters.LockControl. 1207 Key, 1208 Context, 1209 FALSE); 1210 break; 1211 1212 /* Total unlock */ 1213 case IRP_MN_UNLOCK_ALL: 1214 1215 /* Do a fast unlock */ 1216 IoStatusBlock.Status = FsRtlFastUnlockAll(FileLock, 1217 IoStackLocation-> 1218 FileObject, 1219 IoGetRequestorProcess(Irp), 1220 Context); 1221 break; 1222 1223 /* Unlock by key */ 1224 case IRP_MN_UNLOCK_ALL_BY_KEY: 1225 1226 /* Do it */ 1227 IoStatusBlock.Status = 1228 FsRtlFastUnlockAllByKey(FileLock, 1229 IoStackLocation->FileObject, 1230 IoGetRequestorProcess(Irp), 1231 IoStackLocation->Parameters. 1232 LockControl.Key, 1233 Context); 1234 break; 1235 1236 /* Invalid request */ 1237 default: 1238 1239 /* Complete it */ 1240 FsRtlCompleteRequest(Irp, STATUS_INVALID_DEVICE_REQUEST); 1241 IoStatusBlock.Status = STATUS_INVALID_DEVICE_REQUEST; 1242 return STATUS_INVALID_DEVICE_REQUEST; 1243 } 1244 1245 /* Return the status */ 1246 DPRINT("Lock IRP %p %x\n", Irp, IoStatusBlock.Status); 1247 FsRtlCompleteLockIrpReal 1248 (FileLock->CompleteLockIrpRoutine, 1249 Context, 1250 Irp, 1251 IoStatusBlock.Status, 1252 &Status, 1253 NULL); 1254 return IoStatusBlock.Status; 1255 } 1256 1257 /* 1258 * @implemented 1259 */ 1260 VOID 1261 NTAPI 1262 FsRtlInitializeFileLock (IN PFILE_LOCK FileLock, 1263 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL, 1264 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL) 1265 { 1266 /* Setup the lock */ 1267 RtlZeroMemory(FileLock, sizeof(*FileLock)); 1268 FileLock->FastIoIsQuestionable = FALSE; 1269 FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine; 1270 FileLock->UnlockRoutine = UnlockRoutine; 1271 FileLock->LockInformation = NULL; 1272 } 1273 1274 /* 1275 * @implemented 1276 */ 1277 VOID 1278 NTAPI 1279 FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock) 1280 { 1281 if (FileLock->LockInformation) 1282 { 1283 PIRP Irp; 1284 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation; 1285 PCOMBINED_LOCK_ELEMENT Entry; 1286 PLIST_ENTRY SharedEntry; 1287 PLOCK_SHARED_RANGE SharedRange; 1288 // MSDN: this completes any remaining lock IRPs 1289 for (SharedEntry = InternalInfo->SharedLocks.Flink; 1290 SharedEntry != &InternalInfo->SharedLocks;) 1291 { 1292 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry); 1293 SharedEntry = SharedEntry->Flink; 1294 RemoveEntryList(&SharedRange->Entry); 1295 ExFreePoolWithTag(SharedRange, TAG_RANGE); 1296 } 1297 while ((Entry = RtlGetElementGenericTable(&InternalInfo->RangeTable, 0)) != NULL) 1298 { 1299 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry); 1300 } 1301 while ((Irp = IoCsqRemoveNextIrp(&InternalInfo->Csq, NULL)) != NULL) 1302 { 1303 NTSTATUS Status = FsRtlProcessFileLock(FileLock, Irp, NULL); 1304 /* FsRtlProcessFileLock has _Must_inspect_result_ */ 1305 NT_ASSERT(NT_SUCCESS(Status)); 1306 (void)Status; 1307 } 1308 ExFreePoolWithTag(InternalInfo, TAG_FLOCK); 1309 FileLock->LockInformation = NULL; 1310 } 1311 } 1312 1313 /* 1314 * @implemented 1315 */ 1316 PFILE_LOCK 1317 NTAPI 1318 FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL, 1319 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL) 1320 { 1321 PFILE_LOCK FileLock; 1322 1323 /* Try to allocate it */ 1324 FileLock = ExAllocateFromPagedLookasideList(&FsRtlFileLockLookasideList); 1325 if (FileLock) 1326 { 1327 /* Initialize it */ 1328 FsRtlInitializeFileLock(FileLock, 1329 CompleteLockIrpRoutine, 1330 UnlockRoutine); 1331 } 1332 1333 /* Return the lock */ 1334 return FileLock; 1335 } 1336 1337 /* 1338 * @implemented 1339 */ 1340 VOID 1341 NTAPI 1342 FsRtlFreeFileLock(IN PFILE_LOCK FileLock) 1343 { 1344 /* Uninitialize and free the lock */ 1345 FsRtlUninitializeFileLock(FileLock); 1346 ExFreeToPagedLookasideList(&FsRtlFileLockLookasideList, FileLock); 1347 } 1348