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