xref: /reactos/ntoskrnl/fsrtl/filelock.c (revision 9393fc32)
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