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