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