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