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