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 /* PRIVATE FUNCTIONS *********************************************************/
58 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine
,
62 OUT PNTSTATUS NewStatus
,
63 IN PFILE_OBJECT FileObject OPTIONAL
);
65 /* Generic table methods */
67 static PVOID NTAPI
LockAllocate(PRTL_GENERIC_TABLE Table
, CLONG Bytes
)
70 Result
= ExAllocatePoolWithTag(NonPagedPool
, Bytes
, 'LTAB');
71 DPRINT("LockAllocate(%d) => %p\n", Bytes
, Result
);
75 static VOID NTAPI
LockFree(PRTL_GENERIC_TABLE Table
, PVOID Buffer
)
77 DPRINT("LockFree(%p)\n", Buffer
);
78 ExFreePoolWithTag(Buffer
, 'LTAB');
81 static RTL_GENERIC_COMPARE_RESULTS NTAPI LockCompare
82 (PRTL_GENERIC_TABLE Table
, PVOID PtrA
, PVOID PtrB
)
84 PCOMBINED_LOCK_ELEMENT A
= PtrA
, B
= PtrB
;
85 RTL_GENERIC_COMPARE_RESULTS Result
;
87 DPRINT("Starting to compare element %x to element %x\n", PtrA
, PtrB
);
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
)))
99 /* Otherwise, key on the starting byte */
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
:
107 DPRINT("Compare(%x:%x) %x-%x to %x-%x => %d\n",
109 A
->Exclusive
.FileLock
.StartingByte
.LowPart
,
110 A
->Exclusive
.FileLock
.EndingByte
.LowPart
,
111 B
->Exclusive
.FileLock
.StartingByte
.LowPart
,
112 B
->Exclusive
.FileLock
.EndingByte
.LowPart
,
120 static NTSTATUS NTAPI LockInsertIrpEx
125 PLOCK_INFORMATION LockInfo
= CONTAINING_RECORD(Csq
, LOCK_INFORMATION
, Csq
);
126 InsertTailList(&LockInfo
->CsqList
, &Irp
->Tail
.Overlay
.ListEntry
);
127 return STATUS_SUCCESS
;
130 static VOID NTAPI
LockRemoveIrp(PIO_CSQ Csq
, PIRP Irp
)
132 RemoveEntryList(&Irp
->Tail
.Overlay
.ListEntry
);
135 static PIRP NTAPI
LockPeekNextIrp(PIO_CSQ Csq
, PIRP Irp
, PVOID PeekContext
)
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
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
);
147 Following
= LockInfo
->CsqList
.Flink
;
150 Following
= Irp
->Tail
.Overlay
.ListEntry
.Flink
;
152 DPRINT("ListEntry %p Head %p\n", Following
, &LockInfo
->CsqList
);
154 Following
!= &LockInfo
->CsqList
;
155 Following
= Following
->Flink
)
157 PIO_STACK_LOCATION IoStack
;
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 */
170 Matching
= LockCompare
171 (&LockInfo
->RangeTable
, &LockElement
, WhereUnlock
) != GenericEqual
;
173 /* Else get any completable IRP */
180 // This IRP is fine...
181 DPRINT("Returning the IRP %p\n", Irp
);
185 DPRINT("Return NULL\n");
190 LockAcquireQueueLock(PIO_CSQ Csq
, PKIRQL Irql
)
192 PLOCK_INFORMATION LockInfo
= CONTAINING_RECORD(Csq
, LOCK_INFORMATION
, Csq
);
193 KeAcquireSpinLock(&LockInfo
->CsqLock
, Irql
);
197 LockReleaseQueueLock(PIO_CSQ Csq
, KIRQL Irql
)
199 PLOCK_INFORMATION LockInfo
= CONTAINING_RECORD(Csq
, LOCK_INFORMATION
, Csq
);
200 KeReleaseSpinLock(&LockInfo
->CsqLock
, Irql
);
204 LockCompleteCanceledIrp(PIO_CSQ Csq
, PIRP Irp
)
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
,
220 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine
,
224 OUT PNTSTATUS NewStatus
,
225 IN PFILE_OBJECT FileObject OPTIONAL
)
227 /* Check if we have a complete routine */
228 Irp
->IoStatus
.Information
= 0;
231 /* Check if we have a file object */
232 if (FileObject
) FileObject
->LastLock
= NULL
;
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
);
241 /* Otherwise do a normal I/O complete request */
242 DPRINT("Completing IRP %p Status %x\n", Irp
, Status
);
243 FsRtlCompleteRequest(Irp
, Status
);
248 /* PUBLIC FUNCTIONS **********************************************************/
255 FsRtlGetNextFileLock(IN PFILE_LOCK FileLock
,
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
;
267 FsRtlpExpandLockElement
268 (PCOMBINED_LOCK_ELEMENT ToExpand
,
269 PCOMBINED_LOCK_ELEMENT Conflict
)
271 if (ToExpand
->Exclusive
.FileLock
.StartingByte
.QuadPart
>
272 Conflict
->Exclusive
.FileLock
.StartingByte
.QuadPart
)
274 ToExpand
->Exclusive
.FileLock
.StartingByte
=
275 Conflict
->Exclusive
.FileLock
.StartingByte
;
277 if (ToExpand
->Exclusive
.FileLock
.EndingByte
.QuadPart
<
278 Conflict
->Exclusive
.FileLock
.EndingByte
.QuadPart
)
280 ToExpand
->Exclusive
.FileLock
.EndingByte
=
281 Conflict
->Exclusive
.FileLock
.EndingByte
;
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
289 FsRtlpRebuildSharedLockRange
290 (PFILE_LOCK FileLock
,
291 PLOCK_INFORMATION LockInfo
,
292 PCOMBINED_LOCK_ELEMENT Conflict
)
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.
298 BOOLEAN InsertedNew
= FALSE
, RemovedOld
;
299 COMBINED_LOCK_ELEMENT NewElement
= *Conflict
;
300 PCOMBINED_LOCK_ELEMENT Entry
;
301 while ((Entry
= RtlLookupElementGenericTable
302 (FileLock
->LockInformation
, &NewElement
)))
304 FsRtlpExpandLockElement(&NewElement
, Entry
);
305 RemovedOld
= RtlDeleteElementGenericTable
306 (&LockInfo
->RangeTable
,
310 Conflict
= RtlInsertElementGenericTable
311 (&LockInfo
->RangeTable
,
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
,
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
)
338 COMBINED_LOCK_ELEMENT ToInsert
;
339 PCOMBINED_LOCK_ELEMENT Conflict
;
340 PLOCK_INFORMATION LockInfo
;
341 PLOCK_SHARED_RANGE NewSharedRange
;
343 ULARGE_INTEGER UnsignedStart
;
344 ULARGE_INTEGER UnsignedEnd
;
346 DPRINT("FsRtlPrivateLock(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x, FailImmediately %d, Exclusive %d)\n",
347 &FileObject
->FileName
,
348 FileOffset
->HighPart
,
350 (int)FileOffset
->QuadPart
,
353 (int)Length
->QuadPart
,
358 UnsignedStart
.QuadPart
= FileOffset
->QuadPart
;
359 UnsignedEnd
.QuadPart
= FileOffset
->QuadPart
+ Length
->QuadPart
;
361 if (UnsignedEnd
.QuadPart
< UnsignedStart
.QuadPart
)
363 DPRINT("File offset out of range\n");
364 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
367 DPRINT("Complete lock %p Status %x\n", Irp
, IoStatus
->Status
);
368 FsRtlCompleteLockIrpReal
369 (FileLock
->CompleteLockIrpRoutine
,
379 /* Initialize the lock, if necessary */
380 if (!FileLock
->LockInformation
)
382 LockInfo
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(LOCK_INFORMATION
), 'FLCK');
385 IoStatus
->Status
= STATUS_NO_MEMORY
;
388 FileLock
->LockInformation
= LockInfo
;
390 LockInfo
->BelongsTo
= FileLock
;
391 InitializeListHead(&LockInfo
->SharedLocks
);
393 RtlInitializeGenericTable
394 (&LockInfo
->RangeTable
,
400 KeInitializeSpinLock(&LockInfo
->CsqLock
);
401 InitializeListHead(&LockInfo
->CsqList
);
408 LockAcquireQueueLock
,
409 LockReleaseQueueLock
,
410 LockCompleteCanceledIrp
);
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
->UniqueProcessId
;
418 ToInsert
.Exclusive
.FileLock
.Key
= Key
;
419 ToInsert
.Exclusive
.FileLock
.ExclusiveLock
= ExclusiveLock
;
421 Conflict
= RtlInsertElementGenericTable
422 (FileLock
->LockInformation
,
427 if (Conflict
&& !InsertedNew
)
429 if (Conflict
->Exclusive
.FileLock
.ExclusiveLock
|| ExclusiveLock
)
431 DPRINT("Conflict %08x%08x:%08x%08x Exc %d (Want Exc %d)\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
,
440 DPRINT("STATUS_FILE_LOCK_CONFLICT\n");
441 IoStatus
->Status
= STATUS_FILE_LOCK_CONFLICT
;
444 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n");
445 FsRtlCompleteLockIrpReal
446 (FileLock
->CompleteLockIrpRoutine
,
457 IoStatus
->Status
= STATUS_PENDING
;
460 Irp
->IoStatus
.Information
= LockInfo
->Generation
;
461 IoMarkIrpPending(Irp
);
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
++)
478 Conflict
= RtlGetElementGenericTable(&LockInfo
->RangeTable
, i
);
480 /* The first argument will be inserted as a shared range */
481 if (Conflict
&& (LockCompare(&LockInfo
->RangeTable
, Conflict
, &ToInsert
) == GenericEqual
))
483 if (Conflict
->Exclusive
.FileLock
.ExclusiveLock
)
485 /* Found an exclusive match */
488 IoStatus
->Status
= STATUS_FILE_LOCK_CONFLICT
;
489 DPRINT("STATUS_FILE_LOCK_CONFLICT\n");
492 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n");
493 FsRtlCompleteLockIrpReal
494 (FileLock
->CompleteLockIrpRoutine
,
504 IoStatus
->Status
= STATUS_PENDING
;
507 IoMarkIrpPending(Irp
);
519 DPRINT("Overlapping shared lock %wZ %08x%08x %08x%08x\n",
520 &FileObject
->FileName
,
521 Conflict
->Exclusive
.FileLock
.StartingByte
.HighPart
,
522 Conflict
->Exclusive
.FileLock
.StartingByte
.LowPart
,
523 Conflict
->Exclusive
.FileLock
.EndingByte
.HighPart
,
524 Conflict
->Exclusive
.FileLock
.EndingByte
.LowPart
);
525 Conflict
= FsRtlpRebuildSharedLockRange(FileLock
,
530 IoStatus
->Status
= STATUS_NO_MEMORY
;
533 FsRtlCompleteLockIrpReal
534 (FileLock
->CompleteLockIrpRoutine
,
547 /* We got here because there were only overlapping shared locks */
548 /* A shared lock is both a range *and* a list entry. Insert the
551 DPRINT("Adding shared lock %wZ\n", &FileObject
->FileName
);
553 ExAllocatePoolWithTag(NonPagedPool
, sizeof(*NewSharedRange
), 'FSRA');
556 IoStatus
->Status
= STATUS_NO_MEMORY
;
559 FsRtlCompleteLockIrpReal
560 (FileLock
->CompleteLockIrpRoutine
,
569 DPRINT("Adding shared lock %wZ\n", &FileObject
->FileName
);
570 NewSharedRange
->Start
= ToInsert
.Exclusive
.FileLock
.StartingByte
;
571 NewSharedRange
->End
= ToInsert
.Exclusive
.FileLock
.EndingByte
;
572 NewSharedRange
->Key
= Key
;
573 NewSharedRange
->ProcessId
= ToInsert
.Exclusive
.FileLock
.ProcessId
;
574 InsertTailList(&LockInfo
->SharedLocks
, &NewSharedRange
->Entry
);
576 DPRINT("Acquired shared lock %wZ %08x%08x %08x%08x\n",
577 &FileObject
->FileName
,
578 Conflict
->Exclusive
.FileLock
.StartingByte
.HighPart
,
579 Conflict
->Exclusive
.FileLock
.StartingByte
.LowPart
,
580 Conflict
->Exclusive
.FileLock
.EndingByte
.HighPart
,
581 Conflict
->Exclusive
.FileLock
.EndingByte
.LowPart
);
582 IoStatus
->Status
= STATUS_SUCCESS
;
585 FsRtlCompleteLockIrpReal
586 (FileLock
->CompleteLockIrpRoutine
,
598 /* Conflict here is (or would be) the newly inserted element, but we ran
599 * out of space probably. */
600 IoStatus
->Status
= STATUS_NO_MEMORY
;
603 FsRtlCompleteLockIrpReal
604 (FileLock
->CompleteLockIrpRoutine
,
615 DPRINT("Inserted new lock %wZ %08x%08x %08x%08x exclusive %d\n",
616 &FileObject
->FileName
,
617 Conflict
->Exclusive
.FileLock
.StartingByte
.HighPart
,
618 Conflict
->Exclusive
.FileLock
.StartingByte
.LowPart
,
619 Conflict
->Exclusive
.FileLock
.EndingByte
.HighPart
,
620 Conflict
->Exclusive
.FileLock
.EndingByte
.LowPart
,
621 Conflict
->Exclusive
.FileLock
.ExclusiveLock
);
625 ExAllocatePoolWithTag(NonPagedPool
, sizeof(*NewSharedRange
), 'FSRA');
628 IoStatus
->Status
= STATUS_NO_MEMORY
;
631 FsRtlCompleteLockIrpReal
632 (FileLock
->CompleteLockIrpRoutine
,
641 DPRINT("Adding shared lock %wZ\n", &FileObject
->FileName
);
642 NewSharedRange
->Start
= ToInsert
.Exclusive
.FileLock
.StartingByte
;
643 NewSharedRange
->End
= ToInsert
.Exclusive
.FileLock
.EndingByte
;
644 NewSharedRange
->Key
= Key
;
645 NewSharedRange
->ProcessId
= ToInsert
.Exclusive
.FileLock
.ProcessId
;
646 InsertTailList(&LockInfo
->SharedLocks
, &NewSharedRange
->Entry
);
649 /* Assume all is cool, and lock is set */
650 IoStatus
->Status
= STATUS_SUCCESS
;
654 /* Complete the request */
655 FsRtlCompleteLockIrpReal(FileLock
->CompleteLockIrpRoutine
,
662 /* Update the status */
663 IoStatus
->Status
= Status
;
675 FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock
,
679 PIO_STACK_LOCATION IoStack
= IoGetCurrentIrpStackLocation(Irp
);
680 COMBINED_LOCK_ELEMENT ToFind
;
681 PCOMBINED_LOCK_ELEMENT Found
;
682 DPRINT("CheckLockForReadAccess(%wZ, Offset %08x%08x, Length %x)\n",
683 &IoStack
->FileObject
->FileName
,
684 IoStack
->Parameters
.Read
.ByteOffset
.HighPart
,
685 IoStack
->Parameters
.Read
.ByteOffset
.LowPart
,
686 IoStack
->Parameters
.Read
.Length
);
687 if (!FileLock
->LockInformation
) {
688 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack
->FileObject
->FileName
);
691 ToFind
.Exclusive
.FileLock
.StartingByte
= IoStack
->Parameters
.Read
.ByteOffset
;
692 ToFind
.Exclusive
.FileLock
.EndingByte
.QuadPart
=
693 ToFind
.Exclusive
.FileLock
.StartingByte
.QuadPart
+
694 IoStack
->Parameters
.Read
.Length
;
695 Found
= RtlLookupElementGenericTable
696 (FileLock
->LockInformation
,
699 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack
->FileObject
->FileName
);
702 Result
= !Found
->Exclusive
.FileLock
.ExclusiveLock
||
703 IoStack
->Parameters
.Read
.Key
== Found
->Exclusive
.FileLock
.Key
;
704 DPRINT("CheckLockForReadAccess(%wZ) => %s\n", &IoStack
->FileObject
->FileName
, Result
? "TRUE" : "FALSE");
713 FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock
,
717 PIO_STACK_LOCATION IoStack
= IoGetCurrentIrpStackLocation(Irp
);
718 COMBINED_LOCK_ELEMENT ToFind
;
719 PCOMBINED_LOCK_ELEMENT Found
;
720 PEPROCESS Process
= Irp
->Tail
.Overlay
.Thread
->ThreadsProcess
;
721 DPRINT("CheckLockForWriteAccess(%wZ, Offset %08x%08x, Length %x)\n",
722 &IoStack
->FileObject
->FileName
,
723 IoStack
->Parameters
.Write
.ByteOffset
.HighPart
,
724 IoStack
->Parameters
.Write
.ByteOffset
.LowPart
,
725 IoStack
->Parameters
.Write
.Length
);
726 if (!FileLock
->LockInformation
) {
727 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack
->FileObject
->FileName
);
730 ToFind
.Exclusive
.FileLock
.StartingByte
= IoStack
->Parameters
.Write
.ByteOffset
;
731 ToFind
.Exclusive
.FileLock
.EndingByte
.QuadPart
=
732 ToFind
.Exclusive
.FileLock
.StartingByte
.QuadPart
+
733 IoStack
->Parameters
.Write
.Length
;
734 Found
= RtlLookupElementGenericTable
735 (FileLock
->LockInformation
,
738 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack
->FileObject
->FileName
);
741 Result
= Process
->UniqueProcessId
== Found
->Exclusive
.FileLock
.ProcessId
;
742 DPRINT("CheckLockForWriteAccess(%wZ) => %s\n", &IoStack
->FileObject
->FileName
, Result
? "TRUE" : "FALSE");
751 FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock
,
752 IN PLARGE_INTEGER FileOffset
,
753 IN PLARGE_INTEGER Length
,
755 IN PFILE_OBJECT FileObject
,
758 PEPROCESS EProcess
= Process
;
759 COMBINED_LOCK_ELEMENT ToFind
;
760 PCOMBINED_LOCK_ELEMENT Found
;
761 DPRINT("FsRtlFastCheckLockForRead(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n",
762 &FileObject
->FileName
,
763 FileOffset
->HighPart
,
768 ToFind
.Exclusive
.FileLock
.StartingByte
= *FileOffset
;
769 ToFind
.Exclusive
.FileLock
.EndingByte
.QuadPart
=
770 FileOffset
->QuadPart
+ Length
->QuadPart
;
771 if (!FileLock
->LockInformation
) return TRUE
;
772 Found
= RtlLookupElementGenericTable
773 (FileLock
->LockInformation
,
775 if (!Found
|| !Found
->Exclusive
.FileLock
.ExclusiveLock
) return TRUE
;
776 return Found
->Exclusive
.FileLock
.Key
== Key
&&
777 Found
->Exclusive
.FileLock
.ProcessId
== EProcess
->UniqueProcessId
;
785 FsRtlFastCheckLockForWrite(IN PFILE_LOCK FileLock
,
786 IN PLARGE_INTEGER FileOffset
,
787 IN PLARGE_INTEGER Length
,
789 IN PFILE_OBJECT FileObject
,
793 PEPROCESS EProcess
= Process
;
794 COMBINED_LOCK_ELEMENT ToFind
;
795 PCOMBINED_LOCK_ELEMENT Found
;
796 DPRINT("FsRtlFastCheckLockForWrite(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n",
797 &FileObject
->FileName
,
798 FileOffset
->HighPart
,
803 ToFind
.Exclusive
.FileLock
.StartingByte
= *FileOffset
;
804 ToFind
.Exclusive
.FileLock
.EndingByte
.QuadPart
=
805 FileOffset
->QuadPart
+ Length
->QuadPart
;
806 if (!FileLock
->LockInformation
) {
807 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject
->FileName
);
810 Found
= RtlLookupElementGenericTable
811 (FileLock
->LockInformation
,
814 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject
->FileName
);
817 Result
= Found
->Exclusive
.FileLock
.Key
== Key
&&
818 Found
->Exclusive
.FileLock
.ProcessId
== EProcess
->UniqueProcessId
;
819 DPRINT("CheckForWrite(%wZ) => %s\n", &FileObject
->FileName
, Result
? "TRUE" : "FALSE");
828 FsRtlFastUnlockSingle(IN PFILE_LOCK FileLock
,
829 IN PFILE_OBJECT FileObject
,
830 IN PLARGE_INTEGER FileOffset
,
831 IN PLARGE_INTEGER Length
,
832 IN PEPROCESS Process
,
834 IN PVOID Context OPTIONAL
,
835 IN BOOLEAN AlreadySynchronized
)
837 BOOLEAN FoundShared
= FALSE
;
838 PLIST_ENTRY SharedEntry
;
839 PLOCK_SHARED_RANGE SharedRange
= NULL
;
840 COMBINED_LOCK_ELEMENT Find
;
841 PCOMBINED_LOCK_ELEMENT Entry
;
842 PIRP NextMatchingLockIrp
;
843 PLOCK_INFORMATION InternalInfo
= FileLock
->LockInformation
;
844 DPRINT("FsRtlFastUnlockSingle(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x)\n",
845 &FileObject
->FileName
,
846 FileOffset
->HighPart
,
848 (int)FileOffset
->QuadPart
,
851 (int)Length
->QuadPart
,
853 // The region to unlock must correspond exactly to a previously locked region
855 // But Windows 2003 doesn't assert on it and simply ignores that parameter
856 // ASSERT(AlreadySynchronized);
857 Find
.Exclusive
.FileLock
.StartingByte
= *FileOffset
;
858 Find
.Exclusive
.FileLock
.EndingByte
.QuadPart
=
859 FileOffset
->QuadPart
+ Length
->QuadPart
;
860 ASSERT(InternalInfo
);
861 Entry
= RtlLookupElementGenericTable(&InternalInfo
->RangeTable
, &Find
);
863 DPRINT("Range not locked %wZ\n", &FileObject
->FileName
);
864 return STATUS_RANGE_NOT_LOCKED
;
867 DPRINT("Found lock entry: Exclusive %d %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
);
875 if (Entry
->Exclusive
.FileLock
.ExclusiveLock
)
877 if (Entry
->Exclusive
.FileLock
.Key
!= Key
||
878 Entry
->Exclusive
.FileLock
.ProcessId
!= Process
->UniqueProcessId
||
879 Entry
->Exclusive
.FileLock
.StartingByte
.QuadPart
!= FileOffset
->QuadPart
||
880 Entry
->Exclusive
.FileLock
.EndingByte
.QuadPart
!=
881 FileOffset
->QuadPart
+ Length
->QuadPart
)
883 DPRINT("Range not locked %wZ\n", &FileObject
->FileName
);
884 return STATUS_RANGE_NOT_LOCKED
;
886 RtlCopyMemory(&Find
, Entry
, sizeof(Find
));
887 // Remove the old exclusive lock region
888 RtlDeleteElementGenericTable(&InternalInfo
->RangeTable
, Entry
);
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
)
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
->UniqueProcessId
)
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
,
921 PLIST_ENTRY SharedRangeEntry
;
922 PLOCK_SHARED_RANGE WatchSharedRange
;
923 COMBINED_LOCK_ELEMENT RemadeElement
;
924 Find
.Exclusive
.FileLock
.StartingByte
= SharedRange
->Start
;
925 Find
.Exclusive
.FileLock
.EndingByte
= SharedRange
->End
;
926 SharedEntry
= SharedRange
->Entry
.Flink
;
927 RemoveEntryList(&SharedRange
->Entry
);
928 ExFreePool(SharedRange
);
929 /* We need to rebuild the list of shared ranges. */
930 DPRINT("Removing the lock entry %wZ (%08x%08x:%08x%08x)\n",
931 &FileObject
->FileName
,
932 Entry
->Exclusive
.FileLock
.StartingByte
.HighPart
,
933 Entry
->Exclusive
.FileLock
.StartingByte
.LowPart
,
934 Entry
->Exclusive
.FileLock
.EndingByte
.HighPart
,
935 Entry
->Exclusive
.FileLock
.EndingByte
.LowPart
);
937 RemadeElement
= *Entry
;
938 RtlDeleteElementGenericTable(&InternalInfo
->RangeTable
, Entry
);
939 /* Put shared locks back in place */
940 for (SharedRangeEntry
= InternalInfo
->SharedLocks
.Flink
;
941 SharedRangeEntry
!= &InternalInfo
->SharedLocks
;
942 SharedRangeEntry
= SharedRangeEntry
->Flink
)
944 COMBINED_LOCK_ELEMENT LockElement
;
945 WatchSharedRange
= CONTAINING_RECORD(SharedRangeEntry
, LOCK_SHARED_RANGE
, Entry
);
946 LockElement
.Exclusive
.FileLock
.StartingByte
= WatchSharedRange
->Start
;
947 LockElement
.Exclusive
.FileLock
.EndingByte
= WatchSharedRange
->End
;
948 if (LockCompare(&InternalInfo
->RangeTable
, &RemadeElement
, &LockElement
) != GenericEqual
)
950 DPRINT("Skipping range %08x%08x:%08x%08x\n",
951 LockElement
.Exclusive
.FileLock
.StartingByte
.HighPart
,
952 LockElement
.Exclusive
.FileLock
.StartingByte
.LowPart
,
953 LockElement
.Exclusive
.FileLock
.EndingByte
.HighPart
,
954 LockElement
.Exclusive
.FileLock
.EndingByte
.LowPart
);
957 DPRINT("Re-creating range %08x%08x:%08x%08x\n",
958 LockElement
.Exclusive
.FileLock
.StartingByte
.HighPart
,
959 LockElement
.Exclusive
.FileLock
.StartingByte
.LowPart
,
960 LockElement
.Exclusive
.FileLock
.EndingByte
.HighPart
,
961 LockElement
.Exclusive
.FileLock
.EndingByte
.LowPart
);
962 RtlZeroMemory(&RemadeElement
, sizeof(RemadeElement
));
963 RemadeElement
.Exclusive
.FileLock
.StartingByte
= WatchSharedRange
->Start
;
964 RemadeElement
.Exclusive
.FileLock
.EndingByte
= WatchSharedRange
->End
;
965 FsRtlpRebuildSharedLockRange(FileLock
, InternalInfo
, &RemadeElement
);
970 return STATUS_RANGE_NOT_LOCKED
;
974 DPRINT("Lock still has:\n");
975 for (SharedEntry
= InternalInfo
->SharedLocks
.Flink
;
976 SharedEntry
!= &InternalInfo
->SharedLocks
;
977 SharedEntry
= SharedEntry
->Flink
)
979 SharedRange
= CONTAINING_RECORD(SharedEntry
, LOCK_SHARED_RANGE
, Entry
);
980 DPRINT("Shared element %wZ Offset %08x%08x Length %08x%08x Key %x\n",
981 &FileObject
->FileName
,
982 SharedRange
->Start
.HighPart
,
983 SharedRange
->Start
.LowPart
,
984 SharedRange
->End
.HighPart
,
985 SharedRange
->End
.LowPart
,
989 // this is definitely the thing we want
990 InternalInfo
->Generation
++;
991 while ((NextMatchingLockIrp
= IoCsqRemoveNextIrp(&InternalInfo
->Csq
, &Find
)))
993 if (NextMatchingLockIrp
->IoStatus
.Information
== InternalInfo
->Generation
)
995 // We've already looked at this one, meaning that we looped.
996 // Put it back and exit.
1004 // Got a new lock irp... try to do the new lock operation
1005 // Note that we pick an operation that would succeed at the time
1006 // we looked, but can't guarantee that it won't just be re-queued
1007 // because somebody else snatched part of the range in a new thread.
1008 DPRINT("Locking another IRP %p for %p %wZ\n",
1009 &FileObject
->FileName
, FileLock
, NextMatchingLockIrp
);
1010 FsRtlProcessFileLock(InternalInfo
->BelongsTo
, NextMatchingLockIrp
, NULL
);
1013 DPRINT("Success %wZ\n", &FileObject
->FileName
);
1014 return STATUS_SUCCESS
;
1022 FsRtlFastUnlockAll(IN PFILE_LOCK FileLock
,
1023 IN PFILE_OBJECT FileObject
,
1024 IN PEPROCESS Process
,
1025 IN PVOID Context OPTIONAL
)
1027 PLIST_ENTRY ListEntry
;
1028 PCOMBINED_LOCK_ELEMENT Entry
;
1029 PLOCK_INFORMATION InternalInfo
= FileLock
->LockInformation
;
1030 DPRINT("FsRtlFastUnlockAll(%wZ)\n", &FileObject
->FileName
);
1031 // XXX Synchronize somehow
1032 if (!FileLock
->LockInformation
) {
1033 DPRINT("Not locked %wZ\n", &FileObject
->FileName
);
1034 return STATUS_RANGE_NOT_LOCKED
; // no locks
1036 for (ListEntry
= InternalInfo
->SharedLocks
.Flink
;
1037 ListEntry
!= &InternalInfo
->SharedLocks
;)
1039 LARGE_INTEGER Length
;
1040 PLOCK_SHARED_RANGE Range
= CONTAINING_RECORD(ListEntry
, LOCK_SHARED_RANGE
, Entry
);
1041 Length
.QuadPart
= Range
->End
.QuadPart
- Range
->Start
.QuadPart
;
1042 ListEntry
= ListEntry
->Flink
;
1043 if (Range
->ProcessId
!= Process
->UniqueProcessId
)
1045 FsRtlFastUnlockSingle
1055 for (Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, TRUE
);
1057 Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, FALSE
))
1059 LARGE_INTEGER Length
;
1060 // We'll take the first one to be the list head, and free the others first...
1062 Entry
->Exclusive
.FileLock
.EndingByte
.QuadPart
-
1063 Entry
->Exclusive
.FileLock
.StartingByte
.QuadPart
;
1064 FsRtlFastUnlockSingle
1066 Entry
->Exclusive
.FileLock
.FileObject
,
1067 &Entry
->Exclusive
.FileLock
.StartingByte
,
1069 Entry
->Exclusive
.FileLock
.ProcessId
,
1070 Entry
->Exclusive
.FileLock
.Key
,
1074 DPRINT("Done %wZ\n", &FileObject
->FileName
);
1075 return STATUS_SUCCESS
;
1083 FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock
,
1084 IN PFILE_OBJECT FileObject
,
1085 IN PEPROCESS Process
,
1087 IN PVOID Context OPTIONAL
)
1089 PLIST_ENTRY ListEntry
;
1090 PCOMBINED_LOCK_ELEMENT Entry
;
1091 PLOCK_INFORMATION InternalInfo
= FileLock
->LockInformation
;
1093 DPRINT("FsRtlFastUnlockAllByKey(%wZ,Key %x)\n", &FileObject
->FileName
, Key
);
1095 // XXX Synchronize somehow
1096 if (!FileLock
->LockInformation
) return STATUS_RANGE_NOT_LOCKED
; // no locks
1097 for (ListEntry
= InternalInfo
->SharedLocks
.Flink
;
1098 ListEntry
!= &InternalInfo
->SharedLocks
;)
1100 PLOCK_SHARED_RANGE Range
= CONTAINING_RECORD(ListEntry
, LOCK_SHARED_RANGE
, Entry
);
1101 LARGE_INTEGER Length
;
1102 Length
.QuadPart
= Range
->End
.QuadPart
- Range
->Start
.QuadPart
;
1103 ListEntry
= ListEntry
->Flink
;
1104 if (Range
->ProcessId
!= Process
->UniqueProcessId
||
1107 FsRtlFastUnlockSingle
1117 for (Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, TRUE
);
1119 Entry
= RtlEnumerateGenericTable(&InternalInfo
->RangeTable
, FALSE
))
1121 LARGE_INTEGER Length
;
1122 // We'll take the first one to be the list head, and free the others first...
1124 Entry
->Exclusive
.FileLock
.EndingByte
.QuadPart
-
1125 Entry
->Exclusive
.FileLock
.StartingByte
.QuadPart
;
1126 if (Entry
->Exclusive
.FileLock
.Key
== Key
&&
1127 Entry
->Exclusive
.FileLock
.ProcessId
== Process
->UniqueProcessId
)
1129 FsRtlFastUnlockSingle
1131 Entry
->Exclusive
.FileLock
.FileObject
,
1132 &Entry
->Exclusive
.FileLock
.StartingByte
,
1134 Entry
->Exclusive
.FileLock
.ProcessId
,
1135 Entry
->Exclusive
.FileLock
.Key
,
1141 return STATUS_SUCCESS
;
1149 FsRtlProcessFileLock(IN PFILE_LOCK FileLock
,
1151 IN PVOID Context OPTIONAL
)
1153 PIO_STACK_LOCATION IoStackLocation
;
1155 IO_STATUS_BLOCK IoStatusBlock
;
1157 /* Get the I/O Stack location */
1158 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
1159 ASSERT(IoStackLocation
->MajorFunction
== IRP_MJ_LOCK_CONTROL
);
1161 /* Clear the I/O status block and check what function this is */
1162 IoStatusBlock
.Information
= 0;
1164 DPRINT("FsRtlProcessFileLock(%wZ, MinorFunction %x)\n",
1165 &IoStackLocation
->FileObject
->FileName
,
1166 IoStackLocation
->MinorFunction
);
1168 switch(IoStackLocation
->MinorFunction
)
1173 /* Call the private lock routine */
1174 FsRtlPrivateLock(FileLock
,
1175 IoStackLocation
->FileObject
,
1177 Parameters
.LockControl
.ByteOffset
,
1178 IoStackLocation
->Parameters
.LockControl
.Length
,
1179 IoGetRequestorProcess(Irp
),
1180 IoStackLocation
->Parameters
.LockControl
.Key
,
1181 IoStackLocation
->Flags
& SL_FAIL_IMMEDIATELY
,
1182 IoStackLocation
->Flags
& SL_EXCLUSIVE_LOCK
,
1187 return IoStatusBlock
.Status
;
1189 /* A single unlock */
1190 case IRP_MN_UNLOCK_SINGLE
:
1192 /* Call fast unlock */
1193 IoStatusBlock
.Status
=
1194 FsRtlFastUnlockSingle(FileLock
,
1195 IoStackLocation
->FileObject
,
1196 &IoStackLocation
->Parameters
.LockControl
.
1198 IoStackLocation
->Parameters
.LockControl
.
1200 IoGetRequestorProcess(Irp
),
1201 IoStackLocation
->Parameters
.LockControl
.
1208 case IRP_MN_UNLOCK_ALL
:
1210 /* Do a fast unlock */
1211 IoStatusBlock
.Status
= FsRtlFastUnlockAll(FileLock
,
1214 IoGetRequestorProcess(Irp
),
1219 case IRP_MN_UNLOCK_ALL_BY_KEY
:
1222 IoStatusBlock
.Status
=
1223 FsRtlFastUnlockAllByKey(FileLock
,
1224 IoStackLocation
->FileObject
,
1225 IoGetRequestorProcess(Irp
),
1226 IoStackLocation
->Parameters
.
1231 /* Invalid request */
1235 FsRtlCompleteRequest(Irp
, STATUS_INVALID_DEVICE_REQUEST
);
1236 IoStatusBlock
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
1237 return STATUS_INVALID_DEVICE_REQUEST
;
1240 /* Return the status */
1241 DPRINT("Lock IRP %p %x\n", Irp
, IoStatusBlock
.Status
);
1242 FsRtlCompleteLockIrpReal
1243 (FileLock
->CompleteLockIrpRoutine
,
1246 IoStatusBlock
.Status
,
1249 return IoStatusBlock
.Status
;
1257 FsRtlInitializeFileLock (IN PFILE_LOCK FileLock
,
1258 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL
,
1259 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
)
1261 /* Setup the lock */
1262 RtlZeroMemory(FileLock
, sizeof(*FileLock
));
1263 FileLock
->FastIoIsQuestionable
= FALSE
;
1264 FileLock
->CompleteLockIrpRoutine
= CompleteLockIrpRoutine
;
1265 FileLock
->UnlockRoutine
= UnlockRoutine
;
1266 FileLock
->LockInformation
= NULL
;
1274 FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock
)
1276 if (FileLock
->LockInformation
)
1279 PLOCK_INFORMATION InternalInfo
= FileLock
->LockInformation
;
1280 PCOMBINED_LOCK_ELEMENT Entry
;
1281 PLIST_ENTRY SharedEntry
;
1282 PLOCK_SHARED_RANGE SharedRange
;
1283 // MSDN: this completes any remaining lock IRPs
1284 for (SharedEntry
= InternalInfo
->SharedLocks
.Flink
;
1285 SharedEntry
!= &InternalInfo
->SharedLocks
;)
1287 SharedRange
= CONTAINING_RECORD(SharedEntry
, LOCK_SHARED_RANGE
, Entry
);
1288 SharedEntry
= SharedEntry
->Flink
;
1289 RemoveEntryList(SharedEntry
);
1290 ExFreePool(SharedRange
);
1292 while ((Entry
= RtlGetElementGenericTable(&InternalInfo
->RangeTable
, 0)) != NULL
)
1294 RtlDeleteElementGenericTable(&InternalInfo
->RangeTable
, Entry
);
1296 while ((Irp
= IoCsqRemoveNextIrp(&InternalInfo
->Csq
, NULL
)) != NULL
)
1298 FsRtlProcessFileLock(FileLock
, Irp
, NULL
);
1300 ExFreePoolWithTag(InternalInfo
, 'FLCK');
1301 FileLock
->LockInformation
= NULL
;
1310 FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL
,
1311 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
)
1313 PFILE_LOCK FileLock
;
1315 /* Try to allocate it */
1316 FileLock
= ExAllocateFromPagedLookasideList(&FsRtlFileLockLookasideList
);
1320 FsRtlInitializeFileLock(FileLock
,
1321 CompleteLockIrpRoutine
,
1325 /* Return the lock */
1334 FsRtlFreeFileLock(IN PFILE_LOCK FileLock
)
1336 /* Uninitialize and free the lock */
1337 FsRtlUninitializeFileLock(FileLock
);
1338 ExFreeToPagedLookasideList(&FsRtlFileLockLookasideList
, FileLock
);