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