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