079a262cb0d685be998df77e55b23c09ee32a4ff
[reactos.git] / reactos / ntoskrnl / fsrtl / filelock.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/filelock.c
5 * PURPOSE: File Locking implementation for File System Drivers
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 PAGED_LOOKASIDE_LIST FsRtlFileLockLookasideList;
18
19 /* Note: this aligns the two types of lock entry structs so we can access the
20 FILE_LOCK_INFO part in common. Add elements after Shared if new stuff is needed.
21 */
22 typedef union _COMBINED_LOCK_ELEMENT
23 {
24 struct
25 {
26 LIST_ENTRY dummy;
27 FILE_SHARED_LOCK_ENTRY Shared;
28 };
29 FILE_EXCLUSIVE_LOCK_ENTRY Exclusive;
30 }
31 COMBINED_LOCK_ELEMENT, *PCOMBINED_LOCK_ELEMENT;
32
33 typedef struct _LOCK_INFORMATION
34 {
35 RTL_GENERIC_TABLE RangeTable;
36 IO_CSQ Csq;
37 KSPIN_LOCK CsqLock;
38 LIST_ENTRY CsqList;
39 PFILE_LOCK BelongsTo;
40 LIST_ENTRY SharedLocks;
41 ULONG Generation;
42 }
43 LOCK_INFORMATION, *PLOCK_INFORMATION;
44
45 typedef struct _LOCK_SHARED_RANGE
46 {
47 LIST_ENTRY Entry;
48 LARGE_INTEGER Start, End;
49 ULONG Key;
50 PVOID ProcessId;
51 }
52 LOCK_SHARED_RANGE, *PLOCK_SHARED_RANGE;
53
54 /* PRIVATE FUNCTIONS *********************************************************/
55
56 VOID
57 NTAPI
58 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine,
59 IN PVOID Context,
60 IN PIRP Irp,
61 IN NTSTATUS Status,
62 OUT PNTSTATUS NewStatus,
63 IN PFILE_OBJECT FileObject OPTIONAL);
64
65 /* Generic table methods */
66
67 static PVOID NTAPI LockAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes)
68 {
69 PVOID Result;
70 Result = ExAllocatePoolWithTag(NonPagedPool, Bytes, 'LTAB');
71 DPRINT("LockAllocate(%d) => %p\n", Bytes, Result);
72 return Result;
73 }
74
75 static VOID NTAPI LockFree(PRTL_GENERIC_TABLE Table, PVOID Buffer)
76 {
77 DPRINT("LockFree(%p)\n", Buffer);
78 ExFreePoolWithTag(Buffer, 'LTAB');
79 }
80
81 static RTL_GENERIC_COMPARE_RESULTS NTAPI LockCompare
82 (PRTL_GENERIC_TABLE Table, PVOID PtrA, PVOID PtrB)
83 {
84 PCOMBINED_LOCK_ELEMENT A = PtrA, B = PtrB;
85 RTL_GENERIC_COMPARE_RESULTS Result;
86 #if 0
87 DPRINT("Starting to compare element %x to element %x\n", PtrA, PtrB);
88 #endif
89 /* Match if we overlap */
90 if (((A->Exclusive.FileLock.StartingByte.QuadPart <
91 B->Exclusive.FileLock.EndingByte.QuadPart) &&
92 (A->Exclusive.FileLock.StartingByte.QuadPart >=
93 B->Exclusive.FileLock.StartingByte.QuadPart)) ||
94 ((B->Exclusive.FileLock.StartingByte.QuadPart <
95 A->Exclusive.FileLock.EndingByte.QuadPart) &&
96 (B->Exclusive.FileLock.StartingByte.QuadPart >=
97 A->Exclusive.FileLock.StartingByte.QuadPart)))
98 return GenericEqual;
99 /* Otherwise, key on the starting byte */
100 Result =
101 (A->Exclusive.FileLock.StartingByte.QuadPart <
102 B->Exclusive.FileLock.StartingByte.QuadPart) ? GenericLessThan :
103 (A->Exclusive.FileLock.StartingByte.QuadPart >
104 B->Exclusive.FileLock.StartingByte.QuadPart) ? GenericGreaterThan :
105 GenericEqual;
106 #if 0
107 DPRINT("Compare(%x:%x) %x-%x to %x-%x => %d\n",
108 A,B,
109 A->Exclusive.FileLock.StartingByte.LowPart,
110 A->Exclusive.FileLock.EndingByte.LowPart,
111 B->Exclusive.FileLock.StartingByte.LowPart,
112 B->Exclusive.FileLock.EndingByte.LowPart,
113 Result);
114 #endif
115 return Result;
116 }
117
118 /* CSQ methods */
119
120 static NTSTATUS NTAPI LockInsertIrpEx
121 (PIO_CSQ Csq,
122 PIRP Irp,
123 PVOID InsertContext)
124 {
125 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq);
126 InsertTailList(&LockInfo->CsqList, &Irp->Tail.Overlay.ListEntry);
127 return STATUS_SUCCESS;
128 }
129
130 static VOID NTAPI LockRemoveIrp(PIO_CSQ Csq, PIRP Irp)
131 {
132 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
133 }
134
135 static PIRP NTAPI LockPeekNextIrp(PIO_CSQ Csq, PIRP Irp, PVOID PeekContext)
136 {
137 // Context will be a COMBINED_LOCK_ELEMENT. We're looking for a
138 // lock that can be acquired, now that the lock matching PeekContext
139 // has been removed.
140 COMBINED_LOCK_ELEMENT LockElement;
141 PCOMBINED_LOCK_ELEMENT WhereUnlock = PeekContext;
142 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq);
143 PLIST_ENTRY Following;
144 DPRINT("PeekNextIrp(IRP %p, Context %p)\n", Irp, PeekContext);
145 if (!Irp)
146 {
147 Following = LockInfo->CsqList.Flink;
148 }
149 else
150 Following = Irp->Tail.Overlay.ListEntry.Flink;
151
152 DPRINT("ListEntry %p Head %p\n", Following, &LockInfo->CsqList);
153 for (;
154 Following != &LockInfo->CsqList;
155 Following = Following->Flink)
156 {
157 PIO_STACK_LOCATION IoStack;
158 BOOLEAN Matching;
159 Irp = CONTAINING_RECORD(Following, IRP, Tail.Overlay.ListEntry);
160 DPRINT("Irp %p\n", Irp);
161 IoStack = IoGetCurrentIrpStackLocation(Irp);
162 LockElement.Exclusive.FileLock.StartingByte =
163 IoStack->Parameters.LockControl.ByteOffset;
164 LockElement.Exclusive.FileLock.EndingByte.QuadPart =
165 LockElement.Exclusive.FileLock.StartingByte.QuadPart +
166 IoStack->Parameters.LockControl.Length->QuadPart;
167 /* If a context was specified, it's a range to check to unlock */
168 if (WhereUnlock)
169 {
170 Matching = LockCompare
171 (&LockInfo->RangeTable, &LockElement, WhereUnlock) != GenericEqual;
172 }
173 /* Else get any completable IRP */
174 else
175 {
176 Matching = FALSE;
177 }
178 if (!Matching)
179 {
180 // This IRP is fine...
181 DPRINT("Returning the IRP %p\n", Irp);
182 return Irp;
183 }
184 }
185 DPRINT("Return NULL\n");
186 return NULL;
187 }
188
189 static VOID NTAPI
190 LockAcquireQueueLock(PIO_CSQ Csq, PKIRQL Irql)
191 {
192 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq);
193 KeAcquireSpinLock(&LockInfo->CsqLock, Irql);
194 }
195
196 static VOID NTAPI
197 LockReleaseQueueLock(PIO_CSQ Csq, KIRQL Irql)
198 {
199 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq);
200 KeReleaseSpinLock(&LockInfo->CsqLock, Irql);
201 }
202
203 static VOID NTAPI
204 LockCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp)
205 {
206 NTSTATUS Status;
207 PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq);
208 DPRINT("Complete cancelled IRP %p Status %x\n", Irp, STATUS_CANCELLED);
209 FsRtlCompleteLockIrpReal
210 (LockInfo->BelongsTo->CompleteLockIrpRoutine,
211 NULL,
212 Irp,
213 STATUS_CANCELLED,
214 &Status,
215 NULL);
216 }
217
218 VOID
219 NTAPI
220 FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine,
221 IN PVOID Context,
222 IN PIRP Irp,
223 IN NTSTATUS Status,
224 OUT PNTSTATUS NewStatus,
225 IN PFILE_OBJECT FileObject OPTIONAL)
226 {
227 /* Check if we have a complete routine */
228 Irp->IoStatus.Information = 0;
229 if (CompleteRoutine)
230 {
231 /* Check if we have a file object */
232 if (FileObject) FileObject->LastLock = NULL;
233
234 /* Set the I/O Status and do completion */
235 Irp->IoStatus.Status = Status;
236 DPRINT("Calling completion routine %p Status %x\n", Irp, Status);
237 *NewStatus = CompleteRoutine(Context, Irp);
238 }
239 else
240 {
241 /* Otherwise do a normal I/O complete request */
242 DPRINT("Completing IRP %p Status %x\n", Irp, Status);
243 FsRtlCompleteRequest(Irp, Status);
244 *NewStatus = Status;
245 }
246 }
247
248 /* PUBLIC FUNCTIONS **********************************************************/
249
250 /*
251 * @implemented
252 */
253 PFILE_LOCK_INFO
254 NTAPI
255 FsRtlGetNextFileLock(IN PFILE_LOCK FileLock,
256 IN BOOLEAN Restart)
257 {
258 PCOMBINED_LOCK_ELEMENT Entry;
259 if (!FileLock->LockInformation) return NULL;
260 Entry = RtlEnumerateGenericTable(FileLock->LockInformation, Restart);
261 if (!Entry) return NULL;
262 else return &Entry->Exclusive.FileLock;
263 }
264
265 VOID
266 NTAPI
267 FsRtlpExpandLockElement
268 (PCOMBINED_LOCK_ELEMENT ToExpand,
269 PCOMBINED_LOCK_ELEMENT Conflict)
270 {
271 if (ToExpand->Exclusive.FileLock.StartingByte.QuadPart >
272 Conflict->Exclusive.FileLock.StartingByte.QuadPart)
273 {
274 ToExpand->Exclusive.FileLock.StartingByte =
275 Conflict->Exclusive.FileLock.StartingByte;
276 }
277 if (ToExpand->Exclusive.FileLock.EndingByte.QuadPart <
278 Conflict->Exclusive.FileLock.EndingByte.QuadPart)
279 {
280 ToExpand->Exclusive.FileLock.EndingByte =
281 Conflict->Exclusive.FileLock.EndingByte;
282 }
283 }
284
285 /* This function expands the conflicting range Conflict by removing and reinserting it,
286 then adds a shared range of the same size */
287 PCOMBINED_LOCK_ELEMENT
288 NTAPI
289 FsRtlpRebuildSharedLockRange
290 (PFILE_LOCK FileLock,
291 PLOCK_INFORMATION LockInfo,
292 PCOMBINED_LOCK_ELEMENT Conflict)
293 {
294 /* Starting at Conflict->StartingByte and going to Conflict->EndingByte
295 * capture and expand a shared range from the shared range list.
296 * Finish when we've incorporated all overlapping shared regions.
297 */
298 BOOLEAN InsertedNew = FALSE, RemovedOld;
299 COMBINED_LOCK_ELEMENT NewElement = *Conflict;
300 PCOMBINED_LOCK_ELEMENT Entry;
301 while ((Entry = RtlLookupElementGenericTable
302 (FileLock->LockInformation, &NewElement)))
303 {
304 FsRtlpExpandLockElement(&NewElement, Entry);
305 RemovedOld = RtlDeleteElementGenericTable
306 (&LockInfo->RangeTable,
307 Entry);
308 ASSERT(RemovedOld);
309 }
310 Conflict = RtlInsertElementGenericTable
311 (&LockInfo->RangeTable,
312 &NewElement,
313 sizeof(NewElement),
314 &InsertedNew);
315 ASSERT(InsertedNew);
316 return Conflict;
317 }
318
319 /*
320 * @implemented
321 */
322 BOOLEAN
323 NTAPI
324 FsRtlPrivateLock(IN PFILE_LOCK FileLock,
325 IN PFILE_OBJECT FileObject,
326 IN PLARGE_INTEGER FileOffset,
327 IN PLARGE_INTEGER Length,
328 IN PEPROCESS Process,
329 IN ULONG Key,
330 IN BOOLEAN FailImmediately,
331 IN BOOLEAN ExclusiveLock,
332 OUT PIO_STATUS_BLOCK IoStatus,
333 IN PIRP Irp OPTIONAL,
334 IN PVOID Context OPTIONAL,
335 IN BOOLEAN AlreadySynchronized)
336 {
337 NTSTATUS Status;
338 COMBINED_LOCK_ELEMENT ToInsert;
339 PCOMBINED_LOCK_ELEMENT Conflict;
340 PLOCK_INFORMATION LockInfo;
341 PLOCK_SHARED_RANGE NewSharedRange;
342 BOOLEAN InsertedNew;
343 ULARGE_INTEGER UnsignedStart;
344 ULARGE_INTEGER UnsignedEnd;
345
346 DPRINT("FsRtlPrivateLock(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x, FailImmediately %d, Exclusive %d)\n",
347 &FileObject->FileName,
348 FileOffset->HighPart,
349 FileOffset->LowPart,
350 (int)FileOffset->QuadPart,
351 Length->HighPart,
352 Length->LowPart,
353 (int)Length->QuadPart,
354 Key,
355 FailImmediately,
356 ExclusiveLock);
357
358 UnsignedStart.QuadPart = FileOffset->QuadPart;
359 UnsignedEnd.QuadPart = FileOffset->QuadPart + Length->QuadPart;
360
361 if (UnsignedEnd.QuadPart < UnsignedStart.QuadPart)
362 {
363 DPRINT("File offset out of range\n");
364 IoStatus->Status = STATUS_INVALID_PARAMETER;
365 if (Irp)
366 {
367 DPRINT("Complete lock %p Status %x\n", Irp, IoStatus->Status);
368 FsRtlCompleteLockIrpReal
369 (FileLock->CompleteLockIrpRoutine,
370 Context,
371 Irp,
372 IoStatus->Status,
373 &Status,
374 FileObject);
375 }
376 return FALSE;
377 }
378
379 /* Initialize the lock, if necessary */
380 if (!FileLock->LockInformation)
381 {
382 LockInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(LOCK_INFORMATION), 'FLCK');
383 if (!LockInfo)
384 {
385 IoStatus->Status = STATUS_NO_MEMORY;
386 return FALSE;
387 }
388 FileLock->LockInformation = LockInfo;
389
390 LockInfo->BelongsTo = FileLock;
391 InitializeListHead(&LockInfo->SharedLocks);
392
393 RtlInitializeGenericTable
394 (&LockInfo->RangeTable,
395 LockCompare,
396 LockAllocate,
397 LockFree,
398 NULL);
399
400 KeInitializeSpinLock(&LockInfo->CsqLock);
401 InitializeListHead(&LockInfo->CsqList);
402
403 IoCsqInitializeEx
404 (&LockInfo->Csq,
405 LockInsertIrpEx,
406 LockRemoveIrp,
407 LockPeekNextIrp,
408 LockAcquireQueueLock,
409 LockReleaseQueueLock,
410 LockCompleteCanceledIrp);
411 }
412
413 LockInfo = FileLock->LockInformation;
414 ToInsert.Exclusive.FileLock.FileObject = FileObject;
415 ToInsert.Exclusive.FileLock.StartingByte = *FileOffset;
416 ToInsert.Exclusive.FileLock.EndingByte.QuadPart = FileOffset->QuadPart + Length->QuadPart;
417 ToInsert.Exclusive.FileLock.ProcessId = Process->UniqueProcessId;
418 ToInsert.Exclusive.FileLock.Key = Key;
419 ToInsert.Exclusive.FileLock.ExclusiveLock = ExclusiveLock;
420
421 Conflict = RtlInsertElementGenericTable
422 (FileLock->LockInformation,
423 &ToInsert,
424 sizeof(ToInsert),
425 &InsertedNew);
426
427 if (Conflict && !InsertedNew)
428 {
429 if (Conflict->Exclusive.FileLock.ExclusiveLock || ExclusiveLock)
430 {
431 DPRINT("Conflict %08x%08x:%08x%08x Exc %d (Want Exc %d)\n",
432 Conflict->Exclusive.FileLock.StartingByte.HighPart,
433 Conflict->Exclusive.FileLock.StartingByte.LowPart,
434 Conflict->Exclusive.FileLock.EndingByte.HighPart,
435 Conflict->Exclusive.FileLock.EndingByte.LowPart,
436 Conflict->Exclusive.FileLock.ExclusiveLock,
437 ExclusiveLock);
438 if (FailImmediately)
439 {
440 DPRINT("STATUS_FILE_LOCK_CONFLICT\n");
441 IoStatus->Status = STATUS_FILE_LOCK_CONFLICT;
442 if (Irp)
443 {
444 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n");
445 FsRtlCompleteLockIrpReal
446 (FileLock->CompleteLockIrpRoutine,
447 Context,
448 Irp,
449 IoStatus->Status,
450 &Status,
451 FileObject);
452 }
453 return FALSE;
454 }
455 else
456 {
457 IoStatus->Status = STATUS_PENDING;
458 if (Irp)
459 {
460 Irp->IoStatus.Information = LockInfo->Generation;
461 IoMarkIrpPending(Irp);
462 IoCsqInsertIrpEx
463 (&LockInfo->Csq,
464 Irp,
465 NULL,
466 NULL);
467 }
468 }
469 return FALSE;
470 }
471 else
472 {
473 ULONG i;
474 /* We know of at least one lock in range that's shared. We need to
475 * find out if any more exist and any are exclusive. */
476 for (i = 0; i < RtlNumberGenericTableElements(&LockInfo->RangeTable); i++)
477 {
478 Conflict = RtlGetElementGenericTable(&LockInfo->RangeTable, i);
479
480 /* The first argument will be inserted as a shared range */
481 if (Conflict && (LockCompare(&LockInfo->RangeTable, Conflict, &ToInsert) == GenericEqual))
482 {
483 if (Conflict->Exclusive.FileLock.ExclusiveLock)
484 {
485 /* Found an exclusive match */
486 if (FailImmediately)
487 {
488 IoStatus->Status = STATUS_FILE_LOCK_CONFLICT;
489 DPRINT("STATUS_FILE_LOCK_CONFLICT\n");
490 if (Irp)
491 {
492 DPRINT("STATUS_FILE_LOCK_CONFLICT: Complete\n");
493 FsRtlCompleteLockIrpReal
494 (FileLock->CompleteLockIrpRoutine,
495 Context,
496 Irp,
497 IoStatus->Status,
498 &Status,
499 FileObject);
500 }
501 }
502 else
503 {
504 IoStatus->Status = STATUS_PENDING;
505 if (Irp)
506 {
507 IoMarkIrpPending(Irp);
508 IoCsqInsertIrpEx
509 (&LockInfo->Csq,
510 Irp,
511 NULL,
512 NULL);
513 }
514 }
515 return FALSE;
516 }
517 }
518 }
519
520 DPRINT("Overlapping shared lock %wZ %08x%08x %08x%08x\n",
521 &FileObject->FileName,
522 Conflict->Exclusive.FileLock.StartingByte.HighPart,
523 Conflict->Exclusive.FileLock.StartingByte.LowPart,
524 Conflict->Exclusive.FileLock.EndingByte.HighPart,
525 Conflict->Exclusive.FileLock.EndingByte.LowPart);
526 Conflict = FsRtlpRebuildSharedLockRange(FileLock,
527 LockInfo,
528 &ToInsert);
529 if (!Conflict)
530 {
531 IoStatus->Status = STATUS_NO_MEMORY;
532 if (Irp)
533 {
534 FsRtlCompleteLockIrpReal
535 (FileLock->CompleteLockIrpRoutine,
536 Context,
537 Irp,
538 IoStatus->Status,
539 &Status,
540 FileObject);
541 }
542 }
543
544 /* We got here because there were only overlapping shared locks */
545 /* A shared lock is both a range *and* a list entry. Insert the
546 entry here. */
547
548 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName);
549 NewSharedRange =
550 ExAllocatePoolWithTag(NonPagedPool, sizeof(*NewSharedRange), 'FSRA');
551 if (!NewSharedRange)
552 {
553 IoStatus->Status = STATUS_NO_MEMORY;
554 if (Irp)
555 {
556 FsRtlCompleteLockIrpReal
557 (FileLock->CompleteLockIrpRoutine,
558 Context,
559 Irp,
560 IoStatus->Status,
561 &Status,
562 FileObject);
563 }
564 return FALSE;
565 }
566 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName);
567 NewSharedRange->Start = *FileOffset;
568 NewSharedRange->End.QuadPart = FileOffset->QuadPart + Length->QuadPart;
569 NewSharedRange->Key = Key;
570 NewSharedRange->ProcessId = ToInsert.Exclusive.FileLock.ProcessId;
571 InsertTailList(&LockInfo->SharedLocks, &NewSharedRange->Entry);
572
573 DPRINT("Acquired shared lock %wZ %08x%08x %08x%08x\n",
574 &FileObject->FileName,
575 Conflict->Exclusive.FileLock.StartingByte.HighPart,
576 Conflict->Exclusive.FileLock.StartingByte.LowPart,
577 Conflict->Exclusive.FileLock.EndingByte.HighPart,
578 Conflict->Exclusive.FileLock.EndingByte.LowPart);
579 IoStatus->Status = STATUS_SUCCESS;
580 if (Irp)
581 {
582 FsRtlCompleteLockIrpReal
583 (FileLock->CompleteLockIrpRoutine,
584 Context,
585 Irp,
586 IoStatus->Status,
587 &Status,
588 FileObject);
589 }
590 return TRUE;
591 }
592 }
593 else if (!Conflict)
594 {
595 /* Conflict here is (or would be) the newly inserted element, but we ran
596 * out of space probably. */
597 IoStatus->Status = STATUS_NO_MEMORY;
598 if (Irp)
599 {
600 FsRtlCompleteLockIrpReal
601 (FileLock->CompleteLockIrpRoutine,
602 Context,
603 Irp,
604 IoStatus->Status,
605 &Status,
606 FileObject);
607 }
608 return FALSE;
609 }
610 else
611 {
612 DPRINT("Inserted new lock %wZ %08x%08x %08x%08x exclusive %d\n",
613 &FileObject->FileName,
614 Conflict->Exclusive.FileLock.StartingByte.HighPart,
615 Conflict->Exclusive.FileLock.StartingByte.LowPart,
616 Conflict->Exclusive.FileLock.EndingByte.HighPart,
617 Conflict->Exclusive.FileLock.EndingByte.LowPart,
618 Conflict->Exclusive.FileLock.ExclusiveLock);
619 if (!ExclusiveLock)
620 {
621 NewSharedRange =
622 ExAllocatePoolWithTag(NonPagedPool, sizeof(*NewSharedRange), 'FSRA');
623 if (!NewSharedRange)
624 {
625 IoStatus->Status = STATUS_NO_MEMORY;
626 if (Irp)
627 {
628 FsRtlCompleteLockIrpReal
629 (FileLock->CompleteLockIrpRoutine,
630 Context,
631 Irp,
632 IoStatus->Status,
633 &Status,
634 FileObject);
635 }
636 return FALSE;
637 }
638 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName);
639 NewSharedRange->Start = *FileOffset;
640 NewSharedRange->End.QuadPart = FileOffset->QuadPart + Length->QuadPart;
641 NewSharedRange->Key = Key;
642 NewSharedRange->ProcessId = Process->UniqueProcessId;
643 InsertTailList(&LockInfo->SharedLocks, &NewSharedRange->Entry);
644 }
645
646 /* Assume all is cool, and lock is set */
647 IoStatus->Status = STATUS_SUCCESS;
648
649 if (Irp)
650 {
651 /* Complete the request */
652 FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine,
653 Context,
654 Irp,
655 IoStatus->Status,
656 &Status,
657 FileObject);
658
659 /* Update the status */
660 IoStatus->Status = Status;
661 }
662 }
663
664 return TRUE;
665 }
666
667 /*
668 * @implemented
669 */
670 BOOLEAN
671 NTAPI
672 FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock,
673 IN PIRP Irp)
674 {
675 BOOLEAN Result;
676 PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
677 COMBINED_LOCK_ELEMENT ToFind;
678 PCOMBINED_LOCK_ELEMENT Found;
679 DPRINT("CheckLockForReadAccess(%wZ, Offset %08x%08x, Length %x)\n",
680 &IoStack->FileObject->FileName,
681 IoStack->Parameters.Read.ByteOffset.HighPart,
682 IoStack->Parameters.Read.ByteOffset.LowPart,
683 IoStack->Parameters.Read.Length);
684 if (!FileLock->LockInformation) {
685 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
686 return TRUE;
687 }
688 ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Read.ByteOffset;
689 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
690 ToFind.Exclusive.FileLock.StartingByte.QuadPart +
691 IoStack->Parameters.Read.Length;
692 Found = RtlLookupElementGenericTable
693 (FileLock->LockInformation,
694 &ToFind);
695 if (!Found) {
696 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
697 return TRUE;
698 }
699 Result = !Found->Exclusive.FileLock.ExclusiveLock ||
700 IoStack->Parameters.Read.Key == Found->Exclusive.FileLock.Key;
701 DPRINT("CheckLockForReadAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE");
702 return Result;
703 }
704
705 /*
706 * @implemented
707 */
708 BOOLEAN
709 NTAPI
710 FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock,
711 IN PIRP Irp)
712 {
713 BOOLEAN Result;
714 PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
715 COMBINED_LOCK_ELEMENT ToFind;
716 PCOMBINED_LOCK_ELEMENT Found;
717 PEPROCESS Process = Irp->Tail.Overlay.Thread->ThreadsProcess;
718 DPRINT("CheckLockForWriteAccess(%wZ, Offset %08x%08x, Length %x)\n",
719 &IoStack->FileObject->FileName,
720 IoStack->Parameters.Write.ByteOffset.HighPart,
721 IoStack->Parameters.Write.ByteOffset.LowPart,
722 IoStack->Parameters.Write.Length);
723 if (!FileLock->LockInformation) {
724 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
725 return TRUE;
726 }
727 ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Write.ByteOffset;
728 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
729 ToFind.Exclusive.FileLock.StartingByte.QuadPart +
730 IoStack->Parameters.Write.Length;
731 Found = RtlLookupElementGenericTable
732 (FileLock->LockInformation,
733 &ToFind);
734 if (!Found) {
735 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
736 return TRUE;
737 }
738 Result = Process->UniqueProcessId == Found->Exclusive.FileLock.ProcessId;
739 DPRINT("CheckLockForWriteAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE");
740 return Result;
741 }
742
743 /*
744 * @implemented
745 */
746 BOOLEAN
747 NTAPI
748 FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock,
749 IN PLARGE_INTEGER FileOffset,
750 IN PLARGE_INTEGER Length,
751 IN ULONG Key,
752 IN PFILE_OBJECT FileObject,
753 IN PVOID Process)
754 {
755 PEPROCESS EProcess = Process;
756 COMBINED_LOCK_ELEMENT ToFind;
757 PCOMBINED_LOCK_ELEMENT Found;
758 DPRINT("FsRtlFastCheckLockForRead(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n",
759 &FileObject->FileName,
760 FileOffset->HighPart,
761 FileOffset->LowPart,
762 Length->HighPart,
763 Length->LowPart,
764 Key);
765 ToFind.Exclusive.FileLock.StartingByte = *FileOffset;
766 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
767 FileOffset->QuadPart + Length->QuadPart;
768 if (!FileLock->LockInformation) return TRUE;
769 Found = RtlLookupElementGenericTable
770 (FileLock->LockInformation,
771 &ToFind);
772 if (!Found || !Found->Exclusive.FileLock.ExclusiveLock) return TRUE;
773 return Found->Exclusive.FileLock.Key == Key &&
774 Found->Exclusive.FileLock.ProcessId == EProcess->UniqueProcessId;
775 }
776
777 /*
778 * @implemented
779 */
780 BOOLEAN
781 NTAPI
782 FsRtlFastCheckLockForWrite(IN PFILE_LOCK FileLock,
783 IN PLARGE_INTEGER FileOffset,
784 IN PLARGE_INTEGER Length,
785 IN ULONG Key,
786 IN PFILE_OBJECT FileObject,
787 IN PVOID Process)
788 {
789 BOOLEAN Result;
790 PEPROCESS EProcess = Process;
791 COMBINED_LOCK_ELEMENT ToFind;
792 PCOMBINED_LOCK_ELEMENT Found;
793 DPRINT("FsRtlFastCheckLockForWrite(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n",
794 &FileObject->FileName,
795 FileOffset->HighPart,
796 FileOffset->LowPart,
797 Length->HighPart,
798 Length->LowPart,
799 Key);
800 ToFind.Exclusive.FileLock.StartingByte = *FileOffset;
801 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
802 FileOffset->QuadPart + Length->QuadPart;
803 if (!FileLock->LockInformation) {
804 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject->FileName);
805 return TRUE;
806 }
807 Found = RtlLookupElementGenericTable
808 (FileLock->LockInformation,
809 &ToFind);
810 if (!Found) {
811 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject->FileName);
812 return TRUE;
813 }
814 Result = Found->Exclusive.FileLock.Key == Key &&
815 Found->Exclusive.FileLock.ProcessId == EProcess->UniqueProcessId;
816 DPRINT("CheckForWrite(%wZ) => %s\n", &FileObject->FileName, Result ? "TRUE" : "FALSE");
817 return Result;
818 }
819
820 /*
821 * @implemented
822 */
823 NTSTATUS
824 NTAPI
825 FsRtlFastUnlockSingle(IN PFILE_LOCK FileLock,
826 IN PFILE_OBJECT FileObject,
827 IN PLARGE_INTEGER FileOffset,
828 IN PLARGE_INTEGER Length,
829 IN PEPROCESS Process,
830 IN ULONG Key,
831 IN PVOID Context OPTIONAL,
832 IN BOOLEAN AlreadySynchronized)
833 {
834 BOOLEAN FoundShared = FALSE;
835 PLIST_ENTRY SharedEntry;
836 PLOCK_SHARED_RANGE SharedRange = NULL;
837 COMBINED_LOCK_ELEMENT Find;
838 PCOMBINED_LOCK_ELEMENT Entry;
839 PIRP NextMatchingLockIrp;
840 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
841 DPRINT("FsRtlFastUnlockSingle(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x)\n",
842 &FileObject->FileName,
843 FileOffset->HighPart,
844 FileOffset->LowPart,
845 (int)FileOffset->QuadPart,
846 Length->HighPart,
847 Length->LowPart,
848 (int)Length->QuadPart,
849 Key);
850 // The region to unlock must correspond exactly to a previously locked region
851 // -- msdn
852 // But Windows 2003 doesn't assert on it and simply ignores that parameter
853 // ASSERT(AlreadySynchronized);
854 Find.Exclusive.FileLock.StartingByte = *FileOffset;
855 Find.Exclusive.FileLock.EndingByte.QuadPart =
856 FileOffset->QuadPart + Length->QuadPart;
857 ASSERT(InternalInfo);
858 Entry = RtlLookupElementGenericTable(&InternalInfo->RangeTable, &Find);
859 if (!Entry) {
860 DPRINT("Range not locked %wZ\n", &FileObject->FileName);
861 return STATUS_RANGE_NOT_LOCKED;
862 }
863
864 DPRINT("Found lock entry: Exclusive %d %08x%08x:%08x%08x %wZ\n",
865 Entry->Exclusive.FileLock.ExclusiveLock,
866 Entry->Exclusive.FileLock.StartingByte.HighPart,
867 Entry->Exclusive.FileLock.StartingByte.LowPart,
868 Entry->Exclusive.FileLock.EndingByte.HighPart,
869 Entry->Exclusive.FileLock.EndingByte.LowPart,
870 &FileObject->FileName);
871
872 if (Entry->Exclusive.FileLock.ExclusiveLock)
873 {
874 if (Entry->Exclusive.FileLock.Key != Key ||
875 Entry->Exclusive.FileLock.ProcessId != Process->UniqueProcessId ||
876 Entry->Exclusive.FileLock.StartingByte.QuadPart != FileOffset->QuadPart ||
877 Entry->Exclusive.FileLock.EndingByte.QuadPart !=
878 FileOffset->QuadPart + Length->QuadPart)
879 {
880 DPRINT("Range not locked %wZ\n", &FileObject->FileName);
881 return STATUS_RANGE_NOT_LOCKED;
882 }
883 RtlCopyMemory(&Find, Entry, sizeof(Find));
884 // Remove the old exclusive lock region
885 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry);
886 }
887 else
888 {
889 DPRINT("Shared lock %wZ Start %08x%08x End %08x%08x\n",
890 &FileObject->FileName,
891 Entry->Exclusive.FileLock.StartingByte.HighPart,
892 Entry->Exclusive.FileLock.StartingByte.LowPart,
893 Entry->Exclusive.FileLock.EndingByte.HighPart,
894 Entry->Exclusive.FileLock.EndingByte.LowPart);
895 for (SharedEntry = InternalInfo->SharedLocks.Flink;
896 SharedEntry != &InternalInfo->SharedLocks;
897 SharedEntry = SharedEntry->Flink)
898 {
899 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
900 if (SharedRange->Start.QuadPart == FileOffset->QuadPart &&
901 SharedRange->End.QuadPart == FileOffset->QuadPart + Length->QuadPart &&
902 SharedRange->Key == Key &&
903 SharedRange->ProcessId == Process->UniqueProcessId)
904 {
905 FoundShared = TRUE;
906 DPRINT("Found shared element to delete %wZ Start %08x%08x End %08x%08x Key %x\n",
907 &FileObject->FileName,
908 SharedRange->Start.HighPart,
909 SharedRange->Start.LowPart,
910 SharedRange->End.HighPart,
911 SharedRange->End.LowPart,
912 SharedRange->Key);
913 break;
914 }
915 }
916 if (FoundShared)
917 {
918 /* Remove the found range from the shared range lists */
919 RemoveEntryList(&SharedRange->Entry);
920 ExFreePoolWithTag(SharedRange, 'FSRA');
921 /* We need to rebuild the list of shared ranges. */
922 DPRINT("Removing the lock entry %wZ (%08x%08x:%08x%08x)\n",
923 &FileObject->FileName,
924 Entry->Exclusive.FileLock.StartingByte.HighPart,
925 Entry->Exclusive.FileLock.StartingByte.LowPart,
926 Entry->Exclusive.FileLock.EndingByte.HighPart,
927 Entry->Exclusive.FileLock.EndingByte.LowPart);
928
929 /* Remember what was in there and remove it from the table */
930 Find = *Entry;
931 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, &Find);
932 /* Put shared locks back in place */
933 for (SharedEntry = InternalInfo->SharedLocks.Flink;
934 SharedEntry != &InternalInfo->SharedLocks;
935 SharedEntry = SharedEntry->Flink)
936 {
937 COMBINED_LOCK_ELEMENT LockElement;
938 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
939 LockElement.Exclusive.FileLock.FileObject = FileObject;
940 LockElement.Exclusive.FileLock.StartingByte = SharedRange->Start;
941 LockElement.Exclusive.FileLock.EndingByte = SharedRange->End;
942 LockElement.Exclusive.FileLock.ProcessId = SharedRange->ProcessId;
943 LockElement.Exclusive.FileLock.Key = SharedRange->Key;
944 LockElement.Exclusive.FileLock.ExclusiveLock = FALSE;
945
946 if (LockCompare(&InternalInfo->RangeTable, &Find, &LockElement) != GenericEqual)
947 {
948 DPRINT("Skipping range %08x%08x:%08x%08x\n",
949 LockElement.Exclusive.FileLock.StartingByte.HighPart,
950 LockElement.Exclusive.FileLock.StartingByte.LowPart,
951 LockElement.Exclusive.FileLock.EndingByte.HighPart,
952 LockElement.Exclusive.FileLock.EndingByte.LowPart);
953 continue;
954 }
955 DPRINT("Re-creating range %08x%08x:%08x%08x\n",
956 LockElement.Exclusive.FileLock.StartingByte.HighPart,
957 LockElement.Exclusive.FileLock.StartingByte.LowPart,
958 LockElement.Exclusive.FileLock.EndingByte.HighPart,
959 LockElement.Exclusive.FileLock.EndingByte.LowPart);
960 FsRtlpRebuildSharedLockRange(FileLock, InternalInfo, &LockElement);
961 }
962 }
963 else
964 {
965 return STATUS_RANGE_NOT_LOCKED;
966 }
967 }
968
969 #ifndef NDEBUG
970 DPRINT("Lock still has:\n");
971 for (SharedEntry = InternalInfo->SharedLocks.Flink;
972 SharedEntry != &InternalInfo->SharedLocks;
973 SharedEntry = SharedEntry->Flink)
974 {
975 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
976 DPRINT("Shared element %wZ Offset %08x%08x Length %08x%08x Key %x\n",
977 &FileObject->FileName,
978 SharedRange->Start.HighPart,
979 SharedRange->Start.LowPart,
980 SharedRange->End.HighPart,
981 SharedRange->End.LowPart,
982 SharedRange->Key);
983 }
984 #endif
985
986 // this is definitely the thing we want
987 InternalInfo->Generation++;
988 while ((NextMatchingLockIrp = IoCsqRemoveNextIrp(&InternalInfo->Csq, &Find)))
989 {
990 if (NextMatchingLockIrp->IoStatus.Information == InternalInfo->Generation)
991 {
992 // We've already looked at this one, meaning that we looped.
993 // Put it back and exit.
994 IoCsqInsertIrpEx
995 (&InternalInfo->Csq,
996 NextMatchingLockIrp,
997 NULL,
998 NULL);
999 break;
1000 }
1001 // Got a new lock irp... try to do the new lock operation
1002 // Note that we pick an operation that would succeed at the time
1003 // we looked, but can't guarantee that it won't just be re-queued
1004 // because somebody else snatched part of the range in a new thread.
1005 DPRINT("Locking another IRP %p for %p %wZ\n",
1006 &FileObject->FileName, FileLock, NextMatchingLockIrp);
1007 FsRtlProcessFileLock(InternalInfo->BelongsTo, NextMatchingLockIrp, NULL);
1008 }
1009
1010 DPRINT("Success %wZ\n", &FileObject->FileName);
1011 return STATUS_SUCCESS;
1012 }
1013
1014 /*
1015 * @implemented
1016 */
1017 NTSTATUS
1018 NTAPI
1019 FsRtlFastUnlockAll(IN PFILE_LOCK FileLock,
1020 IN PFILE_OBJECT FileObject,
1021 IN PEPROCESS Process,
1022 IN PVOID Context OPTIONAL)
1023 {
1024 PLIST_ENTRY ListEntry;
1025 PCOMBINED_LOCK_ELEMENT Entry;
1026 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1027 DPRINT("FsRtlFastUnlockAll(%wZ)\n", &FileObject->FileName);
1028 // XXX Synchronize somehow
1029 if (!FileLock->LockInformation) {
1030 DPRINT("Not locked %wZ\n", &FileObject->FileName);
1031 return STATUS_RANGE_NOT_LOCKED; // no locks
1032 }
1033 for (ListEntry = InternalInfo->SharedLocks.Flink;
1034 ListEntry != &InternalInfo->SharedLocks;)
1035 {
1036 LARGE_INTEGER Length;
1037 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry);
1038 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart;
1039 ListEntry = ListEntry->Flink;
1040 if (Range->ProcessId != Process->UniqueProcessId)
1041 continue;
1042 FsRtlFastUnlockSingle
1043 (FileLock,
1044 FileObject,
1045 &Range->Start,
1046 &Length,
1047 Range->ProcessId,
1048 Range->Key,
1049 Context,
1050 TRUE);
1051 }
1052 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE);
1053 Entry;
1054 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE))
1055 {
1056 LARGE_INTEGER Length;
1057 // We'll take the first one to be the list head, and free the others first...
1058 Length.QuadPart =
1059 Entry->Exclusive.FileLock.EndingByte.QuadPart -
1060 Entry->Exclusive.FileLock.StartingByte.QuadPart;
1061 FsRtlFastUnlockSingle
1062 (FileLock,
1063 Entry->Exclusive.FileLock.FileObject,
1064 &Entry->Exclusive.FileLock.StartingByte,
1065 &Length,
1066 Entry->Exclusive.FileLock.ProcessId,
1067 Entry->Exclusive.FileLock.Key,
1068 Context,
1069 TRUE);
1070 }
1071 DPRINT("Done %wZ\n", &FileObject->FileName);
1072 return STATUS_SUCCESS;
1073 }
1074
1075 /*
1076 * @implemented
1077 */
1078 NTSTATUS
1079 NTAPI
1080 FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock,
1081 IN PFILE_OBJECT FileObject,
1082 IN PEPROCESS Process,
1083 IN ULONG Key,
1084 IN PVOID Context OPTIONAL)
1085 {
1086 PLIST_ENTRY ListEntry;
1087 PCOMBINED_LOCK_ELEMENT Entry;
1088 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1089
1090 DPRINT("FsRtlFastUnlockAllByKey(%wZ,Key %x)\n", &FileObject->FileName, Key);
1091
1092 // XXX Synchronize somehow
1093 if (!FileLock->LockInformation) return STATUS_RANGE_NOT_LOCKED; // no locks
1094 for (ListEntry = InternalInfo->SharedLocks.Flink;
1095 ListEntry != &InternalInfo->SharedLocks;)
1096 {
1097 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry);
1098 LARGE_INTEGER Length;
1099 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart;
1100 ListEntry = ListEntry->Flink;
1101 if (Range->ProcessId != Process->UniqueProcessId ||
1102 Range->Key != Key)
1103 continue;
1104 FsRtlFastUnlockSingle
1105 (FileLock,
1106 FileObject,
1107 &Range->Start,
1108 &Length,
1109 Range->ProcessId,
1110 Range->Key,
1111 Context,
1112 TRUE);
1113 }
1114 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE);
1115 Entry;
1116 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE))
1117 {
1118 LARGE_INTEGER Length;
1119 // We'll take the first one to be the list head, and free the others first...
1120 Length.QuadPart =
1121 Entry->Exclusive.FileLock.EndingByte.QuadPart -
1122 Entry->Exclusive.FileLock.StartingByte.QuadPart;
1123 if (Entry->Exclusive.FileLock.Key == Key &&
1124 Entry->Exclusive.FileLock.ProcessId == Process->UniqueProcessId)
1125 {
1126 FsRtlFastUnlockSingle
1127 (FileLock,
1128 Entry->Exclusive.FileLock.FileObject,
1129 &Entry->Exclusive.FileLock.StartingByte,
1130 &Length,
1131 Entry->Exclusive.FileLock.ProcessId,
1132 Entry->Exclusive.FileLock.Key,
1133 Context,
1134 TRUE);
1135 }
1136 }
1137
1138 return STATUS_SUCCESS;
1139 }
1140
1141 /*
1142 * @implemented
1143 */
1144 NTSTATUS
1145 NTAPI
1146 FsRtlProcessFileLock(IN PFILE_LOCK FileLock,
1147 IN PIRP Irp,
1148 IN PVOID Context OPTIONAL)
1149 {
1150 PIO_STACK_LOCATION IoStackLocation;
1151 NTSTATUS Status;
1152 IO_STATUS_BLOCK IoStatusBlock;
1153
1154 /* Get the I/O Stack location */
1155 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
1156 ASSERT(IoStackLocation->MajorFunction == IRP_MJ_LOCK_CONTROL);
1157
1158 /* Clear the I/O status block and check what function this is */
1159 IoStatusBlock.Information = 0;
1160
1161 DPRINT("FsRtlProcessFileLock(%wZ, MinorFunction %x)\n",
1162 &IoStackLocation->FileObject->FileName,
1163 IoStackLocation->MinorFunction);
1164
1165 switch(IoStackLocation->MinorFunction)
1166 {
1167 /* A lock */
1168 case IRP_MN_LOCK:
1169
1170 /* Call the private lock routine */
1171 FsRtlPrivateLock(FileLock,
1172 IoStackLocation->FileObject,
1173 &IoStackLocation->
1174 Parameters.LockControl.ByteOffset,
1175 IoStackLocation->Parameters.LockControl.Length,
1176 IoGetRequestorProcess(Irp),
1177 IoStackLocation->Parameters.LockControl.Key,
1178 IoStackLocation->Flags & SL_FAIL_IMMEDIATELY,
1179 IoStackLocation->Flags & SL_EXCLUSIVE_LOCK,
1180 &IoStatusBlock,
1181 Irp,
1182 Context,
1183 FALSE);
1184 return IoStatusBlock.Status;
1185
1186 /* A single unlock */
1187 case IRP_MN_UNLOCK_SINGLE:
1188
1189 /* Call fast unlock */
1190 IoStatusBlock.Status =
1191 FsRtlFastUnlockSingle(FileLock,
1192 IoStackLocation->FileObject,
1193 &IoStackLocation->Parameters.LockControl.
1194 ByteOffset,
1195 IoStackLocation->Parameters.LockControl.
1196 Length,
1197 IoGetRequestorProcess(Irp),
1198 IoStackLocation->Parameters.LockControl.
1199 Key,
1200 Context,
1201 FALSE);
1202 break;
1203
1204 /* Total unlock */
1205 case IRP_MN_UNLOCK_ALL:
1206
1207 /* Do a fast unlock */
1208 IoStatusBlock.Status = FsRtlFastUnlockAll(FileLock,
1209 IoStackLocation->
1210 FileObject,
1211 IoGetRequestorProcess(Irp),
1212 Context);
1213 break;
1214
1215 /* Unlock by key */
1216 case IRP_MN_UNLOCK_ALL_BY_KEY:
1217
1218 /* Do it */
1219 IoStatusBlock.Status =
1220 FsRtlFastUnlockAllByKey(FileLock,
1221 IoStackLocation->FileObject,
1222 IoGetRequestorProcess(Irp),
1223 IoStackLocation->Parameters.
1224 LockControl.Key,
1225 Context);
1226 break;
1227
1228 /* Invalid request */
1229 default:
1230
1231 /* Complete it */
1232 FsRtlCompleteRequest(Irp, STATUS_INVALID_DEVICE_REQUEST);
1233 IoStatusBlock.Status = STATUS_INVALID_DEVICE_REQUEST;
1234 return STATUS_INVALID_DEVICE_REQUEST;
1235 }
1236
1237 /* Return the status */
1238 DPRINT("Lock IRP %p %x\n", Irp, IoStatusBlock.Status);
1239 FsRtlCompleteLockIrpReal
1240 (FileLock->CompleteLockIrpRoutine,
1241 Context,
1242 Irp,
1243 IoStatusBlock.Status,
1244 &Status,
1245 NULL);
1246 return IoStatusBlock.Status;
1247 }
1248
1249 /*
1250 * @implemented
1251 */
1252 VOID
1253 NTAPI
1254 FsRtlInitializeFileLock (IN PFILE_LOCK FileLock,
1255 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1256 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1257 {
1258 /* Setup the lock */
1259 RtlZeroMemory(FileLock, sizeof(*FileLock));
1260 FileLock->FastIoIsQuestionable = FALSE;
1261 FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
1262 FileLock->UnlockRoutine = UnlockRoutine;
1263 FileLock->LockInformation = NULL;
1264 }
1265
1266 /*
1267 * @implemented
1268 */
1269 VOID
1270 NTAPI
1271 FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock)
1272 {
1273 if (FileLock->LockInformation)
1274 {
1275 PIRP Irp;
1276 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1277 PCOMBINED_LOCK_ELEMENT Entry;
1278 PLIST_ENTRY SharedEntry;
1279 PLOCK_SHARED_RANGE SharedRange;
1280 // MSDN: this completes any remaining lock IRPs
1281 for (SharedEntry = InternalInfo->SharedLocks.Flink;
1282 SharedEntry != &InternalInfo->SharedLocks;)
1283 {
1284 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
1285 SharedEntry = SharedEntry->Flink;
1286 RemoveEntryList(&SharedRange->Entry);
1287 ExFreePoolWithTag(SharedRange, 'FSRA');
1288 }
1289 while ((Entry = RtlGetElementGenericTable(&InternalInfo->RangeTable, 0)) != NULL)
1290 {
1291 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry);
1292 }
1293 while ((Irp = IoCsqRemoveNextIrp(&InternalInfo->Csq, NULL)) != NULL)
1294 {
1295 FsRtlProcessFileLock(FileLock, Irp, NULL);
1296 }
1297 ExFreePoolWithTag(InternalInfo, 'FLCK');
1298 FileLock->LockInformation = NULL;
1299 }
1300 }
1301
1302 /*
1303 * @implemented
1304 */
1305 PFILE_LOCK
1306 NTAPI
1307 FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1308 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1309 {
1310 PFILE_LOCK FileLock;
1311
1312 /* Try to allocate it */
1313 FileLock = ExAllocateFromPagedLookasideList(&FsRtlFileLockLookasideList);
1314 if (FileLock)
1315 {
1316 /* Initialize it */
1317 FsRtlInitializeFileLock(FileLock,
1318 CompleteLockIrpRoutine,
1319 UnlockRoutine);
1320 }
1321
1322 /* Return the lock */
1323 return FileLock;
1324 }
1325
1326 /*
1327 * @implemented
1328 */
1329 VOID
1330 NTAPI
1331 FsRtlFreeFileLock(IN PFILE_LOCK FileLock)
1332 {
1333 /* Uninitialize and free the lock */
1334 FsRtlUninitializeFileLock(FileLock);
1335 ExFreeToPagedLookasideList(&FsRtlFileLockLookasideList, FileLock);
1336 }