update for HEAD-2003091401
[reactos.git] / ntoskrnl / fs / filelock.c
1 /* $Id$
2  *
3  * reactos/ntoskrnl/fs/filelock.c
4  *
5  */
6 #include <ddk/ntddk.h>
7 #include <internal/ifs.h>
8 #include <ddk/ntifs.h>
9 #include <ntos.h>
10
11 #define NDEBUG
12 #include <internal/debug.h>
13
14 /*
15 NOTE:
16 I'm not using resource syncronization here, since FsRtlFastCheckLockForRead/Write
17 are allowed to be called at DISPATCH_LEVEL. Must therefore use nonpaged memory for
18 the lists.
19 */
20
21 #define LOCK_START_OFF(Lock)  ((Lock).StartingByte.QuadPart)
22 #define LOCK_END_OFF(Lock)    (((Lock).StartingByte.QuadPart) + ((Lock).Length.QuadPart) - 1)
23 #define REQUEST_START_OFF     (FileOffset->QuadPart)
24 #define REQUEST_END_OFF       ((FileOffset->QuadPart) + (Length->QuadPart) - 1)
25
26 FAST_MUTEX              LockTocMutex;
27 NPAGED_LOOKASIDE_LIST   GrantedLookaside;
28 NPAGED_LOOKASIDE_LIST   LockTocLookaside;
29 PAGED_LOOKASIDE_LIST    LockLookaside;
30
31 /**********************************************************************
32  * NAME                                                 PRIVATE
33  *      FsRtlpInitFileLockingImplementation
34  *
35  */
36 VOID
37 STDCALL
38 FsRtlpInitFileLockingImplementation(VOID)
39 {
40    ExInitializeNPagedLookasideList( &LockTocLookaside,
41                                     NULL,
42                                     NULL,
43                                     0,
44                                     sizeof(FILE_LOCK_TOC),
45                                     IFS_POOL_TAG,
46                                     0
47                                     );
48
49    ExInitializeNPagedLookasideList( &GrantedLookaside,
50                                     NULL,
51                                     NULL,
52                                     0,
53                                     sizeof(FILE_LOCK_GRANTED),
54                                     IFS_POOL_TAG,
55                                     0
56                                     );
57
58    ExInitializePagedLookasideList(  &LockLookaside,
59                                     NULL,
60                                     NULL,
61                                     0,
62                                     sizeof(FILE_LOCK),
63                                     IFS_POOL_TAG,
64                                     0
65                                     );
66
67    ExInitializeFastMutex(&LockTocMutex);
68 }
69
70 /**********************************************************************
71  * NAME                                                 PRIVATE
72  *      FsRtlpFileLockCancelRoutine
73  *
74  */
75 VOID
76 STDCALL 
77 FsRtlpFileLockCancelRoutine(
78    IN PDEVICE_OBJECT DeviceObject, 
79    IN PIRP Irp
80    )
81 {
82    KIRQL                         oldIrql;
83    PKSPIN_LOCK                   SpinLock;
84    PCOMPLETE_LOCK_IRP_ROUTINE    CompleteLockIrpRoutine;
85
86    //don't need this since we have our own sync. protecting irp cancellation
87    IoReleaseCancelSpinLock(Irp->CancelIrql); 
88
89    SpinLock = &((PFILE_LOCK_TOC)Irp->Tail.Overlay.DriverContext[1])->SpinLock;
90
91    KeAcquireSpinLock(SpinLock, &oldIrql);
92    RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
93    KeReleaseSpinLock(SpinLock, oldIrql);
94
95    Irp->IoStatus.Status = STATUS_CANCELLED;
96
97    CompleteLockIrpRoutine = ((PFILE_LOCK)Irp->Tail.Overlay.DriverContext[0])->CompleteLockIrpRoutine;
98    if (CompleteLockIrpRoutine)
99    {
100       CompleteLockIrpRoutine(Irp->Tail.Overlay.DriverContext[2], Irp);
101    }
102    else
103    {
104       IofCompleteRequest(Irp, IO_NO_INCREMENT);
105    }
106    
107 }
108
109 /**********************************************************************
110  * NAME                                                 PRIVATE
111  *      FsRtlpCheckLockForReadOrWriteAccess
112  *
113  */
114 BOOLEAN
115 FASTCALL
116 FsRtlpCheckLockForReadOrWriteAccess(
117    IN PFILE_LOCK           FileLock,
118    IN PLARGE_INTEGER       FileOffset,
119    IN PLARGE_INTEGER       Length,
120    IN ULONG                Key,
121    IN PFILE_OBJECT         FileObject,
122    IN PEPROCESS            Process,
123    IN BOOLEAN              Read
124    )
125 {
126    KIRQL                oldirql;
127    PFILE_LOCK_TOC       LockToc;
128    PFILE_LOCK_GRANTED   Granted;
129    PLIST_ENTRY          EnumEntry;
130
131    assert(FileLock);
132    LockToc = FileLock->LockInformation;
133
134    if (LockToc == NULL || Length->QuadPart == 0) 
135    {
136       return TRUE;
137    }
138
139    KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
140
141    EnumEntry = LockToc->GrantedListHead.Flink;
142    while ( EnumEntry != &LockToc->GrantedListHead)
143    {
144       Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED , ListEntry );
145       //if overlapping
146       if(!(REQUEST_START_OFF > LOCK_END_OFF(Granted->Lock) || 
147          REQUEST_END_OFF < LOCK_START_OFF(Granted->Lock))) 
148       {
149          //No read conflict if (shared lock) OR (exclusive + our lock)
150          //No write conflict if exclusive lock AND our lock
151          if ((Read && !Granted->Lock.ExclusiveLock) ||
152             (Granted->Lock.ExclusiveLock &&     
153             Granted->Lock.Process == Process &&
154             Granted->Lock.FileObject == FileObject &&
155             Granted->Lock.Key == Key ) ) 
156          {
157             //AND if lock surround request region, stop searching and grant
158             if (REQUEST_START_OFF >= LOCK_START_OFF(Granted->Lock) && 
159                REQUEST_END_OFF <= LOCK_END_OFF(Granted->Lock))
160             {
161                EnumEntry = &LockToc->GrantedListHead;//indicate no conflict
162                break;
163             }
164             //else continue searching for conflicts
165          }
166          else //conflict
167          {
168             break;
169          }
170       }
171       EnumEntry = EnumEntry->Flink;
172    }
173
174    KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
175
176    if (EnumEntry == &LockToc->GrantedListHead) 
177    { //no conflict
178       return TRUE;
179    }
180
181    return FALSE;
182 }
183
184
185 /**********************************************************************
186  * NAME                                                 EXPORTED
187  *      FsRtlCheckLockForReadAccess
188  *
189  * @implemented
190  */
191 BOOLEAN
192 STDCALL
193 FsRtlCheckLockForReadAccess (
194    IN PFILE_LOCK  FileLock,
195    IN PIRP        Irp
196    )
197 {
198    PIO_STACK_LOCATION   Stack;
199    LARGE_INTEGER        LocalLength;
200
201    Stack = IoGetCurrentIrpStackLocation(Irp);
202
203    LocalLength.u.LowPart = Stack->Parameters.Read.Length;
204    LocalLength.u.HighPart = 0;
205
206    return FsRtlpCheckLockForReadOrWriteAccess(  FileLock,
207                                                 &Stack->Parameters.Read.ByteOffset,
208                                                 &LocalLength,
209                                                 Stack->Parameters.Read.Key,
210                                                 Stack->FileObject,
211                                                 IoGetRequestorProcess(Irp),
212                                                 TRUE//Read?
213                                                 );
214 }
215
216
217 /**********************************************************************
218  * NAME                                                 EXPORTED
219  *      FsRtlCheckLockForWriteAccess
220  *
221  * @implemented
222  */
223 BOOLEAN
224 STDCALL
225 FsRtlCheckLockForWriteAccess (
226    IN PFILE_LOCK  FileLock,
227    IN PIRP        Irp
228    )
229 {
230    PIO_STACK_LOCATION   Stack;
231    LARGE_INTEGER                   LocalLength;
232
233    Stack = IoGetCurrentIrpStackLocation(Irp);
234
235    LocalLength.u.LowPart = Stack->Parameters.Read.Length;
236    LocalLength.u.HighPart = 0;
237
238    return FsRtlpCheckLockForReadOrWriteAccess(  FileLock,
239                                                 &Stack->Parameters.Write.ByteOffset,
240                                                 &LocalLength,
241                                                 Stack->Parameters.Write.Key,
242                                                 Stack->FileObject,
243                                                 IoGetRequestorProcess(Irp),
244                                                 FALSE//Read?
245                                                 );
246
247 }
248
249
250
251
252 /**********************************************************************
253  * NAME                                                 EXPORTED
254  *      FsRtlFastCheckLockForRead
255  *
256  * @implemented
257  */
258 BOOLEAN
259 STDCALL
260 FsRtlFastCheckLockForRead (
261    IN PFILE_LOCK           FileLock,
262    IN PLARGE_INTEGER       FileOffset,
263    IN PLARGE_INTEGER       Length,
264    IN ULONG                Key,
265    IN PFILE_OBJECT         FileObject,
266    IN PEPROCESS            Process
267    )
268 {
269    return FsRtlpCheckLockForReadOrWriteAccess(  FileLock,
270                                                 FileOffset,
271                                                 Length,
272                                                 Key,
273                                                 FileObject,
274                                                 Process,
275                                                 TRUE//Read?
276                                                 );
277 }
278
279
280 /**********************************************************************
281  * NAME                                                 EXPORTED
282  *      FsRtlFastCheckLockForWrite
283  *
284  * @implemented
285  */
286 BOOLEAN
287 STDCALL
288 FsRtlFastCheckLockForWrite (
289    IN PFILE_LOCK           FileLock,
290    IN PLARGE_INTEGER       FileOffset,
291    IN PLARGE_INTEGER       Length,
292    IN ULONG                Key,
293    IN PFILE_OBJECT         FileObject,
294    IN PEPROCESS            Process
295    )
296 {
297    return FsRtlpCheckLockForReadOrWriteAccess(  FileLock,
298                                                 FileOffset,
299                                                 Length,
300                                                 Key,
301                                                 FileObject,
302                                                 Process,
303                                                 FALSE//Read?
304                                                 );
305 }
306
307
308
309 /**********************************************************************
310  * NAME                                                 PRIVATE
311  *      FsRtlpFastUnlockAllByKey
312  *
313  */
314 NTSTATUS
315 FASTCALL
316 FsRtlpFastUnlockAllByKey(
317    IN PFILE_LOCK           FileLock,
318    IN PFILE_OBJECT         FileObject,
319    IN PEPROCESS            Process,
320    IN DWORD                Key,      /* FIXME: guess */
321    IN BOOLEAN              UseKey,   /* FIXME: guess */
322    IN PVOID                Context OPTIONAL
323    )
324 {
325    KIRQL                                      oldirql;
326    PFILE_LOCK_TOC                  LockToc;
327    PLIST_ENTRY                     EnumEntry;
328    PFILE_LOCK_GRANTED   Granted;
329    BOOLEAN                                 Unlock = FALSE;
330    //must make local copy since FILE_LOCK struct is allowed to be paged
331    PUNLOCK_ROUTINE              GotUnlockRoutine;
332
333    assert(FileLock);
334    LockToc = FileLock->LockInformation;
335
336    if (LockToc == NULL)
337    {
338       return STATUS_RANGE_NOT_LOCKED;
339    }
340
341    GotUnlockRoutine = FileLock->UnlockRoutine;
342    KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
343
344    EnumEntry = LockToc->GrantedListHead.Flink;
345    while (EnumEntry != &LockToc->GrantedListHead ) 
346    {
347       Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED, ListEntry);
348       EnumEntry = EnumEntry->Flink;
349
350       if (Granted->Lock.Process == Process &&
351          Granted->Lock.FileObject == FileObject &&
352          (!UseKey || (UseKey && Granted->Lock.Key == Key)) )
353       {
354          RemoveEntryList(&Granted->ListEntry);
355          Unlock = TRUE;
356
357          if (GotUnlockRoutine) 
358          {
359             /*
360             Put on unlocked list and call unlock routine for them afterwards.
361             This way we don't have to restart enum after each call
362             */
363             InsertHeadList(&LockToc->UnlockedListHead,&Granted->ListEntry);
364          }
365          else 
366          {
367             ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);     
368          }
369       }
370    }
371
372    if (Unlock)
373    {
374       //call unlock routine for each unlocked lock (if any)
375       while (!IsListEmpty(&LockToc->UnlockedListHead)) 
376       {
377          EnumEntry = RemoveTailList(&LockToc->UnlockedListHead);
378          Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED, ListEntry);
379          KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
380          FileLock->UnlockRoutine(Context,&Granted->Lock);
381          ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);
382          KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
383       }
384
385       //NOTE: holding spinlock while calling this
386       FsRtlpCompletePendingLocks(FileLock, LockToc, &oldirql);
387
388       if (IsListEmpty(&LockToc->GrantedListHead)) 
389       {
390          KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
391          FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
392       }
393       else
394       {
395          KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
396       }
397       return STATUS_SUCCESS;
398    }
399
400    KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
401    return STATUS_RANGE_NOT_LOCKED;
402 }
403
404 /**********************************************************************
405  * NAME                                                 EXPORTED
406  *      FsRtlFastUnlockAll
407  *
408  * @implemented
409  */
410 NTSTATUS
411 STDCALL
412 FsRtlFastUnlockAll /*ByProcess*/ (
413    IN PFILE_LOCK           FileLock,
414    IN PFILE_OBJECT         FileObject,
415    IN PEPROCESS            Process,
416    IN PVOID                Context OPTIONAL
417    )
418 {
419    return FsRtlpFastUnlockAllByKey( FileLock,
420                                     FileObject,
421                                     Process,
422                                     0,     /* Key */
423                                     FALSE, /* Do NOT use Key */
424                                     Context
425                                     );
426 }
427
428 /**********************************************************************
429  * NAME                                                 EXPORTED
430  *      FsRtlFastUnlockAllByKey
431  *
432  * @implemented
433  */
434 NTSTATUS
435 STDCALL
436 FsRtlFastUnlockAllByKey (
437    IN PFILE_LOCK           FileLock,
438    IN PFILE_OBJECT         FileObject,
439    IN PEPROCESS            Process,
440    IN ULONG                Key,
441    IN PVOID                Context OPTIONAL
442    )
443 {
444    return FsRtlpFastUnlockAllByKey( FileLock,
445                                     FileObject,
446                                     Process,
447                                     Key,
448                                     TRUE, /* Use Key */
449                                     Context
450                                     );
451 }
452
453
454 /**********************************************************************
455  * NAME                                                 PRIVATE
456  *      FsRtlpAddLock
457  *
458  * NOTE
459  *  Spinlock held at entry !!
460  */
461 NTSTATUS
462 FASTCALL
463 FsRtlpAddLock(
464    IN PFILE_LOCK_TOC               LockToc,
465    IN PFILE_OBJECT         FileObject,
466    IN PLARGE_INTEGER       FileOffset,
467    IN PLARGE_INTEGER       Length,
468    IN PEPROCESS            Process,
469    IN ULONG                Key,
470    IN BOOLEAN              ExclusiveLock
471    )
472 {
473    PLIST_ENTRY          EnumEntry;
474    PFILE_LOCK_GRANTED   Granted;
475
476    EnumEntry = LockToc->GrantedListHead.Flink;
477    while (EnumEntry != &LockToc->GrantedListHead) 
478    {
479       Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED, ListEntry);
480       //if overlapping
481       if(!(REQUEST_START_OFF > LOCK_END_OFF(Granted->Lock) || 
482          REQUEST_END_OFF < LOCK_START_OFF(Granted->Lock))) 
483       {
484          //never conflict if shared lock and we want to add a shared lock
485          if (!Granted->Lock.ExclusiveLock && !ExclusiveLock) 
486          {
487             //AND if lock surround region, stop searching and insert lock
488             if (REQUEST_START_OFF >= LOCK_START_OFF(Granted->Lock) && 
489                REQUEST_END_OFF <= LOCK_END_OFF(Granted->Lock))
490             {
491                EnumEntry = &LockToc->GrantedListHead;
492                break;
493             }
494             //else keep locking for conflicts
495          }
496          else 
497          {//conflict if we want share access to excl. lock OR exlc. access to shared lock
498             break;//FAIL
499          }
500       }
501       EnumEntry = EnumEntry->Flink;
502    }
503
504    if (EnumEntry == &LockToc->GrantedListHead) 
505    {//no conflict
506       Granted = ExAllocateFromNPagedLookasideList(&GrantedLookaside);
507
508       Granted->Lock.StartingByte = *FileOffset;
509       Granted->Lock.Length = *Length;
510       Granted->Lock.ExclusiveLock = ExclusiveLock;
511       Granted->Lock.Key = Key;
512       Granted->Lock.FileObject = FileObject;
513       Granted->Lock.Process = Process;
514       Granted->Lock.EndingByte.QuadPart = REQUEST_END_OFF;
515
516       InsertHeadList(&LockToc->GrantedListHead,&Granted->ListEntry);
517       return TRUE;
518    }
519
520    return FALSE;
521
522 }
523
524
525
526 /**********************************************************************
527  * NAME                                                 PRIVATE
528  *      FsRtlpCompletePendingLocks
529  *
530  * NOTE
531  *  Spinlock held at entry !!
532  */
533 VOID
534 FASTCALL
535 FsRtlpCompletePendingLocks(
536    IN       PFILE_LOCK     FileLock,
537    IN       PFILE_LOCK_TOC LockToc,
538    IN OUT   PKIRQL         oldirql
539    )
540 {
541    //walk pending list, FIFO order, try 2 complete locks
542    PLIST_ENTRY                   EnumEntry;
543    PIRP                          Irp;
544    PEXTENDED_IO_STACK_LOCATION            Stack;
545
546    EnumEntry = LockToc->PendingListHead.Blink;
547    while (EnumEntry != &LockToc->PendingListHead) 
548    {
549       Irp = CONTAINING_RECORD(EnumEntry,IRP, Tail.Overlay.ListEntry);
550
551       Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
552       if (FsRtlpAddLock(LockToc,
553                         Stack->FileObject,
554                         &Stack->Parameters.LockControl.ByteOffset,
555                         Stack->Parameters.LockControl.Length,
556                         IoGetRequestorProcess(Irp),
557                         Stack->Parameters.LockControl.Key,
558                         Stack->Flags & SL_EXCLUSIVE_LOCK
559                         ) ) 
560       {
561          RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
562
563          if (!IoSetCancelRoutine(Irp, NULL))
564          {
565             /*
566             Cancel routine WILL be called after we release the spinlock. It will try to remove 
567             the irp from the list and cancel/complete this irp. Since we allready removed it, 
568             make its ListEntry point to itself.
569             */
570             InitializeListHead(&Irp->Tail.Overlay.ListEntry);
571          }
572          else
573          {
574             /*
575             Cancel routine will NOT be called, canceled or not.
576
577             Put on completed list and complete them all afterwards.
578             This way we don't have to restart enum after each completion.
579             */
580             Irp->IoStatus.Status = STATUS_SUCCESS;
581             Irp->IoStatus.Information = 0;
582             InsertHeadList(&LockToc->CompletedListHead,&Irp->Tail.Overlay.ListEntry);
583          }
584       }
585       EnumEntry = EnumEntry->Blink;
586    }
587
588    //complete irp's (if any)
589    while (!IsListEmpty(&LockToc->CompletedListHead)) 
590    {
591       EnumEntry = RemoveTailList(&LockToc->CompletedListHead);
592       KeReleaseSpinLock(&LockToc->SpinLock, *oldirql);//fires cancel routine
593       Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
594
595       if (FileLock->CompleteLockIrpRoutine)
596       {
597          FileLock->CompleteLockIrpRoutine(Irp->Tail.Overlay.DriverContext[2], Irp);
598       }
599       else
600       {
601          IofCompleteRequest(Irp, IO_NO_INCREMENT);
602       }
603
604       KeAcquireSpinLock(&LockToc->SpinLock, oldirql);
605    }
606
607 }
608
609
610
611 /**********************************************************************
612  * NAME                                                 PRIVATE
613  *      FsRtlpUnlockSingle
614  *
615  */
616 NTSTATUS
617 FASTCALL
618 FsRtlpUnlockSingle(
619    IN PFILE_LOCK           FileLock,
620    IN PFILE_OBJECT         FileObject,
621    IN PLARGE_INTEGER       FileOffset,
622    IN PLARGE_INTEGER       Length,
623    IN PEPROCESS            Process,
624    IN ULONG                Key,
625    IN PVOID                Context OPTIONAL,
626    IN BOOLEAN              AlreadySynchronized,
627    IN BOOLEAN              CallUnlockRoutine
628    )
629 {
630    KIRQL                oldirql;
631    PFILE_LOCK_TOC       LockToc;
632    PFILE_LOCK_GRANTED   Granted;
633    PLIST_ENTRY          EnumEntry;
634
635    assert(FileLock);
636    LockToc = FileLock->LockInformation;
637
638    if (LockToc == NULL || Length->QuadPart == 0)
639    {
640       return STATUS_RANGE_NOT_LOCKED;
641    }
642
643    KeAcquireSpinLock(&LockToc->SpinLock, &oldirql );
644
645    EnumEntry = LockToc->GrantedListHead.Flink;
646    while (EnumEntry != &LockToc->GrantedListHead) 
647    {
648       Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
649
650       //must be exact match
651       if (FileOffset->QuadPart == Granted->Lock.StartingByte.QuadPart &&
652          Length->QuadPart == Granted->Lock.Length.QuadPart &&
653          Granted->Lock.Process == Process &&
654          Granted->Lock.FileObject == FileObject &&
655          Granted->Lock.Key == Key) 
656       {
657          RemoveEntryList(&Granted->ListEntry);
658          FsRtlpCompletePendingLocks(FileLock, LockToc, &oldirql);
659
660          if (IsListEmpty(&LockToc->GrantedListHead))
661          {
662             KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
663             FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
664          }
665          else
666          {
667             KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
668          }
669
670          if (FileLock->UnlockRoutine && CallUnlockRoutine)
671          {
672             FileLock->UnlockRoutine(Context,&Granted->Lock);
673          }
674
675          ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);
676
677          return STATUS_SUCCESS;
678       }
679       EnumEntry = EnumEntry->Flink;
680    }
681
682    KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
683
684    return STATUS_RANGE_NOT_LOCKED;
685
686 }
687
688
689
690 /**********************************************************************
691  * NAME                                                 EXPORTED
692  *      FsRtlFastUnlockSingle
693  *
694  * @implemented
695  */
696 NTSTATUS
697 STDCALL
698 FsRtlFastUnlockSingle (
699    IN PFILE_LOCK           FileLock,
700    IN PFILE_OBJECT         FileObject,
701    IN PLARGE_INTEGER       FileOffset,
702    IN PLARGE_INTEGER       Length,
703    IN PEPROCESS            Process,
704    IN ULONG                Key,
705    IN PVOID                Context OPTIONAL,
706    IN BOOLEAN              AlreadySynchronized
707    )
708 {
709    return FsRtlpUnlockSingle( FileLock,
710                               FileObject,
711                               FileOffset,
712                               Length,
713                               Process,
714                               Key,
715                               Context,
716                               AlreadySynchronized,
717                               TRUE//CallUnlockRoutine
718                               );
719 }
720
721 /**********************************************************************
722  * NAME                                                 EXPORTED
723  *      FsRtlpDumpFileLocks
724  *
725  *      NOTE: used for testing and debugging
726  */
727 VOID
728 FASTCALL
729 FsRtlpDumpFileLocks(
730    IN PFILE_LOCK  FileLock
731    )
732 {
733    KIRQL                oldirql;
734    PFILE_LOCK_TOC       LockToc;
735    PFILE_LOCK_GRANTED   Granted;
736    PIRP                 Irp;
737    PLIST_ENTRY          EnumEntry;
738    PEXTENDED_IO_STACK_LOCATION   Stack;
739
740    assert(FileLock);
741    LockToc = FileLock->LockInformation;
742
743    if (LockToc == NULL) 
744    {
745       DPRINT1("No file locks\n");
746       return;
747    }
748
749    DPRINT1("Dumping granted file locks, FIFO order\n");
750
751    KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
752
753    EnumEntry = LockToc->GrantedListHead.Blink;
754    while ( EnumEntry != &LockToc->GrantedListHead)
755    {
756       Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED , ListEntry );
757
758       DPRINT1("%s, start: %i, len: %i, end: %i, key: %i, proc: 0x%X, fob: 0x%X\n",
759          Granted->Lock.ExclusiveLock ? "EXCL" : "SHRD",
760          Granted->Lock.StartingByte.QuadPart,
761          Granted->Lock.Length.QuadPart,
762          Granted->Lock.EndingByte.QuadPart,
763          Granted->Lock.Key,
764          Granted->Lock.Process,
765          Granted->Lock.FileObject
766          );
767
768       EnumEntry = EnumEntry->Blink;
769    }
770
771    DPRINT1("Dumping pending file locks, FIFO order\n");
772
773    EnumEntry = LockToc->PendingListHead.Blink;
774    while ( EnumEntry != &LockToc->PendingListHead)
775    {
776       Irp = CONTAINING_RECORD(EnumEntry, IRP , Tail.Overlay.ListEntry );
777
778       Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
779
780       DPRINT1("%s, start: %i, len: %i, end: %i, key: %i, proc: 0x%X, fob: 0x%X\n",
781          (Stack->Flags & SL_EXCLUSIVE_LOCK) ? "EXCL" : "SHRD",
782          Stack->Parameters.LockControl.ByteOffset.QuadPart,
783          Stack->Parameters.LockControl.Length->QuadPart,
784          Stack->Parameters.LockControl.ByteOffset.QuadPart + Stack->Parameters.LockControl.Length->QuadPart - 1,
785          Stack->Parameters.LockControl.Key,
786          IoGetRequestorProcess(Irp),
787          Stack->FileObject
788          );
789
790       EnumEntry = EnumEntry->Blink;
791    }
792
793    KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
794 }
795
796
797
798 /**********************************************************************
799  * NAME                                                 EXPORTED
800  *      FsRtlGetNextFileLock
801  *
802  * RETURN VALUE
803  *      NULL if no more locks.
804  *
805  * @implemented
806  */
807 PFILE_LOCK_INFO
808 STDCALL
809 FsRtlGetNextFileLock (
810    IN PFILE_LOCK   FileLock,
811    IN BOOLEAN      Restart
812    )
813 {
814    /*
815    Messy enumeration of granted locks.
816    What our last ptr. in LastReturnedLock points at, might have been freed between 
817    calls, so we have to scan thru the list every time, searching for our last lock.
818    If it's not there anymore, restart the enumeration...
819    */
820    KIRQL                oldirql;
821    PLIST_ENTRY          EnumEntry;
822    PFILE_LOCK_GRANTED   Granted;
823    PFILE_LOCK_TOC       LockToc;
824    BOOLEAN              FoundPrevious = FALSE;
825    //must make local copy since FILE_LOCK struct is allowed to be in paged mem
826    FILE_LOCK_INFO       LocalLastReturnedLockInfo;
827    PVOID                LocalLastReturnedLock;
828
829    assert(FileLock);
830    LockToc = FileLock->LockInformation;
831    if (LockToc == NULL)
832    {
833       return NULL;
834    }
835
836    LocalLastReturnedLock = FileLock->LastReturnedLock;
837
838    KeAcquireSpinLock(&LockToc->SpinLock,&oldirql);
839
840 restart:;
841
842    EnumEntry = LockToc->GrantedListHead.Flink;
843
844    if (Restart)
845    {
846       if (EnumEntry != &LockToc->GrantedListHead)
847       {
848          Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
849          LocalLastReturnedLockInfo = Granted->Lock;
850          KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
851
852          FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
853          FileLock->LastReturnedLock = EnumEntry;
854          return &FileLock->LastReturnedLockInfo;
855       }
856       else 
857       {
858          KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
859          return NULL;
860       }
861    }
862
863    //else: continue enum
864    while (EnumEntry != &LockToc->GrantedListHead) 
865    {
866       //found previous lock?
867       if (EnumEntry == LocalLastReturnedLock) 
868       {
869          FoundPrevious = TRUE;
870          //get next
871          EnumEntry = EnumEntry->Flink;
872          if (EnumEntry != &LockToc->GrantedListHead)
873          {
874             Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
875             LocalLastReturnedLockInfo = Granted->Lock;
876             KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
877
878             FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
879             FileLock->LastReturnedLock = EnumEntry;
880             return &FileLock->LastReturnedLockInfo;
881          }
882          break;
883       }
884       EnumEntry = EnumEntry->Flink;
885    }
886
887    if (!FoundPrevious) 
888    {
889       //got here? uh no, didn't find our last lock..must have been freed...restart
890       Restart = TRUE;
891       goto restart;
892    }
893
894    KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
895
896    return NULL;//no (more) locks
897 }
898
899
900 /**********************************************************************
901  * NAME                                                 EXPORTED
902  *      FsRtlInitializeFileLock
903  *
904  * NOTE
905  *  Called when creating/allocating/initializing FCB
906  *
907  * @implemented
908  */
909 VOID
910 STDCALL
911 FsRtlInitializeFileLock (
912    IN PFILE_LOCK                   FileLock,
913    IN PCOMPLETE_LOCK_IRP_ROUTINE   CompleteLockIrpRoutine OPTIONAL,
914    IN PUNLOCK_ROUTINE              UnlockRoutine OPTIONAL
915    )
916 {
917
918    FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
919    FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
920    FileLock->UnlockRoutine = UnlockRoutine;
921    FileLock->LockInformation = NULL;
922
923 }
924
925
926 /**********************************************************************
927  * NAME                                                 EXPORTED
928  *      FsRtlPrivateLock
929  *
930  * @implemented
931  */
932 BOOLEAN
933 STDCALL
934 FsRtlPrivateLock (
935    IN PFILE_LOCK           FileLock,
936    IN PFILE_OBJECT         FileObject,
937    IN PLARGE_INTEGER       FileOffset,
938    IN PLARGE_INTEGER       Length,
939    IN PEPROCESS            Process,
940    IN ULONG                Key,
941    IN BOOLEAN              FailImmediately, //seems meaningless for fast io
942    IN BOOLEAN              ExclusiveLock,
943    OUT PIO_STATUS_BLOCK    IoStatus,
944    IN PIRP                 Irp OPTIONAL,
945    IN PVOID                Context,
946    IN BOOLEAN              AlreadySynchronized
947    )
948 {
949    PFILE_LOCK_TOC       LockToc;
950    KIRQL                oldirql;
951
952    assert(FileLock);
953    if (FileLock->LockInformation == NULL) 
954    {
955       ExAcquireFastMutex(&LockTocMutex);
956       //still NULL?
957       if (FileLock->LockInformation == NULL)
958       {
959          FileLock->LockInformation = ExAllocateFromNPagedLookasideList(&LockTocLookaside);
960          LockToc =  FileLock->LockInformation;
961          KeInitializeSpinLock(&LockToc->SpinLock);
962          InitializeListHead(&LockToc->GrantedListHead);
963          InitializeListHead(&LockToc->PendingListHead);
964          InitializeListHead(&LockToc->CompletedListHead);
965          InitializeListHead(&LockToc->UnlockedListHead);
966       }
967       ExReleaseFastMutex(&LockTocMutex);
968    }
969
970    LockToc =  FileLock->LockInformation;
971    KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
972
973    //try add new lock (while holding spin lock)
974    if (FsRtlpAddLock(LockToc,
975                      FileObject,
976                      FileOffset,
977                      Length,
978                      Process,
979                      Key,
980                      ExclusiveLock
981                      ) ) 
982    {
983       IoStatus->Status = STATUS_SUCCESS;
984    }
985    else if (Irp && !FailImmediately) 
986    {    //failed + irp + no fail = mk. pending
987       //for our cancel routine
988       Irp->Tail.Overlay.DriverContext[0] = (PVOID)FileLock;
989       Irp->Tail.Overlay.DriverContext[1] = (PVOID)LockToc;
990       Irp->Tail.Overlay.DriverContext[2] = Context;
991
992       IoSetCancelRoutine(Irp, FsRtlpFileLockCancelRoutine);
993
994       if (Irp->Cancel) 
995       {
996          //irp canceled even before we got to queue it
997          if (IoSetCancelRoutine(Irp, NULL))
998          {  //Cancel routine will NOT be called: cancel it here
999             IoStatus->Status = STATUS_CANCELLED; 
1000          }
1001          else
1002          {  //Cancel routine WILL be called. When we release the lock it will complete the irp
1003             //Return pending since we are not completing the irp here
1004             Irp->IoStatus.Status = IoStatus->Status = STATUS_PENDING;
1005             Irp->IoStatus.Information = 0;
1006             InitializeListHead(&Irp->Tail.Overlay.ListEntry);
1007          }
1008          
1009       }
1010       else 
1011       {  //not cancelled: queue irp
1012          IoMarkIrpPending(Irp);
1013          Irp->IoStatus.Status = IoStatus->Status = STATUS_PENDING;
1014          Irp->IoStatus.Information = 0;
1015          InsertHeadList(&LockToc->PendingListHead,&Irp->Tail.Overlay.ListEntry);
1016       }
1017
1018    }
1019    else 
1020    {
1021       IoStatus->Status = STATUS_LOCK_NOT_GRANTED;
1022    }
1023
1024    KeReleaseSpinLock(&LockToc->SpinLock, oldirql);      //fires cancel routine
1025
1026    //never pending if no irp;-)
1027    assert(!(IoStatus->Status == STATUS_PENDING && !Irp));
1028
1029    if (IoStatus->Status != STATUS_PENDING) 
1030    {
1031       if (IoStatus->Status == STATUS_SUCCESS) 
1032       {
1033          FsRtlAreThereCurrentFileLocks(FileLock) = TRUE;
1034       }
1035
1036       if (Irp) 
1037       {
1038          Irp->IoStatus.Status = IoStatus->Status;
1039          Irp->IoStatus.Information = 0;
1040
1041          if (FileLock->CompleteLockIrpRoutine) 
1042          { //complete irp routine
1043
1044             if (!NT_SUCCESS(FileLock->CompleteLockIrpRoutine(Context,Irp))) 
1045             {
1046                //CompleteLockIrpRoutine complain: revert changes
1047                FsRtlpUnlockSingle(  FileLock,
1048                                     FileObject,
1049                                     FileOffset,
1050                                     Length,
1051                                     Process,
1052                                     Key,
1053                                     Context,
1054                                     AlreadySynchronized,
1055                                     FALSE//CallUnlockRoutine
1056                                     );
1057             }
1058          }
1059          else 
1060          {//std irp completion
1061             IofCompleteRequest(Irp, IO_NO_INCREMENT);
1062          }
1063       }
1064    }
1065
1066    //NOTE: only fast io seems to care about this return value
1067    return (IoStatus->Status == STATUS_SUCCESS || FailImmediately);
1068
1069 }
1070
1071
1072
1073 /**********************************************************************
1074  * NAME                                                 EXPORTED
1075  *      FsRtlProcessFileLock
1076  *
1077  * @implemented
1078  */
1079 NTSTATUS
1080 STDCALL
1081 FsRtlProcessFileLock (
1082    IN PFILE_LOCK   FileLock,
1083    IN PIRP         Irp,
1084    IN PVOID        Context OPTIONAL
1085    )
1086 {
1087    PEXTENDED_IO_STACK_LOCATION   Stack;
1088    NTSTATUS             Status;
1089    IO_STATUS_BLOCK      LocalIoStatus;
1090
1091    assert(FileLock);
1092    Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
1093    Irp->IoStatus.Information = 0;
1094
1095    switch(Stack->MinorFunction)
1096    {
1097       case IRP_MN_LOCK:
1098          //ret: BOOLEAN
1099          FsRtlPrivateLock( FileLock,
1100                            Stack->FileObject,
1101                            &Stack->Parameters.LockControl.ByteOffset, //not pointer!
1102                            Stack->Parameters.LockControl.Length,
1103                            IoGetRequestorProcess(Irp),
1104                            Stack->Parameters.LockControl.Key,
1105                            Stack->Flags & SL_FAIL_IMMEDIATELY,
1106                            Stack->Flags & SL_EXCLUSIVE_LOCK,
1107                            &LocalIoStatus,
1108                            Irp,
1109                            Context,
1110                            FALSE);
1111
1112          return LocalIoStatus.Status;
1113
1114       case IRP_MN_UNLOCK_SINGLE:
1115          Status = FsRtlFastUnlockSingle ( FileLock,
1116                                           Stack->FileObject,
1117                                           &Stack->Parameters.LockControl.ByteOffset,
1118                                           Stack->Parameters.LockControl.Length,
1119                                           IoGetRequestorProcess(Irp),
1120                                           Stack->Parameters.LockControl.Key,
1121                                           Context,
1122                                           FALSE);
1123          break;
1124
1125       case IRP_MN_UNLOCK_ALL:
1126          Status = FsRtlFastUnlockAll(  FileLock,
1127                                        Stack->FileObject,
1128                                        IoGetRequestorProcess(Irp),
1129                                        Context);
1130          break;
1131
1132       case IRP_MN_UNLOCK_ALL_BY_KEY:
1133          Status = FsRtlFastUnlockAllByKey (  FileLock,
1134                                              Stack->FileObject,
1135                                              IoGetRequestorProcess(Irp),
1136                                              Stack->Parameters.LockControl.Key,
1137                                              Context);
1138
1139          break;
1140
1141       default:
1142          Irp->IoStatus.Status = Status = STATUS_INVALID_DEVICE_REQUEST;
1143          IofCompleteRequest(Irp, IO_NO_INCREMENT);
1144          return Status;
1145    }
1146
1147    Irp->IoStatus.Status = Status;
1148
1149    if (FileLock->CompleteLockIrpRoutine )
1150    {
1151       FileLock->CompleteLockIrpRoutine(Context,Irp);
1152    }
1153    else
1154    {
1155       IofCompleteRequest(Irp,IO_NO_INCREMENT);
1156    }
1157
1158    return Status;
1159 }
1160
1161
1162 /**********************************************************************
1163  * NAME                                                 EXPORTED
1164  *      FsRtlUninitializeFileLock
1165  *
1166  * @implemented
1167  */
1168 VOID
1169 STDCALL
1170 FsRtlUninitializeFileLock (
1171    IN PFILE_LOCK FileLock
1172    )
1173 {
1174    PFILE_LOCK_TOC       LockToc;
1175    PIRP                 Irp;
1176    PFILE_LOCK_GRANTED   Granted;
1177    PLIST_ENTRY          EnumEntry;
1178    KIRQL                oldirql;
1179
1180    assert(FileLock);
1181    if (FileLock->LockInformation == NULL)
1182    {
1183       return;
1184    }
1185
1186    LockToc = FileLock->LockInformation;
1187
1188    KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
1189
1190    //remove and free granted locks
1191    while (!IsListEmpty(&LockToc->GrantedListHead)) 
1192    {
1193       EnumEntry = RemoveTailList(&LockToc->GrantedListHead);
1194       Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED, ListEntry);
1195       ExFreeToNPagedLookasideList(&GrantedLookaside, Granted);
1196    }
1197
1198    //remove, complete and free all pending locks
1199    while (!IsListEmpty(&LockToc->PendingListHead)) 
1200    {
1201       EnumEntry = RemoveTailList(&LockToc->PendingListHead);
1202       Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
1203
1204       if (!IoSetCancelRoutine(Irp, NULL))
1205       {  
1206          //The cancel routine will be called. When we release the lock it will complete the irp.
1207          InitializeListHead(&Irp->Tail.Overlay.ListEntry);
1208       }
1209       else
1210       {
1211          /*
1212          Cancel routine will NOT be called, even though the irp might have been canceled.
1213          Don't care since we'l complete it faster than the cancel routine would have.
1214          */
1215          KeReleaseSpinLock(&LockToc->SpinLock, oldirql);//fires cancel routine  
1216
1217          Irp->IoStatus.Status = STATUS_RANGE_NOT_LOCKED;
1218    
1219          if (FileLock->CompleteLockIrpRoutine)
1220          {
1221             FileLock->CompleteLockIrpRoutine(Irp->Tail.Overlay.DriverContext[2], Irp);
1222          }
1223          else
1224          {
1225             IofCompleteRequest(Irp, IO_NO_INCREMENT);
1226          }
1227
1228          KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
1229       }
1230    }
1231
1232    KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
1233
1234    ExFreeToNPagedLookasideList(&LockTocLookaside, LockToc);
1235
1236    FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
1237    FileLock->LockInformation = NULL;
1238
1239 }
1240
1241
1242 /**********************************************************************
1243  * NAME                                                 EXPORTED
1244  *      FsRtlAllocateFileLock
1245  *
1246  * NOTE
1247  *      Only present in NT 5.0 or later.
1248  *      FCB FILE_LOCK struct should/is acording to DDK allocated from paged pool!
1249  *
1250  * @implemented
1251  */
1252 PFILE_LOCK
1253 STDCALL
1254 FsRtlAllocateFileLock(
1255    IN PCOMPLETE_LOCK_IRP_ROUTINE    CompleteLockIrpRoutine OPTIONAL,
1256    IN PUNLOCK_ROUTINE               UnlockRoutine OPTIONAL
1257    )
1258 {
1259    PFILE_LOCK  FileLock;
1260
1261    FileLock = ExAllocateFromPagedLookasideList(&LockLookaside);
1262
1263    FsRtlInitializeFileLock(FileLock,
1264                            CompleteLockIrpRoutine,
1265                            UnlockRoutine
1266                            );
1267
1268    return FileLock;
1269 }
1270
1271 /**********************************************************************
1272  * NAME                                                 EXPORTED
1273  *      FsRtlFreeFileLock
1274  *
1275  * NOTE
1276  *      Only present in NT 5.0 or later.
1277  *      FCB FILE_LOCK struct should/is acording to DDK allocated from paged pool!
1278  *
1279  * @implemented
1280  */
1281 VOID
1282 STDCALL
1283 FsRtlFreeFileLock(
1284    IN PFILE_LOCK FileLock
1285    )
1286 {
1287    assert(FileLock);
1288
1289    FsRtlUninitializeFileLock(FileLock);
1290    ExFreeToPagedLookasideList(&LockLookaside, FileLock);
1291 }
1292
1293 /* EOF */