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