Merge 25584, 25588.
[reactos.git] / reactos / ntoskrnl / fs / filelock.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/fs/filelock.c
6 * PURPOSE: No purpose listed.
7 *
8 * PROGRAMMERS: No programmer listed.
9 */
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <internal/debug.h>
14
15 /*
16 NOTE:
17 I'm not using resource syncronization here, since FsRtlFastCheckLockForRead/Write
18 are allowed to be called at DISPATCH_LEVEL. Must therefore use nonpaged memory for
19 the lists.
20 UPDATE: I'm not sure about this! -Gunnar
21 */
22
23 FAST_MUTEX LockTocMutex;
24 NPAGED_LOOKASIDE_LIST GrantedLookaside;
25 NPAGED_LOOKASIDE_LIST LockTocLookaside;
26 PAGED_LOOKASIDE_LIST LockLookaside;
27
28 __inline BOOLEAN
29 IsOverlappingLock(PFILE_LOCK_INFO Lock,
30 PLARGE_INTEGER StartOffset,
31 PLARGE_INTEGER EndOffset)
32 {
33 if ((ULONGLONG)StartOffset->QuadPart > (ULONGLONG)Lock->EndingByte.QuadPart)
34 {
35 return FALSE;
36 }
37
38 if ((ULONGLONG)EndOffset->QuadPart < (ULONGLONG)Lock->StartingByte.QuadPart)
39 {
40 return FALSE;
41 }
42
43 return TRUE;
44 }
45
46 __inline BOOLEAN
47 IsSurroundingLock(PFILE_LOCK_INFO Lock,
48 PLARGE_INTEGER StartOffset,
49 PLARGE_INTEGER EndOffset)
50 {
51 if ((ULONGLONG)StartOffset->QuadPart >= (ULONGLONG)Lock->StartingByte.QuadPart &&
52 (ULONGLONG)EndOffset->QuadPart <= (ULONGLONG)Lock->EndingByte.QuadPart)
53 {
54 return TRUE;
55 }
56
57 return FALSE;
58 }
59
60 VOID
61 NTAPI
62 FsRtlInitSystem(VOID)
63 {
64 /* Initialize the list for all lock information structures */
65 ExInitializeNPagedLookasideList(&LockTocLookaside,
66 NULL,
67 NULL,
68 0,
69 sizeof(FILE_LOCK_TOC),
70 IFS_POOL_TAG,
71 0);
72
73 /* Initialize the list for granted locks */
74 ExInitializeNPagedLookasideList(&GrantedLookaside,
75 NULL,
76 NULL,
77 0,
78 sizeof(FILE_LOCK_GRANTED),
79 IFS_POOL_TAG,
80 0);
81
82 /* Initialize the list for lock allocations */
83 ExInitializePagedLookasideList(&LockLookaside,
84 NULL,
85 NULL,
86 0,
87 sizeof(FILE_LOCK),
88 IFS_POOL_TAG,
89 0);
90
91 /* Initialize the lock information mutex */
92 ExInitializeFastMutex(&LockTocMutex);
93 }
94
95 VOID
96 NTAPI
97 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine,
98 IN PVOID Context,
99 IN PIRP Irp,
100 IN NTSTATUS Status,
101 OUT PNTSTATUS NewStatus,
102 IN PFILE_OBJECT FileObject OPTIONAL)
103 {
104 /* Check if we have a complete routine */
105 if (CompleteRoutine)
106 {
107 /* Check if we have a file object */
108 if (FileObject) FileObject->LastLock = NULL;
109
110 /* Set the I/O Status and do completion */
111 Irp->IoStatus.Status = Status;
112 *NewStatus = CompleteRoutine(Context, Irp);
113 }
114 else
115 {
116 /* Otherwise do a normal I/O complete request */
117 FsRtlCompleteRequest(Irp, Status);
118 *NewStatus = Status;
119 }
120 }
121
122 VOID
123 STDCALL
124 FsRtlpFileLockCancelRoutine(IN PDEVICE_OBJECT DeviceObject,
125 IN PIRP Irp)
126 {
127 KIRQL oldIrql;
128 PKSPIN_LOCK SpinLock;
129
130 //don't need this since we have our own sync. protecting irp cancellation
131 IoReleaseCancelSpinLock(Irp->CancelIrql);
132
133 SpinLock = Irp->Tail.Overlay.DriverContext[3];
134
135 KeAcquireSpinLock(SpinLock, &oldIrql);
136
137 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
138
139 KeReleaseSpinLock(SpinLock, oldIrql);
140
141 Irp->IoStatus.Status = STATUS_CANCELLED;
142 Irp->IoStatus.Information = 0;
143
144 IoCompleteRequest(Irp, IO_NO_INCREMENT);
145 }
146
147 BOOLEAN
148 FASTCALL
149 FsRtlpCheckLockForReadOrWriteAccess(IN PFILE_LOCK FileLock,
150 IN PLARGE_INTEGER FileOffset,
151 IN PLARGE_INTEGER Length,
152 IN ULONG Key,
153 IN PFILE_OBJECT FileObject,
154 IN PEPROCESS Process,
155 IN BOOLEAN Read)
156 {
157 KIRQL oldirql;
158 PFILE_LOCK_TOC LockToc;
159 PFILE_LOCK_GRANTED Granted;
160 LARGE_INTEGER EndOffset;
161
162 ASSERT(FileLock);
163
164 LockToc = FileLock->LockInformation;
165
166 if (LockToc == NULL || Length->QuadPart == 0)
167 {
168 return TRUE;
169 }
170
171 EndOffset.QuadPart = FileOffset->QuadPart + Length->QuadPart - 1;
172
173 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
174
175 LIST_FOR_EACH(Granted, &LockToc->GrantedListHead, FILE_LOCK_GRANTED, ListEntry)
176 {
177 //if overlapping
178 if(IsOverlappingLock(&Granted->Lock, FileOffset, &EndOffset))
179 {
180 //No read conflict if (shared lock) OR (exclusive + our lock)
181 //No write conflict if exclusive lock AND our lock
182 if ((Read && !Granted->Lock.ExclusiveLock) ||
183 (Granted->Lock.ExclusiveLock &&
184 Granted->Lock.ProcessId == Process &&
185 Granted->Lock.FileObject == FileObject &&
186 Granted->Lock.Key == Key ) )
187 {
188 //AND if lock surround request region, stop searching and grant
189 if (IsSurroundingLock(&Granted->Lock, FileOffset, &EndOffset) )
190 {
191 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
192 return TRUE;
193 }
194
195 //else continue searching for conflicts
196 continue;
197 }
198
199 //found conflict
200 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
201 return FALSE;
202 }
203
204 }
205
206 //no conflict
207 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
208 return TRUE;
209 }
210
211 NTSTATUS
212 FASTCALL
213 FsRtlpFastUnlockAllByKey(IN PFILE_LOCK FileLock,
214 IN PFILE_OBJECT FileObject,
215 IN PEPROCESS Process,
216 IN ULONG Key,
217 IN BOOLEAN UseKey,
218 IN PVOID Context OPTIONAL)
219 {
220 KIRQL oldirql;
221 PFILE_LOCK_TOC LockToc;
222 PFILE_LOCK_GRANTED Granted, tmp;
223 BOOLEAN Unlock = FALSE;
224 //must make local copy since FILE_LOCK struct is allowed to be paged
225 BOOLEAN GotUnlockRoutine;
226 LIST_ENTRY UnlockedListHead;
227 PLIST_ENTRY EnumEntry;
228
229 ASSERT(FileLock);
230 LockToc = FileLock->LockInformation;
231
232 if (LockToc == NULL)
233 {
234 return STATUS_RANGE_NOT_LOCKED;
235 }
236
237 InitializeListHead(&UnlockedListHead);
238 GotUnlockRoutine = FileLock->UnlockRoutine != NULL;
239 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
240
241 LIST_FOR_EACH_SAFE(Granted, tmp, &LockToc->GrantedListHead, FILE_LOCK_GRANTED, ListEntry)
242 {
243
244 if (Granted->Lock.ProcessId == Process &&
245 Granted->Lock.FileObject == FileObject &&
246 (!UseKey || (UseKey && Granted->Lock.Key == Key)) )
247 {
248 RemoveEntryList(&Granted->ListEntry);
249 Unlock = TRUE;
250
251 if (GotUnlockRoutine)
252 {
253 /*
254 Put on unlocked list and call unlock routine for them afterwards.
255 This way we don't have to restart enum after each call
256 */
257 InsertHeadList(&UnlockedListHead,&Granted->ListEntry);
258 }
259 else
260 {
261 ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);
262 }
263 }
264 }
265
266 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
267
268 if (Unlock)
269 {
270 //call unlock routine for each unlocked lock (if any)
271 while (!IsListEmpty(&UnlockedListHead))
272 {
273 EnumEntry = RemoveTailList(&UnlockedListHead);
274 Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED, ListEntry);
275
276 FileLock->UnlockRoutine(Granted->UnlockContext, &Granted->Lock);
277 ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);
278 }
279
280 //NOTE: holding spinlock while calling this
281 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
282 FsRtlpCompletePendingLocks(FileLock, LockToc, &oldirql, Context);
283
284 if (IsListEmpty(&LockToc->GrantedListHead))
285 {
286 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
287 FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
288 }
289 else
290 {
291 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
292 }
293
294 return STATUS_SUCCESS;
295 }
296
297 return STATUS_RANGE_NOT_LOCKED;
298 }
299
300 BOOLEAN
301 FASTCALL
302 FsRtlpAddLock(IN PFILE_LOCK_TOC LockToc,
303 IN PFILE_OBJECT FileObject,
304 IN PLARGE_INTEGER FileOffset,
305 IN PLARGE_INTEGER Length,
306 IN PEPROCESS Process,
307 IN ULONG Key,
308 IN BOOLEAN ExclusiveLock,
309 IN PVOID Context)
310 {
311 PFILE_LOCK_GRANTED Granted;
312 LARGE_INTEGER EndOffset;
313
314 EndOffset.QuadPart = FileOffset->QuadPart + Length->QuadPart - 1;
315
316 //loop and try to find conflicking locks
317 LIST_FOR_EACH(Granted, &LockToc->GrantedListHead, FILE_LOCK_GRANTED, ListEntry)
318 {
319 if (IsOverlappingLock(&Granted->Lock, FileOffset, &EndOffset))
320 {
321 //we found a locks that overlap with the new lock
322
323 //if both locks are shared, we might have a fast path outa here...
324 if (!Granted->Lock.ExclusiveLock && !ExclusiveLock)
325 {
326 //if existing lock surround new lock, we know that no other exclusive lock
327 //may overlap with our new lock;-D
328 if (IsSurroundingLock(&Granted->Lock, FileOffset, &EndOffset))
329 {
330 break;
331 }
332
333 //else keep locking for conflicts
334 continue;
335 }
336
337 //we found a conflict:
338 //we want shared access to an excl. lock OR exlc. access to a shared lock
339 return FALSE;
340 }
341 }
342
343 Granted = ExAllocateFromNPagedLookasideList(&GrantedLookaside);
344
345 //starting offset
346 Granted->Lock.StartingByte = *FileOffset;
347 Granted->Lock.Length = *Length;
348 Granted->Lock.ExclusiveLock = ExclusiveLock;
349 Granted->Lock.Key = Key;
350 Granted->Lock.FileObject = FileObject;
351 Granted->Lock.ProcessId = Process;
352 //ending offset
353 Granted->Lock.EndingByte = EndOffset;
354 Granted->UnlockContext = Context;
355
356 InsertHeadList(&LockToc->GrantedListHead,&Granted->ListEntry);
357 return TRUE;
358 }
359
360 VOID
361 FASTCALL
362 FsRtlpCompletePendingLocks(IN PFILE_LOCK FileLock,
363 IN PFILE_LOCK_TOC LockToc,
364 IN OUT PKIRQL oldirql,
365 IN PVOID Context)
366 {
367 //walk pending list, FIFO order, try 2 complete locks
368 PLIST_ENTRY EnumEntry;
369 PIRP Irp, tmp;
370 PIO_STACK_LOCATION Stack;
371 LIST_ENTRY CompletedListHead;
372
373 InitializeListHead(&CompletedListHead);
374
375 LIST_FOR_EACH_SAFE(Irp, tmp, &LockToc->PendingListHead, IRP, Tail.Overlay.ListEntry)
376 {
377 Stack = IoGetCurrentIrpStackLocation(Irp);
378 if (FsRtlpAddLock(LockToc,
379 Stack->FileObject,
380 &Stack->Parameters.LockControl.ByteOffset,
381 Stack->Parameters.LockControl.Length,
382 IoGetRequestorProcess(Irp),
383 Stack->Parameters.LockControl.Key,
384 Stack->Flags & SL_EXCLUSIVE_LOCK,
385 Irp->Tail.Overlay.DriverContext[2] //Context
386 ) )
387 {
388 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
389
390 if (!IoSetCancelRoutine(Irp, NULL))
391 {
392 //irp is canceled and cancelroutine will run when we release the lock
393 InitializeListHead(&Irp->Tail.Overlay.ListEntry);
394 continue;
395 }
396
397 /*
398 Put on completed list and complete them all afterwards.
399 This way we don't have to restart enum after each completion.
400 */
401 InsertHeadList(&CompletedListHead, &Irp->Tail.Overlay.ListEntry);
402 }
403 }
404
405 KeReleaseSpinLock(&LockToc->SpinLock, *oldirql);
406
407 //complete irp's (if any)
408 while (!IsListEmpty(&CompletedListHead))
409 {
410 EnumEntry = RemoveTailList(&CompletedListHead);
411
412 Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
413
414 Irp->IoStatus.Status = STATUS_SUCCESS;
415 Irp->IoStatus.Information = 0;
416
417 if (FileLock->CompleteLockIrpRoutine)
418 {
419 if (FileLock->CompleteLockIrpRoutine(Context, Irp)!=STATUS_SUCCESS)
420 {
421 Stack = IoGetCurrentIrpStackLocation(Irp);
422
423 //revert
424 FsRtlpUnlockSingle ( FileLock,
425 Stack->FileObject,
426 &Stack->Parameters.LockControl.ByteOffset,
427 Stack->Parameters.LockControl.Length,
428 IoGetRequestorProcess(Irp),
429 Stack->Parameters.LockControl.Key,
430 NULL, /* unused context */
431 FALSE /* don't call unlock copletion rout.*/
432 );
433 }
434 }
435 else
436 {
437 IoCompleteRequest(Irp, IO_NO_INCREMENT);
438 }
439
440 }
441
442 KeAcquireSpinLock(&LockToc->SpinLock, oldirql);
443 }
444
445 NTSTATUS
446 FASTCALL
447 FsRtlpUnlockSingle(IN PFILE_LOCK FileLock,
448 IN PFILE_OBJECT FileObject,
449 IN PLARGE_INTEGER FileOffset,
450 IN PLARGE_INTEGER Length,
451 IN PEPROCESS Process,
452 IN ULONG Key,
453 IN PVOID Context OPTIONAL,
454 IN BOOLEAN CallUnlockRoutine)
455 {
456 KIRQL oldirql;
457 PFILE_LOCK_TOC LockToc;
458 PFILE_LOCK_GRANTED Granted, tmp;
459
460 ASSERT(FileLock);
461 LockToc = FileLock->LockInformation;
462
463 if (LockToc == NULL)
464 {
465 return STATUS_RANGE_NOT_LOCKED;
466 }
467
468 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql );
469
470 LIST_FOR_EACH_SAFE(Granted, tmp, &LockToc->GrantedListHead, FILE_LOCK_GRANTED,ListEntry)
471 {
472
473 //must be exact match
474 if (FileOffset->QuadPart == Granted->Lock.StartingByte.QuadPart &&
475 Length->QuadPart == Granted->Lock.Length.QuadPart &&
476 Granted->Lock.ProcessId == Process &&
477 Granted->Lock.FileObject == FileObject &&
478 Granted->Lock.Key == Key)
479 {
480 RemoveEntryList(&Granted->ListEntry);
481 FsRtlpCompletePendingLocks(FileLock, LockToc, &oldirql, Context);
482
483 if (IsListEmpty(&LockToc->GrantedListHead))
484 {
485 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
486
487 FsRtlAreThereCurrentFileLocks(FileLock) = FALSE; //paged data
488 }
489 else
490 {
491 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
492 }
493
494 if (FileLock->UnlockRoutine && CallUnlockRoutine)
495 {
496 FileLock->UnlockRoutine(Granted->UnlockContext, &Granted->Lock);
497 }
498
499 ExFreeToNPagedLookasideList(&GrantedLookaside, Granted);
500
501 return STATUS_SUCCESS;
502 }
503 }
504
505 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
506
507 return STATUS_RANGE_NOT_LOCKED;
508
509 }
510
511 /* PUBLIC FUNCTIONS **********************************************************/
512
513 /*
514 * @implemented
515 */
516 PFILE_LOCK_INFO
517 NTAPI
518 FsRtlGetNextFileLock(IN PFILE_LOCK FileLock,
519 IN BOOLEAN Restart)
520 {
521 /*
522 Messy enumeration of granted locks.
523 What our last ptr. in LastReturnedLock points at, might have been freed between
524 calls, so we have to scan thru the list every time, searching for our last lock.
525 If it's not there anymore, restart the enumeration...
526 */
527 KIRQL oldirql;
528 PLIST_ENTRY EnumEntry;
529 PFILE_LOCK_GRANTED Granted;
530 PFILE_LOCK_TOC LockToc;
531 BOOLEAN FoundPrevious = FALSE;
532 //must make local copy since FILE_LOCK struct is allowed to be in paged mem
533 FILE_LOCK_INFO LocalLastReturnedLockInfo;
534 PVOID LocalLastReturnedLock;
535
536 ASSERT(FileLock);
537 LockToc = FileLock->LockInformation;
538 if (LockToc == NULL)
539 {
540 return NULL;
541 }
542
543 LocalLastReturnedLock = FileLock->LastReturnedLock;
544
545 KeAcquireSpinLock(&LockToc->SpinLock,&oldirql);
546
547 restart:;
548
549 EnumEntry = LockToc->GrantedListHead.Flink;
550
551 if (Restart)
552 {
553 if (EnumEntry != &LockToc->GrantedListHead)
554 {
555 Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
556 LocalLastReturnedLockInfo = Granted->Lock;
557 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
558
559 FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
560 FileLock->LastReturnedLock = EnumEntry;
561 return &FileLock->LastReturnedLockInfo;
562 }
563 else
564 {
565 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
566 return NULL;
567 }
568 }
569
570 //else: continue enum
571 while (EnumEntry != &LockToc->GrantedListHead)
572 {
573 //found previous lock?
574 if (EnumEntry == LocalLastReturnedLock)
575 {
576 FoundPrevious = TRUE;
577 //get next
578 EnumEntry = EnumEntry->Flink;
579 if (EnumEntry != &LockToc->GrantedListHead)
580 {
581 Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
582 LocalLastReturnedLockInfo = Granted->Lock;
583 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
584
585 FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
586 FileLock->LastReturnedLock = EnumEntry;
587 return &FileLock->LastReturnedLockInfo;
588 }
589 break;
590 }
591 EnumEntry = EnumEntry->Flink;
592 }
593
594 if (!FoundPrevious)
595 {
596 //got here? uh no, didn't find our last lock..must have been freed...restart
597 Restart = TRUE;
598 goto restart;
599 }
600
601 KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
602
603 return NULL;//no (more) locks
604 }
605
606 /*
607 * @implemented
608 */
609 BOOLEAN
610 NTAPI
611 FsRtlPrivateLock(IN PFILE_LOCK FileLock,
612 IN PFILE_OBJECT FileObject,
613 IN PLARGE_INTEGER FileOffset,
614 IN PLARGE_INTEGER Length,
615 IN PEPROCESS Process,
616 IN ULONG Key,
617 IN BOOLEAN FailImmediately, //seems meaningless for fast io
618 IN BOOLEAN ExclusiveLock,
619 OUT PIO_STATUS_BLOCK IoStatus,
620 IN PIRP Irp OPTIONAL,
621 IN PVOID Context OPTIONAL,
622 IN BOOLEAN AlreadySynchronized)
623 {
624 PFILE_LOCK_TOC LockToc;
625 KIRQL oldirql;
626
627 ASSERT(FileLock);
628 if (FileLock->LockInformation == NULL)
629 {
630 ExAcquireFastMutex(&LockTocMutex);
631 //still NULL?
632 if (FileLock->LockInformation == NULL)
633 {
634 FileLock->LockInformation = ExAllocateFromNPagedLookasideList(&LockTocLookaside);
635 LockToc = FileLock->LockInformation;
636 KeInitializeSpinLock(&LockToc->SpinLock);
637 InitializeListHead(&LockToc->GrantedListHead);
638 InitializeListHead(&LockToc->PendingListHead);
639 }
640 ExReleaseFastMutex(&LockTocMutex);
641 }
642
643 LockToc = FileLock->LockInformation;
644 KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
645
646 //try add new lock (while holding spin lock)
647 if (FsRtlpAddLock(LockToc,
648 FileObject,
649 FileOffset,
650 Length,
651 Process,
652 Key,
653 ExclusiveLock,
654 Context
655 ) )
656 {
657 IoStatus->Status = STATUS_SUCCESS;
658 }
659 else if (Irp && !FailImmediately)
660 {
661 //failed + irp + no fail = make. pending
662
663 Irp->Tail.Overlay.DriverContext[3] = &LockToc->SpinLock;
664 Irp->Tail.Overlay.DriverContext[2] = Context;
665
666 (void)IoSetCancelRoutine(Irp, FsRtlpFileLockCancelRoutine);
667 if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
668 {
669 //irp was canceled
670 KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
671
672 Irp->IoStatus.Status = STATUS_CANCELLED;
673 Irp->IoStatus.Information = 0;
674 IoCompleteRequest(Irp, IO_NO_INCREMENT);
675
676 return TRUE;
677 }
678
679 IoMarkIrpPending(Irp);
680 Irp->IoStatus.Status = IoStatus->Status = STATUS_PENDING;
681 Irp->IoStatus.Information = 0;
682 InsertHeadList(&LockToc->PendingListHead,&Irp->Tail.Overlay.ListEntry);
683
684 }
685 else
686 {
687 IoStatus->Status = STATUS_LOCK_NOT_GRANTED;
688 }
689
690 KeReleaseSpinLock(&LockToc->SpinLock, oldirql); //fires cancel routine
691
692 //never pending if no irp;-)
693 ASSERT(!(IoStatus->Status == STATUS_PENDING && !Irp));
694
695 if (IoStatus->Status != STATUS_PENDING)
696 {
697 if (IoStatus->Status == STATUS_SUCCESS)
698 {
699 FsRtlAreThereCurrentFileLocks(FileLock) = TRUE;
700 }
701
702 if (Irp)
703 {
704 Irp->IoStatus.Status = IoStatus->Status;
705 Irp->IoStatus.Information = 0;
706 if (FileLock->CompleteLockIrpRoutine)
707 {
708 if (FileLock->CompleteLockIrpRoutine(Context,Irp)!=STATUS_SUCCESS)
709 {
710 //CompleteLockIrpRoutine complain: revert changes
711 FsRtlpUnlockSingle( FileLock,
712 FileObject,
713 FileOffset,
714 Length,
715 Process,
716 Key,
717 NULL, /* context */
718 FALSE /* don't call unlock copletion routine */
719 );
720 }
721 }
722 else
723 {
724 IoCompleteRequest(Irp, IO_NO_INCREMENT);
725 }
726 }
727 }
728
729 //NOTE: only fast io seems to care about this return value
730 return (IoStatus->Status == STATUS_SUCCESS || FailImmediately);
731 }
732
733 /*
734 * @implemented
735 */
736 BOOLEAN
737 NTAPI
738 FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock,
739 IN PIRP Irp)
740 {
741 PIO_STACK_LOCATION Stack;
742 LARGE_INTEGER LocalLength;
743
744 /* Get the I/O Stack location and length */
745 Stack = IoGetCurrentIrpStackLocation(Irp);
746 LocalLength.QuadPart = Stack->Parameters.Read.Length;
747
748 /* Call the internal API */
749 return FsRtlpCheckLockForReadOrWriteAccess(FileLock,
750 &Stack->Parameters.
751 Read.ByteOffset,
752 &LocalLength,
753 Stack->Parameters.Read.Key,
754 Stack->FileObject,
755 IoGetRequestorProcess(Irp),
756 TRUE);
757 }
758
759 /*
760 * @implemented
761 */
762 BOOLEAN
763 NTAPI
764 FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock,
765 IN PIRP Irp)
766 {
767 PIO_STACK_LOCATION Stack;
768 LARGE_INTEGER LocalLength;
769
770 /* Get the I/O Stack location and length */
771 Stack = IoGetCurrentIrpStackLocation(Irp);
772 LocalLength.QuadPart = Stack->Parameters.Read.Length;
773
774 /* Call the internal API */
775 return FsRtlpCheckLockForReadOrWriteAccess(FileLock,
776 &Stack->Parameters.
777 Read.ByteOffset,
778 &LocalLength,
779 Stack->Parameters.Read.Key,
780 Stack->FileObject,
781 IoGetRequestorProcess(Irp),
782 FALSE);
783 }
784
785 /*
786 * @implemented
787 */
788 BOOLEAN
789 NTAPI
790 FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock,
791 IN PLARGE_INTEGER FileOffset,
792 IN PLARGE_INTEGER Length,
793 IN ULONG Key,
794 IN PFILE_OBJECT FileObject,
795 IN PEPROCESS Process)
796 {
797 /* Call the internal API */
798 return FsRtlpCheckLockForReadOrWriteAccess(FileLock,
799 FileOffset,
800 Length,
801 Key,
802 FileObject,
803 Process,
804 TRUE);
805 }
806
807 /*
808 * @implemented
809 */
810 BOOLEAN
811 NTAPI
812 FsRtlFastCheckLockForWrite(IN PFILE_LOCK FileLock,
813 IN PLARGE_INTEGER FileOffset,
814 IN PLARGE_INTEGER Length,
815 IN ULONG Key,
816 IN PFILE_OBJECT FileObject,
817 IN PEPROCESS Process)
818 {
819 /* Call the internal API */
820 return FsRtlpCheckLockForReadOrWriteAccess(FileLock,
821 FileOffset,
822 Length,
823 Key,
824 FileObject,
825 Process,
826 FALSE);
827 }
828
829 /*
830 * @implemented
831 */
832 NTSTATUS
833 NTAPI
834 FsRtlFastUnlockSingle(IN PFILE_LOCK FileLock,
835 IN PFILE_OBJECT FileObject,
836 IN PLARGE_INTEGER FileOffset,
837 IN PLARGE_INTEGER Length,
838 IN PEPROCESS Process,
839 IN ULONG Key,
840 IN PVOID Context OPTIONAL,
841 IN BOOLEAN AlreadySynchronized)
842 {
843 /* Call the internal API */
844 return FsRtlpUnlockSingle(FileLock,
845 FileObject,
846 FileOffset,
847 Length,
848 Process,
849 Key,
850 Context,
851 TRUE);
852 }
853
854 /*
855 * @implemented
856 */
857 NTSTATUS
858 NTAPI
859 FsRtlFastUnlockAll(IN PFILE_LOCK FileLock,
860 IN PFILE_OBJECT FileObject,
861 IN PEPROCESS Process,
862 IN PVOID Context OPTIONAL)
863 {
864 /* Call the generic function by process */
865 return FsRtlpFastUnlockAllByKey(FileLock,
866 FileObject,
867 Process,
868 0,
869 FALSE,
870 Context);
871 }
872
873 /*
874 * @implemented
875 */
876 NTSTATUS
877 NTAPI
878 FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock,
879 IN PFILE_OBJECT FileObject,
880 IN PEPROCESS Process,
881 IN ULONG Key,
882 IN PVOID Context OPTIONAL)
883 {
884 /* Call the generic function by key */
885 return FsRtlpFastUnlockAllByKey(FileLock,
886 FileObject,
887 Process,
888 Key,
889 TRUE,
890 Context);
891 }
892
893 /*
894 * @implemented
895 */
896 NTSTATUS
897 NTAPI
898 FsRtlProcessFileLock(IN PFILE_LOCK FileLock,
899 IN PIRP Irp,
900 IN PVOID Context OPTIONAL)
901 {
902 PIO_STACK_LOCATION IoStackLocation;
903 NTSTATUS Status;
904 IO_STATUS_BLOCK IoStatusBlock;
905
906 /* Get the I/O Stack location */
907 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
908 ASSERT(IoStackLocation->MajorFunction == IRP_MJ_LOCK_CONTROL);
909
910 /* Clear the I/O status block and check what function this is */
911 IoStatusBlock.Information = 0;
912 switch(IoStackLocation->MinorFunction)
913 {
914 /* A lock */
915 case IRP_MN_LOCK:
916
917 /* Call the private lock routine */
918 FsRtlPrivateLock(FileLock,
919 IoStackLocation->FileObject,
920 &IoStackLocation->
921 Parameters.LockControl.ByteOffset,
922 IoStackLocation->Parameters.LockControl.Length,
923 IoGetRequestorProcess(Irp),
924 IoStackLocation->Parameters.LockControl.Key,
925 IoStackLocation->Flags & SL_FAIL_IMMEDIATELY,
926 IoStackLocation->Flags & SL_EXCLUSIVE_LOCK,
927 &IoStatusBlock,
928 Irp,
929 Context,
930 FALSE);
931 break;
932
933 /* A single unlock */
934 case IRP_MN_UNLOCK_SINGLE:
935
936 /* Call fast unlock */
937 IoStatusBlock.Status =
938 FsRtlFastUnlockSingle(FileLock,
939 IoStackLocation->FileObject,
940 &IoStackLocation->Parameters.LockControl.
941 ByteOffset,
942 IoStackLocation->Parameters.LockControl.
943 Length,
944 IoGetRequestorProcess(Irp),
945 IoStackLocation->Parameters.LockControl.
946 Key,
947 Context,
948 FALSE);
949
950 /* Complete the IRP */
951 FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine,
952 Context,
953 Irp,
954 IoStatusBlock.Status,
955 &Status,
956 NULL);
957 break;
958
959 /* Total unlock */
960 case IRP_MN_UNLOCK_ALL:
961
962 /* Do a fast unlock */
963 IoStatusBlock.Status = FsRtlFastUnlockAll(FileLock,
964 IoStackLocation->
965 FileObject,
966 IoGetRequestorProcess(Irp),
967 Context);
968
969 /* Complete the IRP */
970 FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine,
971 Context,
972 Irp,
973 IoStatusBlock.Status,
974 &Status,
975 NULL);
976 break;
977
978 /* Unlock by key */
979 case IRP_MN_UNLOCK_ALL_BY_KEY:
980
981 /* Do it */
982 IoStatusBlock.Status =
983 FsRtlFastUnlockAllByKey(FileLock,
984 IoStackLocation->FileObject,
985 IoGetRequestorProcess(Irp),
986 IoStackLocation->Parameters.
987 LockControl.Key,
988 Context);
989
990 /* Complete the IRP */
991 FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine,
992 Context,
993 Irp,
994 IoStatusBlock.Status,
995 &Status,
996 NULL);
997 break;
998
999 /* Invalid request */
1000 default:
1001
1002 /* Complete it */
1003 FsRtlCompleteRequest(Irp, STATUS_INVALID_DEVICE_REQUEST);
1004 IoStatusBlock.Status = STATUS_INVALID_DEVICE_REQUEST;
1005 break;
1006 }
1007
1008 /* Return the status */
1009 return IoStatusBlock.Status;
1010 }
1011
1012 /*
1013 * @implemented
1014 */
1015 VOID
1016 NTAPI
1017 FsRtlInitializeFileLock (IN PFILE_LOCK FileLock,
1018 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1019 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1020 {
1021 /* Setup the lock */
1022 FileLock->FastIoIsQuestionable = FALSE;
1023 FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
1024 FileLock->UnlockRoutine = UnlockRoutine;
1025 FileLock->LockInformation = NULL;
1026 }
1027
1028 /*
1029 * @implemented
1030 */
1031 VOID
1032 NTAPI
1033 FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock)
1034 {
1035 PFILE_LOCK_TOC LockToc;
1036 PIRP Irp;
1037 PFILE_LOCK_GRANTED Granted;
1038 PLIST_ENTRY EnumEntry;
1039 KIRQL OldIrql;
1040
1041 /* Get the lock information */
1042 LockToc = FileLock->LockInformation;
1043 if (!FileLock->LockInformation) return;
1044
1045 /* Acquire the lock queue */
1046 KeAcquireSpinLock(&LockToc->SpinLock, &OldIrql);
1047
1048 /* Loop the lock tree */
1049 while (!IsListEmpty(&LockToc->GrantedListHead))
1050 {
1051 /* Get the entry */
1052 EnumEntry = RemoveTailList(&LockToc->GrantedListHead);
1053
1054 /* Get the granted lock and free it */
1055 Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED, ListEntry);
1056 ExFreeToNPagedLookasideList(&GrantedLookaside, Granted);
1057 }
1058
1059 /* Loop the waiting locks */
1060 while (!IsListEmpty(&LockToc->PendingListHead))
1061 {
1062 /* Get the entry and IRP */
1063 EnumEntry = RemoveTailList(&LockToc->PendingListHead);
1064 Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
1065
1066 /* Release the lock */
1067 KeReleaseSpinLock(&LockToc->SpinLock, OldIrql);
1068
1069 /* Acquire cancel spinlock and clear the cancel routine */
1070 IoAcquireCancelSpinLock(&Irp->CancelIrql);
1071 (void)IoSetCancelRoutine(Irp, NULL);
1072 IoReleaseCancelSpinLock(Irp->CancelIrql);
1073
1074 /* Complete the IRP */
1075 Irp->IoStatus.Information = 0;
1076 Irp->IoStatus.Status = STATUS_RANGE_NOT_LOCKED;
1077 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1078
1079 /* Acquire the lock again */
1080 KeAcquireSpinLock(&LockToc->SpinLock, &OldIrql);
1081 }
1082
1083 /* Release the lock and free it */
1084 KeReleaseSpinLock(&LockToc->SpinLock, OldIrql);
1085 ExFreeToNPagedLookasideList(&LockTocLookaside, LockToc);
1086
1087 /* Remove the lock information pointer */
1088 FileLock->LockInformation = NULL;
1089 }
1090
1091 /*
1092 * @implemented
1093 */
1094 PFILE_LOCK
1095 NTAPI
1096 FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1097 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1098 {
1099 PFILE_LOCK FileLock;
1100
1101 /* Try to allocate it */
1102 FileLock = ExAllocateFromPagedLookasideList(&LockLookaside);
1103 if (FileLock)
1104 {
1105 /* Initialize it */
1106 FsRtlInitializeFileLock(FileLock,
1107 CompleteLockIrpRoutine,
1108 UnlockRoutine);
1109 }
1110
1111 /* Return the lock */
1112 return FileLock;
1113 }
1114
1115 /*
1116 * @implemented
1117 */
1118 VOID
1119 NTAPI
1120 FsRtlFreeFileLock(IN PFILE_LOCK FileLock)
1121 {
1122 /* Uninitialize and free the lock */
1123 FsRtlUninitializeFileLock(FileLock);
1124 ExFreeToPagedLookasideList(&LockLookaside, FileLock);
1125 }
1126
1127 /* EOF */