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