W32API conversion update
[reactos.git] / reactos / ntoskrnl / fs / filelock.c
1 /* $Id: filelock.c,v 1.10 2003/08/07 11:47:33 silverblade 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 PEXTENDED_IO_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 = (PEXTENDED_IO_STACK_LOCATION) 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 VOID
727 FASTCALL
728 FsRtlpDumpFileLocks(
729 IN PFILE_LOCK FileLock
730 )
731 {
732 KIRQL oldirql;
733 PFILE_LOCK_TOC LockToc;
734 PFILE_LOCK_GRANTED Granted;
735 PIRP Irp;
736 PLIST_ENTRY EnumEntry;
737 PEXTENDED_IO_STACK_LOCATION Stack;
738
739 assert(FileLock);
740 LockToc = FileLock->LockInformation;
741
742 if (LockToc == NULL)
743 {
744 DPRINT1("No file locks\n");
745 return;
746 }
747
748 DPRINT1("Dumping granted file locks, FIFO order\n");
749
750 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
751
752 EnumEntry = LockToc->GrantedListHead.Blink;
753 while ( EnumEntry != &LockToc->GrantedListHead)
754 {
755 Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED , ListEntry );
756
757 DPRINT1("%s, start: %i, len: %i, end: %i, key: %i, proc: 0x%X, fob: 0x%X\n",
758 Granted->Lock.ExclusiveLock ? "EXCL" : "SHRD",
759 Granted->Lock.StartingByte.QuadPart,
760 Granted->Lock.Length.QuadPart,
761 Granted->Lock.EndingByte.QuadPart,
762 Granted->Lock.Key,
763 Granted->Lock.Process,
764 Granted->Lock.FileObject
765 );
766
767 EnumEntry = EnumEntry->Blink;
768 }
769
770 DPRINT1("Dumping pending file locks, FIFO order\n");
771
772 EnumEntry = LockToc->PendingListHead.Blink;
773 while ( EnumEntry != &LockToc->PendingListHead)
774 {
775 Irp = CONTAINING_RECORD(EnumEntry, IRP , Tail.Overlay.ListEntry );
776
777 Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
778
779 DPRINT1("%s, start: %i, len: %i, end: %i, key: %i, proc: 0x%X, fob: 0x%X\n",
780 (Stack->Flags & SL_EXCLUSIVE_LOCK) ? "EXCL" : "SHRD",
781 Stack->Parameters.LockControl.ByteOffset.QuadPart,
782 Stack->Parameters.LockControl.Length->QuadPart,
783 Stack->Parameters.LockControl.ByteOffset.QuadPart + Stack->Parameters.LockControl.Length->QuadPart - 1,
784 Stack->Parameters.LockControl.Key,
785 IoGetRequestorProcess(Irp),
786 Stack->FileObject
787 );
788
789 EnumEntry = EnumEntry->Blink;
790 }
791
792 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
793 }
794
795
796
797 /**********************************************************************
798 * NAME EXPORTED
799 * FsRtlGetNextFileLock
800 *
801 * RETURN VALUE
802 * NULL if no more locks.
803 *
804 * @implemented
805 */
806 PFILE_LOCK_INFO
807 STDCALL
808 FsRtlGetNextFileLock (
809 IN PFILE_LOCK FileLock,
810 IN BOOLEAN Restart
811 )
812 {
813 /*
814 Messy enumeration of granted locks.
815 What our last ptr. in LastReturnedLock points at, might have been freed between
816 calls, so we have to scan thru the list every time, searching for our last lock.
817 If it's not there anymore, restart the enumeration...
818 */
819 KIRQL oldirql;
820 PLIST_ENTRY EnumEntry;
821 PFILE_LOCK_GRANTED Granted;
822 PFILE_LOCK_TOC LockToc;
823 BOOLEAN FoundPrevious = FALSE;
824 //must make local copy since FILE_LOCK struct is allowed to be in paged mem
825 FILE_LOCK_INFO LocalLastReturnedLockInfo;
826 PVOID LocalLastReturnedLock;
827
828 assert(FileLock);
829 LockToc = FileLock->LockInformation;
830 if (LockToc == NULL)
831 {
832 return NULL;
833 }
834
835 LocalLastReturnedLock = FileLock->LastReturnedLock;
836
837 KeAcquireSpinLock(&LockToc->SpinLock,&oldirql);
838
839 restart:;
840
841 EnumEntry = LockToc->GrantedListHead.Flink;
842
843 if (Restart)
844 {
845 if (EnumEntry != &LockToc->GrantedListHead)
846 {
847 Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
848 LocalLastReturnedLockInfo = Granted->Lock;
849 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
850
851 FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
852 FileLock->LastReturnedLock = EnumEntry;
853 return &FileLock->LastReturnedLockInfo;
854 }
855 else
856 {
857 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
858 return NULL;
859 }
860 }
861
862 //else: continue enum
863 while (EnumEntry != &LockToc->GrantedListHead)
864 {
865 //found previous lock?
866 if (EnumEntry == LocalLastReturnedLock)
867 {
868 FoundPrevious = TRUE;
869 //get next
870 EnumEntry = EnumEntry->Flink;
871 if (EnumEntry != &LockToc->GrantedListHead)
872 {
873 Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
874 LocalLastReturnedLockInfo = Granted->Lock;
875 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
876
877 FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
878 FileLock->LastReturnedLock = EnumEntry;
879 return &FileLock->LastReturnedLockInfo;
880 }
881 break;
882 }
883 EnumEntry = EnumEntry->Flink;
884 }
885
886 if (!FoundPrevious)
887 {
888 //got here? uh no, didn't find our last lock..must have been freed...restart
889 Restart = TRUE;
890 goto restart;
891 }
892
893 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
894
895 return NULL;//no (more) locks
896 }
897
898
899 /**********************************************************************
900 * NAME EXPORTED
901 * FsRtlInitializeFileLock
902 *
903 * NOTE
904 * Called when creating/allocating/initializing FCB
905 *
906 * @implemented
907 */
908 VOID
909 STDCALL
910 FsRtlInitializeFileLock (
911 IN PFILE_LOCK FileLock,
912 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
913 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
914 )
915 {
916
917 FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
918 FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
919 FileLock->UnlockRoutine = UnlockRoutine;
920 FileLock->LockInformation = NULL;
921
922 }
923
924
925 /**********************************************************************
926 * NAME EXPORTED
927 * FsRtlPrivateLock
928 *
929 * @implemented
930 */
931 BOOLEAN
932 STDCALL
933 FsRtlPrivateLock (
934 IN PFILE_LOCK FileLock,
935 IN PFILE_OBJECT FileObject,
936 IN PLARGE_INTEGER FileOffset,
937 IN PLARGE_INTEGER Length,
938 IN PEPROCESS Process,
939 IN ULONG Key,
940 IN BOOLEAN FailImmediately, //seems meaningless for fast io
941 IN BOOLEAN ExclusiveLock,
942 OUT PIO_STATUS_BLOCK IoStatus,
943 IN PIRP Irp OPTIONAL,
944 IN PVOID Context,
945 IN BOOLEAN AlreadySynchronized
946 )
947 {
948 PFILE_LOCK_TOC LockToc;
949 KIRQL oldirql;
950
951 assert(FileLock);
952 if (FileLock->LockInformation == NULL)
953 {
954 ExAcquireFastMutex(&LockTocMutex);
955 //still NULL?
956 if (FileLock->LockInformation == NULL)
957 {
958 FileLock->LockInformation = ExAllocateFromNPagedLookasideList(&LockTocLookaside);
959 LockToc = FileLock->LockInformation;
960 KeInitializeSpinLock(&LockToc->SpinLock);
961 InitializeListHead(&LockToc->GrantedListHead);
962 InitializeListHead(&LockToc->PendingListHead);
963 InitializeListHead(&LockToc->CompletedListHead);
964 InitializeListHead(&LockToc->UnlockedListHead);
965 }
966 ExReleaseFastMutex(&LockTocMutex);
967 }
968
969 LockToc = FileLock->LockInformation;
970 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
971
972 //try add new lock (while holding spin lock)
973 if (FsRtlpAddLock(LockToc,
974 FileObject,
975 FileOffset,
976 Length,
977 Process,
978 Key,
979 ExclusiveLock
980 ) )
981 {
982 IoStatus->Status = STATUS_SUCCESS;
983 }
984 else if (Irp && !FailImmediately)
985 { //failed + irp + no fail = mk. pending
986 //for our cancel routine
987 Irp->Tail.Overlay.DriverContext[0] = (PVOID)FileLock;
988 Irp->Tail.Overlay.DriverContext[1] = (PVOID)LockToc;
989 Irp->Tail.Overlay.DriverContext[2] = Context;
990
991 IoSetCancelRoutine(Irp, FsRtlpFileLockCancelRoutine);
992
993 if (Irp->Cancel)
994 {
995 //irp canceled even before we got to queue it
996 if (IoSetCancelRoutine(Irp, NULL))
997 { //Cancel routine will NOT be called: cancel it here
998 IoStatus->Status = STATUS_CANCELLED;
999 }
1000 else
1001 { //Cancel routine WILL be called. When we release the lock it will complete the irp
1002 //Return pending since we are not completing the irp here
1003 Irp->IoStatus.Status = IoStatus->Status = STATUS_PENDING;
1004 Irp->IoStatus.Information = 0;
1005 InitializeListHead(&Irp->Tail.Overlay.ListEntry);
1006 }
1007
1008 }
1009 else
1010 { //not cancelled: queue irp
1011 IoMarkIrpPending(Irp);
1012 Irp->IoStatus.Status = IoStatus->Status = STATUS_PENDING;
1013 Irp->IoStatus.Information = 0;
1014 InsertHeadList(&LockToc->PendingListHead,&Irp->Tail.Overlay.ListEntry);
1015 }
1016
1017 }
1018 else
1019 {
1020 IoStatus->Status = STATUS_LOCK_NOT_GRANTED;
1021 }
1022
1023 KeReleaseSpinLock(&LockToc->SpinLock, oldirql); //fires cancel routine
1024
1025 //never pending if no irp;-)
1026 assert(!(IoStatus->Status == STATUS_PENDING && !Irp));
1027
1028 if (IoStatus->Status != STATUS_PENDING)
1029 {
1030 if (IoStatus->Status == STATUS_SUCCESS)
1031 {
1032 FsRtlAreThereCurrentFileLocks(FileLock) = TRUE;
1033 }
1034
1035 if (Irp)
1036 {
1037 Irp->IoStatus.Status = IoStatus->Status;
1038 Irp->IoStatus.Information = 0;
1039
1040 if (FileLock->CompleteLockIrpRoutine)
1041 { //complete irp routine
1042
1043 if (!NT_SUCCESS(FileLock->CompleteLockIrpRoutine(Context,Irp)))
1044 {
1045 //CompleteLockIrpRoutine complain: revert changes
1046 FsRtlpUnlockSingle( FileLock,
1047 FileObject,
1048 FileOffset,
1049 Length,
1050 Process,
1051 Key,
1052 Context,
1053 AlreadySynchronized,
1054 FALSE//CallUnlockRoutine
1055 );
1056 }
1057 }
1058 else
1059 {//std irp completion
1060 IofCompleteRequest(Irp, IO_NO_INCREMENT);
1061 }
1062 }
1063 }
1064
1065 //NOTE: only fast io seems to care about this return value
1066 return (IoStatus->Status == STATUS_SUCCESS || FailImmediately);
1067
1068 }
1069
1070
1071
1072 /**********************************************************************
1073 * NAME EXPORTED
1074 * FsRtlProcessFileLock
1075 *
1076 * @implemented
1077 */
1078 NTSTATUS
1079 STDCALL
1080 FsRtlProcessFileLock (
1081 IN PFILE_LOCK FileLock,
1082 IN PIRP Irp,
1083 IN PVOID Context OPTIONAL
1084 )
1085 {
1086 PEXTENDED_IO_STACK_LOCATION Stack;
1087 NTSTATUS Status;
1088 IO_STATUS_BLOCK LocalIoStatus;
1089
1090 assert(FileLock);
1091 Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
1092 Irp->IoStatus.Information = 0;
1093
1094 switch(Stack->MinorFunction)
1095 {
1096 case IRP_MN_LOCK:
1097 //ret: BOOLEAN
1098 FsRtlPrivateLock( FileLock,
1099 Stack->FileObject,
1100 &Stack->Parameters.LockControl.ByteOffset, //not pointer!
1101 Stack->Parameters.LockControl.Length,
1102 IoGetRequestorProcess(Irp),
1103 Stack->Parameters.LockControl.Key,
1104 Stack->Flags & SL_FAIL_IMMEDIATELY,
1105 Stack->Flags & SL_EXCLUSIVE_LOCK,
1106 &LocalIoStatus,
1107 Irp,
1108 Context,
1109 FALSE);
1110
1111 return LocalIoStatus.Status;
1112
1113 case IRP_MN_UNLOCK_SINGLE:
1114 Status = FsRtlFastUnlockSingle ( FileLock,
1115 Stack->FileObject,
1116 &Stack->Parameters.LockControl.ByteOffset,
1117 Stack->Parameters.LockControl.Length,
1118 IoGetRequestorProcess(Irp),
1119 Stack->Parameters.LockControl.Key,
1120 Context,
1121 FALSE);
1122 break;
1123
1124 case IRP_MN_UNLOCK_ALL:
1125 Status = FsRtlFastUnlockAll( FileLock,
1126 Stack->FileObject,
1127 IoGetRequestorProcess(Irp),
1128 Context);
1129 break;
1130
1131 case IRP_MN_UNLOCK_ALL_BY_KEY:
1132 Status = FsRtlFastUnlockAllByKey ( FileLock,
1133 Stack->FileObject,
1134 IoGetRequestorProcess(Irp),
1135 Stack->Parameters.LockControl.Key,
1136 Context);
1137
1138 break;
1139
1140 default:
1141 Irp->IoStatus.Status = Status = STATUS_INVALID_DEVICE_REQUEST;
1142 IofCompleteRequest(Irp, IO_NO_INCREMENT);
1143 return Status;
1144 }
1145
1146 Irp->IoStatus.Status = Status;
1147
1148 if (FileLock->CompleteLockIrpRoutine )
1149 {
1150 FileLock->CompleteLockIrpRoutine(Context,Irp);
1151 }
1152 else
1153 {
1154 IofCompleteRequest(Irp,IO_NO_INCREMENT);
1155 }
1156
1157 return Status;
1158 }
1159
1160
1161 /**********************************************************************
1162 * NAME EXPORTED
1163 * FsRtlUninitializeFileLock
1164 *
1165 * @implemented
1166 */
1167 VOID
1168 STDCALL
1169 FsRtlUninitializeFileLock (
1170 IN PFILE_LOCK FileLock
1171 )
1172 {
1173 PFILE_LOCK_TOC LockToc;
1174 PIRP Irp;
1175 PFILE_LOCK_GRANTED Granted;
1176 PLIST_ENTRY EnumEntry;
1177 KIRQL oldirql;
1178
1179 assert(FileLock);
1180 if (FileLock->LockInformation == NULL)
1181 {
1182 return;
1183 }
1184
1185 LockToc = FileLock->LockInformation;
1186
1187 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
1188
1189 //remove and free granted locks
1190 while (!IsListEmpty(&LockToc->GrantedListHead))
1191 {
1192 EnumEntry = RemoveTailList(&LockToc->GrantedListHead);
1193 Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED, ListEntry);
1194 ExFreeToNPagedLookasideList(&GrantedLookaside, Granted);
1195 }
1196
1197 //remove, complete and free all pending locks
1198 while (!IsListEmpty(&LockToc->PendingListHead))
1199 {
1200 EnumEntry = RemoveTailList(&LockToc->PendingListHead);
1201 Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
1202
1203 if (!IoSetCancelRoutine(Irp, NULL))
1204 {
1205 //The cancel routine will be called. When we release the lock it will complete the irp.
1206 InitializeListHead(&Irp->Tail.Overlay.ListEntry);
1207 }
1208 else
1209 {
1210 /*
1211 Cancel routine will NOT be called, even though the irp might have been canceled.
1212 Don't care since we'l complete it faster than the cancel routine would have.
1213 */
1214 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);//fires cancel routine
1215
1216 Irp->IoStatus.Status = STATUS_RANGE_NOT_LOCKED;
1217
1218 if (FileLock->CompleteLockIrpRoutine)
1219 {
1220 FileLock->CompleteLockIrpRoutine(Irp->Tail.Overlay.DriverContext[2], Irp);
1221 }
1222 else
1223 {
1224 IofCompleteRequest(Irp, IO_NO_INCREMENT);
1225 }
1226
1227 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
1228 }
1229 }
1230
1231 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
1232
1233 ExFreeToNPagedLookasideList(&LockTocLookaside, LockToc);
1234
1235 FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
1236 FileLock->LockInformation = NULL;
1237
1238 }
1239
1240
1241 /**********************************************************************
1242 * NAME EXPORTED
1243 * FsRtlAllocateFileLock
1244 *
1245 * NOTE
1246 * Only present in NT 5.0 or later.
1247 * FCB FILE_LOCK struct should/is acording to DDK allocated from paged pool!
1248 *
1249 * @implemented
1250 */
1251 PFILE_LOCK
1252 STDCALL
1253 FsRtlAllocateFileLock(
1254 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1255 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
1256 )
1257 {
1258 PFILE_LOCK FileLock;
1259
1260 FileLock = ExAllocateFromPagedLookasideList(&LockLookaside);
1261
1262 FsRtlInitializeFileLock(FileLock,
1263 CompleteLockIrpRoutine,
1264 UnlockRoutine
1265 );
1266
1267 return FileLock;
1268 }
1269
1270 /**********************************************************************
1271 * NAME EXPORTED
1272 * FsRtlFreeFileLock
1273 *
1274 * NOTE
1275 * Only present in NT 5.0 or later.
1276 * FCB FILE_LOCK struct should/is acording to DDK allocated from paged pool!
1277 *
1278 * @implemented
1279 */
1280 VOID
1281 STDCALL
1282 FsRtlFreeFileLock(
1283 IN PFILE_LOCK FileLock
1284 )
1285 {
1286 assert(FileLock);
1287
1288 FsRtlUninitializeFileLock(FileLock);
1289 ExFreeToPagedLookasideList(&LockLookaside, FileLock);
1290 }
1291
1292 /* EOF */