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