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