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)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 PAGED_LOOKASIDE_LIST FsRtlFileLockLookasideList
;
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.
22 typedef union _COMBINED_LOCK_ELEMENT
27 FILE_SHARED_LOCK_ENTRY Shared
;
29 FILE_EXCLUSIVE_LOCK_ENTRY Exclusive
;
31 COMBINED_LOCK_ELEMENT
, *PCOMBINED_LOCK_ELEMENT
;
33 typedef struct _LOCK_INFORMATION
35 RTL_GENERIC_TABLE RangeTable
;
40 LIST_ENTRY SharedLocks
;
43 LOCK_INFORMATION
, *PLOCK_INFORMATION
;
45 typedef struct _LOCK_SHARED_RANGE
48 LARGE_INTEGER Start
, End
;
52 LOCK_SHARED_RANGE
, *PLOCK_SHARED_RANGE
;
54 #define TAG_TABLE 'LTAB'
55 #define TAG_RANGE 'FSRA'
56 #define TAG_FLOCK 'FLCK'
58 /* PRIVATE FUNCTIONS *********************************************************/
62 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine
,
66 OUT PNTSTATUS NewStatus
,
67 IN PFILE_OBJECT FileObject OPTIONAL
);
69 /* Generic table methods */
71 static PVOID NTAPI
LockAllocate(PRTL_GENERIC_TABLE Table
, CLONG Bytes
)
74 Result
= ExAllocatePoolWithTag(NonPagedPool
, Bytes
, TAG_TABLE
);
75 DPRINT("LockAllocate(%lu) => %p\n", Bytes
, Result
);
79 static VOID NTAPI
LockFree(PRTL_GENERIC_TABLE Table
, PVOID Buffer
)
81 DPRINT("LockFree(%p)\n", Buffer
);
82 ExFreePoolWithTag(Buffer
, TAG_TABLE
);
85 static RTL_GENERIC_COMPARE_RESULTS NTAPI LockCompare
86 (PRTL_GENERIC_TABLE Table
, PVOID PtrA
, PVOID PtrB
)
88 PCOMBINED_LOCK_ELEMENT A
= PtrA
, B
= PtrB
;
89 RTL_GENERIC_COMPARE_RESULTS Result
;
91 DPRINT("Starting to compare element %x to element %x\n", PtrA
, PtrB
);
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
)))
103 /* Otherwise, key on the starting byte */
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
:
111 DPRINT("Compare(%x:%x) %x-%x to %x-%x => %d\n",
113 A
->Exclusive
.FileLock
.StartingByte
.LowPart
,
114 A
->Exclusive
.FileLock
.EndingByte
.LowPart
,
115 B
->Exclusive
.FileLock
.StartingByte
.LowPart
,
116 B
->Exclusive
.FileLock
.EndingByte
.LowPart
,
124 static NTSTATUS NTAPI LockInsertIrpEx
129 PLOCK_INFORMATION LockInfo
= CONTAINING_RECORD(Csq
, LOCK_INFORMATION
, Csq
);
130 InsertTailList(&LockInfo
->CsqList
, &Irp
->Tail
.Overlay
.ListEntry
);
131 return STATUS_SUCCESS
;
134 static VOID NTAPI
LockRemoveIrp(PIO_CSQ Csq
, PIRP Irp
)
136 RemoveEntryList(&Irp
->Tail
.Overlay
.ListEntry
);
139 static PIRP NTAPI
LockPeekNextIrp(PIO_CSQ Csq
, PIRP Irp
, PVOID PeekContext
)
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
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
);
151 Following
= LockInfo
->CsqList
.Flink
;
154 Following
= Irp
->Tail
.Overlay
.ListEntry
.Flink
;
156 DPRINT("ListEntry %p Head %p\n", Following
, &LockInfo
->CsqList
);
158 Following
!= &LockInfo
->CsqList
;
159 Following
= Following
->Flink
)
161 PIO_STACK_LOCATION IoStack
;
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 */
174 Matching
= LockCompare
175 (&LockInfo
->RangeTable
, &LockElement
, WhereUnlock
) != GenericEqual
;
177 /* Else get any completable IRP */
184 // This IRP is fine...
185 DPRINT("Returning the IRP %p\n", Irp
);
189 DPRINT("Return NULL\n");
194 LockAcquireQueueLock(PIO_CSQ Csq
, PKIRQL Irql
)
196 PLOCK_INFORMATION LockInfo
= CONTAINING_RECORD(Csq
, LOCK_INFORMATION
, Csq
);
197 KeAcquireSpinLock(&LockInfo
->CsqLock
, Irql
);
201 LockReleaseQueueLock(PIO_CSQ Csq
, KIRQL Irql
)
203 PLOCK_INFORMATION LockInfo
= CONTAINING_RECORD(Csq
, LOCK_INFORMATION
, Csq
);
204 KeReleaseSpinLock(&LockInfo
->CsqLock
, Irql
);
208 LockCompleteCanceledIrp(PIO_CSQ Csq
, PIRP Irp
)
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
,
224 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine
,
228 OUT PNTSTATUS NewStatus
,
229 IN PFILE_OBJECT FileObject OPTIONAL
)
231 /* Check if we have a complete routine */
232 Irp
->IoStatus
.Information
= 0;
235 /* Check if we have a file object */
236 if (FileObject
) FileObject
->LastLock
= NULL
;
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
);
245 /* Otherwise do a normal I/O complete request */
246 DPRINT("Completing IRP %p Status %x\n", Irp
, Status
);
247 FsRtlCompleteRequest(Irp
, Status
);
252 /* PUBLIC FUNCTIONS **********************************************************/
259 FsRtlGetNextFileLock(IN PFILE_LOCK FileLock
,
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
;
271 FsRtlpExpandLockElement
272 (PCOMBINED_LOCK_ELEMENT ToExpand
,
273 PCOMBINED_LOCK_ELEMENT Conflict
)
275 if (ToExpand
->Exclusive
.FileLock
.StartingByte
.QuadPart
>
276 Conflict
->Exclusive
.FileLock
.StartingByte
.QuadPart
)
278 ToExpand
->Exclusive
.FileLock
.StartingByte
=
279 Conflict
->Exclusive
.FileLock
.StartingByte
;
281 if (ToExpand
->Exclusive
.FileLock
.EndingByte
.QuadPart
<
282 Conflict
->Exclusive
.FileLock
.EndingByte
.QuadPart
)
284 ToExpand
->Exclusive
.FileLock
.EndingByte
=
285 Conflict
->Exclusive
.FileLock
.EndingByte
;
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
293 FsRtlpRebuildSharedLockRange
294 (PFILE_LOCK FileLock
,
295 PLOCK_INFORMATION LockInfo
,
296 PCOMBINED_LOCK_ELEMENT Conflict
)
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.
302 BOOLEAN InsertedNew
= FALSE
, RemovedOld
;
303 COMBINED_LOCK_ELEMENT NewElement
= *Conflict
;
304 PCOMBINED_LOCK_ELEMENT Entry
;
305 while ((Entry
= RtlLookupElementGenericTable
306 (FileLock
->LockInformation
, &NewElement
)))
308 FsRtlpExpandLockElement(&NewElement
, Entry
);
309 RemovedOld
= RtlDeleteElementGenericTable
310 (&LockInfo
->RangeTable
,
314 Conflict
= RtlInsertElementGenericTable
315 (&LockInfo
->RangeTable
,
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
,
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
)
342 COMBINED_LOCK_ELEMENT ToInsert
;
343 PCOMBINED_LOCK_ELEMENT Conflict
;
344 PLOCK_INFORMATION LockInfo
;
345 PLOCK_SHARED_RANGE NewSharedRange
;
347 ULARGE_INTEGER UnsignedStart
;
348 ULARGE_INTEGER UnsignedEnd
;
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
,
354 (int)FileOffset
->QuadPart
,
357 (int)Length
->QuadPart
,
362 UnsignedStart
.QuadPart
= FileOffset
->QuadPart
;
363 UnsignedEnd
.QuadPart
= FileOffset
->QuadPart
+ Length
->QuadPart
;
365 if (UnsignedEnd
.QuadPart
< UnsignedStart
.QuadPart
)
367 DPRINT("File offset out of range\n");
368 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
371 DPRINT("Complete lock %p Status %x\n", Irp
, IoStatus
->Status
);
372 FsRtlCompleteLockIrpReal
373 (FileLock
->CompleteLockIrpRoutine
,
383 /* Initialize the lock, if necessary */
384 if (!FileLock
->LockInformation
)
386 LockInfo
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(LOCK_INFORMATION
), TAG_FLOCK
);
389 IoStatus
->Status
= STATUS_NO_MEMORY
;
392 FileLock
->LockInformation
= LockInfo
;
394 LockInfo
->BelongsTo
= FileLock
;
395 InitializeListHead(&LockInfo
->SharedLocks
);
397 RtlInitializeGenericTable
398 (&LockInfo
->RangeTable
,
404 KeInitializeSpinLock(&LockInfo
->CsqLock
);
405 InitializeListHead(&LockInfo
->CsqList
);
412 LockAcquireQueueLock
,
413 LockReleaseQueueLock
,
414 LockCompleteCanceledIrp
);
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
->UniqueProcessId
;
422 ToInsert
.Exclusive
.FileLock
.Key
= Key
;
423 ToInsert
.Exclusive
.FileLock
.ExclusiveLock
= ExclusiveLock
;
425 Conflict
= RtlInsertElementGenericTable
426 (FileLock
->LockInformation
,
431 if (Conflict
&& !InsertedNew
)
433 if (Conflict
->Exclusive
.FileLock
.ExclusiveLock
|| ExclusiveLock
)
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
,
444 DPRINT("STATUS_FILE_LOCK_CONFLICT\n");
445 IoStatus
->Status
= STATUS_FILE_LOCK_CONFLICT
;
448 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n");
449 FsRtlCompleteLockIrpReal
450 (FileLock
->CompleteLockIrpRoutine
,
461 IoStatus
->Status
= STATUS_PENDING
;
464 Irp
->IoStatus
.Information
= LockInfo
->Generation
;
465 IoMarkIrpPending(Irp
);
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
++)
482 Conflict
= RtlGetElementGenericTable(&LockInfo
->RangeTable
, i
);
484 /* The first argument will be inserted as a shared range */
485 if (Conflict
&& (LockCompare(&LockInfo
->RangeTable
, Conflict
, &ToInsert
) == GenericEqual
))
487 if (Conflict
->Exclusive
.FileLock
.ExclusiveLock
)
489 /* Found an exclusive match */
492 IoStatus
->Status
= STATUS_FILE_LOCK_CONFLICT
;
493 DPRINT("STATUS_FILE_LOCK_CONFLICT\n");
496 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n");
497 FsRtlCompleteLockIrpReal
498 (FileLock
->CompleteLockIrpRoutine
,
508 IoStatus
->Status
= STATUS_PENDING
;
511 IoMarkIrpPending(Irp
);
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
,
535 IoStatus
->Status
= STATUS_NO_MEMORY
;
538 FsRtlCompleteLockIrpReal
539 (FileLock
->CompleteLockIrpRoutine
,
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
552 DPRINT("Adding shared lock %wZ\n", &FileObject
->FileName
);
554 ExAllocatePoolWithTag(NonPagedPool
, sizeof(*NewSharedRange
), TAG_RANGE
);
557 IoStatus
->Status
= STATUS_NO_MEMORY
;
560 FsRtlCompleteLockIrpReal
561 (FileLock
->CompleteLockIrpRoutine
,
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
);
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
;
586 FsRtlCompleteLockIrpReal
587 (FileLock
->CompleteLockIrpRoutine
,
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
;
604 FsRtlCompleteLockIrpReal
605 (FileLock
->CompleteLockIrpRoutine
,
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
);
626 ExAllocatePoolWithTag(NonPagedPool
, sizeof(*NewSharedRange
), TAG_RANGE
);
629 IoStatus
->Status
= STATUS_NO_MEMORY
;
632 FsRtlCompleteLockIrpReal
633 (FileLock
->CompleteLockIrpRoutine
,
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
->UniqueProcessId
;
647 InsertTailList(&LockInfo
->SharedLocks
, &NewSharedRange
->Entry
);
650 /* Assume all is cool, and lock is set */
651 IoStatus
->Status
= STATUS_SUCCESS
;
655 /* Complete the request */
656 FsRtlCompleteLockIrpReal(FileLock
->CompleteLockIrpRoutine
,
663 /* Update the status */
664 IoStatus
->Status
= Status
;
676 FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock
,
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
);
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
,
700 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack
->FileObject
->FileName
);
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");
714 FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock
,
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
);
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
,
739 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack
->FileObject
->FileName
);
742 Result
= Process
->UniqueProcessId
== Found
->Exclusive
.FileLock
.ProcessId
;
743 DPRINT("CheckLockForWriteAccess(%wZ) => %s\n", &IoStack
->FileObject
->FileName
, Result
? "TRUE" : "FALSE");
752 FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock
,
753 IN PLARGE_INTEGER FileOffset
,
754 IN PLARGE_INTEGER Length
,
756 IN PFILE_OBJECT FileObject
,
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
,
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
,
776 if (!Found
|| !Found
->Exclusive
.FileLock
.ExclusiveLock
) return TRUE
;
777 return Found
->Exclusive
.FileLock
.Key
== Key
&&
778 Found
->Exclusive
.FileLock
.ProcessId
== EProcess
->UniqueProcessId
;
786 FsRtlFastCheckLockForWrite(IN PFILE_LOCK FileLock
,
787 IN PLARGE_INTEGER FileOffset
,
788 IN PLARGE_INTEGER Length
,
790 IN PFILE_OBJECT FileObject
,
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
,
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
);
811 Found
= RtlLookupElementGenericTable
812 (FileLock
->LockInformation
,
815 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject
->FileName
);
818 Result
= Found
->Exclusive
.FileLock
.Key
== Key
&&
819 Found
->Exclusive
.FileLock
.ProcessId
== EProcess
->UniqueProcessId
;
820 DPRINT("CheckForWrite(%wZ) => %s\n", &FileObject
->FileName
, Result
? "TRUE" : "FALSE");
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
,
835 IN PVOID Context OPTIONAL
,
836 IN BOOLEAN AlreadySynchronized
)
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
,
849 (int)FileOffset
->QuadPart
,
852 (int)Length
->QuadPart
,
854 // The region to unlock must correspond exactly to a previously locked region
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
;
862 DPRINT("File not previously locked (ever)\n");
863 return STATUS_RANGE_NOT_LOCKED
;
865 Entry
= RtlLookupElementGenericTable(&InternalInfo
->RangeTable
, &Find
);
867 DPRINT("Range not locked %wZ\n", &FileObject
->FileName
);
868 return STATUS_RANGE_NOT_LOCKED
;
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
);
879 if (Entry
->Exclusive
.FileLock
.ExclusiveLock
)
881 if (Entry
->Exclusive
.FileLock
.Key
!= Key
||
882 Entry
->Exclusive
.FileLock
.ProcessId
!= Process
->UniqueProcessId
||
883 Entry
->Exclusive
.FileLock
.StartingByte
.QuadPart
!= FileOffset
->QuadPart
||
884 Entry
->Exclusive
.FileLock
.EndingByte
.QuadPart
!=
885 FileOffset
->QuadPart
+ Length
->QuadPart
)
887 DPRINT("Range not locked %wZ\n", &FileObject
->FileName
);
888 return STATUS_RANGE_NOT_LOCKED
;
890 RtlCopyMemory(&Find
, Entry
, sizeof(Find
));
891 // Remove the old exclusive lock region
892 RtlDeleteElementGenericTable(&InternalInfo
->RangeTable
, Entry
);
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
)
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
->UniqueProcessId
)
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
,
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
);
936 /* Remember what was in there and remove it from the table */
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
)
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
;
953 if (LockCompare(&InternalInfo
->RangeTable
, &Find
, &LockElement
) != GenericEqual
)
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
);
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
);
972 return STATUS_RANGE_NOT_LOCKED
;
977 DPRINT("Lock still has:\n");
978 for (SharedEntry
= InternalInfo
->SharedLocks
.Flink
;
979 SharedEntry
!= &InternalInfo
->SharedLocks
;
980 SharedEntry
= SharedEntry
->Flink
)
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
,
993 // this is definitely the thing we want
994 InternalInfo
->Generation
++;
995 while ((NextMatchingLockIrp
= IoCsqRemoveNextIrp(&InternalInfo
->Csq
, &Find
)))
997 if (NextMatchingLockIrp
->IoStatus
.Information
== InternalInfo
->Generation
)
999 // We've already looked at this one, meaning that we looped.
1000 // Put it back and exit.
1002 (&InternalInfo
->Csq
,
1003 NextMatchingLockIrp
,
1008 // Got a new lock irp... try to do the new lock operation
1009 // Note that we pick an operation that would succeed at the time
1010 // we looked, but can't guarantee that it won't just be re-queued
1011 // because somebody else snatched part of the range in a new thread.
1012 DPRINT("Locking another IRP %p for %p %wZ\n",
1013 &FileObject
->FileName
, FileLock
, NextMatchingLockIrp
);
1014 FsRtlProcessFileLock(InternalInfo
->BelongsTo
, NextMatchingLockIrp
, NULL
);
1017 DPRINT("Success %wZ\n", &FileObject
->FileName
);
1018 return STATUS_SUCCESS
;
1026 FsRtlFastUnlockAll(IN PFILE_LOCK FileLock
,
1027 IN PFILE_OBJECT FileObject
,
1028 IN PEPROCESS Process
,
1029 IN PVOID Context OPTIONAL
)
1031 PLIST_ENTRY ListEntry
;
1032 PCOMBINED_LOCK_ELEMENT Entry
;
1033 PLOCK_INFORMATION InternalInfo
= FileLock
->LockInformation
;
1034 DPRINT("FsRtlFastUnlockAll(%wZ)\n", &FileObject
->FileName
);
1035 // XXX Synchronize somehow
1036 if (!FileLock
->LockInformation
) {
1037 DPRINT("Not locked %wZ\n", &FileObject
->FileName
);
1038 return STATUS_RANGE_NOT_LOCKED
; // no locks
1040 for (ListEntry
= InternalInfo
->SharedLocks
.Flink
;
1041 ListEntry
!= &InternalInfo
->SharedLocks
;)
1043 LARGE_INTEGER Length
;
1044 PLOCK_SHARED_RANGE Range
= CONTAINING_RECORD(ListEntry
, LOCK_SHARED_RANGE
, Entry
);
1045 Length
.QuadPart
= Range
->End
.QuadPart
- Range
->Start
.QuadPart
;
1046 ListEntry
= ListEntry
->Flink
;
1047 if (Range
->ProcessId
!= Process
->UniqueProcessId
)
1049 FsRtlFastUnlockSingle
1059 for (Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, TRUE
);
1061 Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, FALSE
))
1063 LARGE_INTEGER Length
;
1064 // We'll take the first one to be the list head, and free the others first...
1066 Entry
->Exclusive
.FileLock
.EndingByte
.QuadPart
-
1067 Entry
->Exclusive
.FileLock
.StartingByte
.QuadPart
;
1068 FsRtlFastUnlockSingle
1070 Entry
->Exclusive
.FileLock
.FileObject
,
1071 &Entry
->Exclusive
.FileLock
.StartingByte
,
1073 Entry
->Exclusive
.FileLock
.ProcessId
,
1074 Entry
->Exclusive
.FileLock
.Key
,
1078 DPRINT("Done %wZ\n", &FileObject
->FileName
);
1079 return STATUS_SUCCESS
;
1087 FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock
,
1088 IN PFILE_OBJECT FileObject
,
1089 IN PEPROCESS Process
,
1091 IN PVOID Context OPTIONAL
)
1093 PLIST_ENTRY ListEntry
;
1094 PCOMBINED_LOCK_ELEMENT Entry
;
1095 PLOCK_INFORMATION InternalInfo
= FileLock
->LockInformation
;
1097 DPRINT("FsRtlFastUnlockAllByKey(%wZ,Key %x)\n", &FileObject
->FileName
, Key
);
1099 // XXX Synchronize somehow
1100 if (!FileLock
->LockInformation
) return STATUS_RANGE_NOT_LOCKED
; // no locks
1101 for (ListEntry
= InternalInfo
->SharedLocks
.Flink
;
1102 ListEntry
!= &InternalInfo
->SharedLocks
;)
1104 PLOCK_SHARED_RANGE Range
= CONTAINING_RECORD(ListEntry
, LOCK_SHARED_RANGE
, Entry
);
1105 LARGE_INTEGER Length
;
1106 Length
.QuadPart
= Range
->End
.QuadPart
- Range
->Start
.QuadPart
;
1107 ListEntry
= ListEntry
->Flink
;
1108 if (Range
->ProcessId
!= Process
->UniqueProcessId
||
1111 FsRtlFastUnlockSingle
1121 for (Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, TRUE
);
1123 Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, FALSE
))
1125 LARGE_INTEGER Length
;
1126 // We'll take the first one to be the list head, and free the others first...
1128 Entry
->Exclusive
.FileLock
.EndingByte
.QuadPart
-
1129 Entry
->Exclusive
.FileLock
.StartingByte
.QuadPart
;
1130 if (Entry
->Exclusive
.FileLock
.Key
== Key
&&
1131 Entry
->Exclusive
.FileLock
.ProcessId
== Process
->UniqueProcessId
)
1133 FsRtlFastUnlockSingle
1135 Entry
->Exclusive
.FileLock
.FileObject
,
1136 &Entry
->Exclusive
.FileLock
.StartingByte
,
1138 Entry
->Exclusive
.FileLock
.ProcessId
,
1139 Entry
->Exclusive
.FileLock
.Key
,
1145 return STATUS_SUCCESS
;
1153 FsRtlProcessFileLock(IN PFILE_LOCK FileLock
,
1155 IN PVOID Context OPTIONAL
)
1157 PIO_STACK_LOCATION IoStackLocation
;
1159 IO_STATUS_BLOCK IoStatusBlock
;
1161 /* Get the I/O Stack location */
1162 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
1163 ASSERT(IoStackLocation
->MajorFunction
== IRP_MJ_LOCK_CONTROL
);
1165 /* Clear the I/O status block and check what function this is */
1166 IoStatusBlock
.Information
= 0;
1168 DPRINT("FsRtlProcessFileLock(%wZ, MinorFunction %x)\n",
1169 &IoStackLocation
->FileObject
->FileName
,
1170 IoStackLocation
->MinorFunction
);
1172 switch(IoStackLocation
->MinorFunction
)
1177 /* Call the private lock routine */
1178 FsRtlPrivateLock(FileLock
,
1179 IoStackLocation
->FileObject
,
1181 Parameters
.LockControl
.ByteOffset
,
1182 IoStackLocation
->Parameters
.LockControl
.Length
,
1183 IoGetRequestorProcess(Irp
),
1184 IoStackLocation
->Parameters
.LockControl
.Key
,
1185 IoStackLocation
->Flags
& SL_FAIL_IMMEDIATELY
,
1186 IoStackLocation
->Flags
& SL_EXCLUSIVE_LOCK
,
1191 return IoStatusBlock
.Status
;
1193 /* A single unlock */
1194 case IRP_MN_UNLOCK_SINGLE
:
1196 /* Call fast unlock */
1197 IoStatusBlock
.Status
=
1198 FsRtlFastUnlockSingle(FileLock
,
1199 IoStackLocation
->FileObject
,
1200 &IoStackLocation
->Parameters
.LockControl
.
1202 IoStackLocation
->Parameters
.LockControl
.
1204 IoGetRequestorProcess(Irp
),
1205 IoStackLocation
->Parameters
.LockControl
.
1212 case IRP_MN_UNLOCK_ALL
:
1214 /* Do a fast unlock */
1215 IoStatusBlock
.Status
= FsRtlFastUnlockAll(FileLock
,
1218 IoGetRequestorProcess(Irp
),
1223 case IRP_MN_UNLOCK_ALL_BY_KEY
:
1226 IoStatusBlock
.Status
=
1227 FsRtlFastUnlockAllByKey(FileLock
,
1228 IoStackLocation
->FileObject
,
1229 IoGetRequestorProcess(Irp
),
1230 IoStackLocation
->Parameters
.
1235 /* Invalid request */
1239 FsRtlCompleteRequest(Irp
, STATUS_INVALID_DEVICE_REQUEST
);
1240 IoStatusBlock
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
1241 return STATUS_INVALID_DEVICE_REQUEST
;
1244 /* Return the status */
1245 DPRINT("Lock IRP %p %x\n", Irp
, IoStatusBlock
.Status
);
1246 FsRtlCompleteLockIrpReal
1247 (FileLock
->CompleteLockIrpRoutine
,
1250 IoStatusBlock
.Status
,
1253 return IoStatusBlock
.Status
;
1261 FsRtlInitializeFileLock (IN PFILE_LOCK FileLock
,
1262 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL
,
1263 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
)
1265 /* Setup the lock */
1266 RtlZeroMemory(FileLock
, sizeof(*FileLock
));
1267 FileLock
->FastIoIsQuestionable
= FALSE
;
1268 FileLock
->CompleteLockIrpRoutine
= CompleteLockIrpRoutine
;
1269 FileLock
->UnlockRoutine
= UnlockRoutine
;
1270 FileLock
->LockInformation
= NULL
;
1278 FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock
)
1280 if (FileLock
->LockInformation
)
1283 PLOCK_INFORMATION InternalInfo
= FileLock
->LockInformation
;
1284 PCOMBINED_LOCK_ELEMENT Entry
;
1285 PLIST_ENTRY SharedEntry
;
1286 PLOCK_SHARED_RANGE SharedRange
;
1287 // MSDN: this completes any remaining lock IRPs
1288 for (SharedEntry
= InternalInfo
->SharedLocks
.Flink
;
1289 SharedEntry
!= &InternalInfo
->SharedLocks
;)
1291 SharedRange
= CONTAINING_RECORD(SharedEntry
, LOCK_SHARED_RANGE
, Entry
);
1292 SharedEntry
= SharedEntry
->Flink
;
1293 RemoveEntryList(&SharedRange
->Entry
);
1294 ExFreePoolWithTag(SharedRange
, TAG_RANGE
);
1296 while ((Entry
= RtlGetElementGenericTable(&InternalInfo
->RangeTable
, 0)) != NULL
)
1298 RtlDeleteElementGenericTable(&InternalInfo
->RangeTable
, Entry
);
1300 while ((Irp
= IoCsqRemoveNextIrp(&InternalInfo
->Csq
, NULL
)) != NULL
)
1302 FsRtlProcessFileLock(FileLock
, Irp
, NULL
);
1304 ExFreePoolWithTag(InternalInfo
, TAG_FLOCK
);
1305 FileLock
->LockInformation
= NULL
;
1314 FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL
,
1315 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
)
1317 PFILE_LOCK FileLock
;
1319 /* Try to allocate it */
1320 FileLock
= ExAllocateFromPagedLookasideList(&FsRtlFileLockLookasideList
);
1324 FsRtlInitializeFileLock(FileLock
,
1325 CompleteLockIrpRoutine
,
1329 /* Return the lock */
1338 FsRtlFreeFileLock(IN PFILE_LOCK FileLock
)
1340 /* Uninitialize and free the lock */
1341 FsRtlUninitializeFileLock(FileLock
);
1342 ExFreeToPagedLookasideList(&FsRtlFileLockLookasideList
, FileLock
);