Synchronize with trunk's revision r57652.
[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 /* 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 else
518 {
519 DPRINT("Overlapping shared lock %wZ %08x%08x %08x%08x\n",
520 &FileObject->FileName,
521 Conflict->Exclusive.FileLock.StartingByte.HighPart,
522 Conflict->Exclusive.FileLock.StartingByte.LowPart,
523 Conflict->Exclusive.FileLock.EndingByte.HighPart,
524 Conflict->Exclusive.FileLock.EndingByte.LowPart);
525 Conflict = FsRtlpRebuildSharedLockRange(FileLock,
526 LockInfo,
527 &ToInsert);
528 if (!Conflict)
529 {
530 IoStatus->Status = STATUS_NO_MEMORY;
531 if (Irp)
532 {
533 FsRtlCompleteLockIrpReal
534 (FileLock->CompleteLockIrpRoutine,
535 Context,
536 Irp,
537 IoStatus->Status,
538 &Status,
539 FileObject);
540 }
541 break;
542 }
543 }
544 }
545 }
546
547 /* We got here because there were only overlapping shared locks */
548 /* A shared lock is both a range *and* a list entry. Insert the
549 entry here. */
550
551 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName);
552 NewSharedRange =
553 ExAllocatePoolWithTag(NonPagedPool, sizeof(*NewSharedRange), 'FSRA');
554 if (!NewSharedRange)
555 {
556 IoStatus->Status = STATUS_NO_MEMORY;
557 if (Irp)
558 {
559 FsRtlCompleteLockIrpReal
560 (FileLock->CompleteLockIrpRoutine,
561 Context,
562 Irp,
563 IoStatus->Status,
564 &Status,
565 FileObject);
566 }
567 return FALSE;
568 }
569 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName);
570 NewSharedRange->Start = ToInsert.Exclusive.FileLock.StartingByte;
571 NewSharedRange->End = ToInsert.Exclusive.FileLock.EndingByte;
572 NewSharedRange->Key = Key;
573 NewSharedRange->ProcessId = ToInsert.Exclusive.FileLock.ProcessId;
574 InsertTailList(&LockInfo->SharedLocks, &NewSharedRange->Entry);
575
576 DPRINT("Acquired shared lock %wZ %08x%08x %08x%08x\n",
577 &FileObject->FileName,
578 Conflict->Exclusive.FileLock.StartingByte.HighPart,
579 Conflict->Exclusive.FileLock.StartingByte.LowPart,
580 Conflict->Exclusive.FileLock.EndingByte.HighPart,
581 Conflict->Exclusive.FileLock.EndingByte.LowPart);
582 IoStatus->Status = STATUS_SUCCESS;
583 if (Irp)
584 {
585 FsRtlCompleteLockIrpReal
586 (FileLock->CompleteLockIrpRoutine,
587 Context,
588 Irp,
589 IoStatus->Status,
590 &Status,
591 FileObject);
592 }
593 return TRUE;
594 }
595 }
596 else if (!Conflict)
597 {
598 /* Conflict here is (or would be) the newly inserted element, but we ran
599 * out of space probably. */
600 IoStatus->Status = STATUS_NO_MEMORY;
601 if (Irp)
602 {
603 FsRtlCompleteLockIrpReal
604 (FileLock->CompleteLockIrpRoutine,
605 Context,
606 Irp,
607 IoStatus->Status,
608 &Status,
609 FileObject);
610 }
611 return FALSE;
612 }
613 else
614 {
615 DPRINT("Inserted new lock %wZ %08x%08x %08x%08x exclusive %d\n",
616 &FileObject->FileName,
617 Conflict->Exclusive.FileLock.StartingByte.HighPart,
618 Conflict->Exclusive.FileLock.StartingByte.LowPart,
619 Conflict->Exclusive.FileLock.EndingByte.HighPart,
620 Conflict->Exclusive.FileLock.EndingByte.LowPart,
621 Conflict->Exclusive.FileLock.ExclusiveLock);
622 if (!ExclusiveLock)
623 {
624 NewSharedRange =
625 ExAllocatePoolWithTag(NonPagedPool, sizeof(*NewSharedRange), 'FSRA');
626 if (!NewSharedRange)
627 {
628 IoStatus->Status = STATUS_NO_MEMORY;
629 if (Irp)
630 {
631 FsRtlCompleteLockIrpReal
632 (FileLock->CompleteLockIrpRoutine,
633 Context,
634 Irp,
635 IoStatus->Status,
636 &Status,
637 FileObject);
638 }
639 return FALSE;
640 }
641 DPRINT("Adding shared lock %wZ\n", &FileObject->FileName);
642 NewSharedRange->Start = ToInsert.Exclusive.FileLock.StartingByte;
643 NewSharedRange->End = ToInsert.Exclusive.FileLock.EndingByte;
644 NewSharedRange->Key = Key;
645 NewSharedRange->ProcessId = ToInsert.Exclusive.FileLock.ProcessId;
646 InsertTailList(&LockInfo->SharedLocks, &NewSharedRange->Entry);
647 }
648
649 /* Assume all is cool, and lock is set */
650 IoStatus->Status = STATUS_SUCCESS;
651
652 if (Irp)
653 {
654 /* Complete the request */
655 FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine,
656 Context,
657 Irp,
658 IoStatus->Status,
659 &Status,
660 FileObject);
661
662 /* Update the status */
663 IoStatus->Status = Status;
664 }
665 }
666
667 return TRUE;
668 }
669
670 /*
671 * @implemented
672 */
673 BOOLEAN
674 NTAPI
675 FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock,
676 IN PIRP Irp)
677 {
678 BOOLEAN Result;
679 PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
680 COMBINED_LOCK_ELEMENT ToFind;
681 PCOMBINED_LOCK_ELEMENT Found;
682 DPRINT("CheckLockForReadAccess(%wZ, Offset %08x%08x, Length %x)\n",
683 &IoStack->FileObject->FileName,
684 IoStack->Parameters.Read.ByteOffset.HighPart,
685 IoStack->Parameters.Read.ByteOffset.LowPart,
686 IoStack->Parameters.Read.Length);
687 if (!FileLock->LockInformation) {
688 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
689 return TRUE;
690 }
691 ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Read.ByteOffset;
692 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
693 ToFind.Exclusive.FileLock.StartingByte.QuadPart +
694 IoStack->Parameters.Read.Length;
695 Found = RtlLookupElementGenericTable
696 (FileLock->LockInformation,
697 &ToFind);
698 if (!Found) {
699 DPRINT("CheckLockForReadAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
700 return TRUE;
701 }
702 Result = !Found->Exclusive.FileLock.ExclusiveLock ||
703 IoStack->Parameters.Read.Key == Found->Exclusive.FileLock.Key;
704 DPRINT("CheckLockForReadAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE");
705 return Result;
706 }
707
708 /*
709 * @implemented
710 */
711 BOOLEAN
712 NTAPI
713 FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock,
714 IN PIRP Irp)
715 {
716 BOOLEAN Result;
717 PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
718 COMBINED_LOCK_ELEMENT ToFind;
719 PCOMBINED_LOCK_ELEMENT Found;
720 PEPROCESS Process = Irp->Tail.Overlay.Thread->ThreadsProcess;
721 DPRINT("CheckLockForWriteAccess(%wZ, Offset %08x%08x, Length %x)\n",
722 &IoStack->FileObject->FileName,
723 IoStack->Parameters.Write.ByteOffset.HighPart,
724 IoStack->Parameters.Write.ByteOffset.LowPart,
725 IoStack->Parameters.Write.Length);
726 if (!FileLock->LockInformation) {
727 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
728 return TRUE;
729 }
730 ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Write.ByteOffset;
731 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
732 ToFind.Exclusive.FileLock.StartingByte.QuadPart +
733 IoStack->Parameters.Write.Length;
734 Found = RtlLookupElementGenericTable
735 (FileLock->LockInformation,
736 &ToFind);
737 if (!Found) {
738 DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
739 return TRUE;
740 }
741 Result = Process->UniqueProcessId == Found->Exclusive.FileLock.ProcessId;
742 DPRINT("CheckLockForWriteAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE");
743 return Result;
744 }
745
746 /*
747 * @implemented
748 */
749 BOOLEAN
750 NTAPI
751 FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock,
752 IN PLARGE_INTEGER FileOffset,
753 IN PLARGE_INTEGER Length,
754 IN ULONG Key,
755 IN PFILE_OBJECT FileObject,
756 IN PVOID Process)
757 {
758 PEPROCESS EProcess = Process;
759 COMBINED_LOCK_ELEMENT ToFind;
760 PCOMBINED_LOCK_ELEMENT Found;
761 DPRINT("FsRtlFastCheckLockForRead(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n",
762 &FileObject->FileName,
763 FileOffset->HighPart,
764 FileOffset->LowPart,
765 Length->HighPart,
766 Length->LowPart,
767 Key);
768 ToFind.Exclusive.FileLock.StartingByte = *FileOffset;
769 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
770 FileOffset->QuadPart + Length->QuadPart;
771 if (!FileLock->LockInformation) return TRUE;
772 Found = RtlLookupElementGenericTable
773 (FileLock->LockInformation,
774 &ToFind);
775 if (!Found || !Found->Exclusive.FileLock.ExclusiveLock) return TRUE;
776 return Found->Exclusive.FileLock.Key == Key &&
777 Found->Exclusive.FileLock.ProcessId == EProcess->UniqueProcessId;
778 }
779
780 /*
781 * @implemented
782 */
783 BOOLEAN
784 NTAPI
785 FsRtlFastCheckLockForWrite(IN PFILE_LOCK FileLock,
786 IN PLARGE_INTEGER FileOffset,
787 IN PLARGE_INTEGER Length,
788 IN ULONG Key,
789 IN PFILE_OBJECT FileObject,
790 IN PVOID Process)
791 {
792 BOOLEAN Result;
793 PEPROCESS EProcess = Process;
794 COMBINED_LOCK_ELEMENT ToFind;
795 PCOMBINED_LOCK_ELEMENT Found;
796 DPRINT("FsRtlFastCheckLockForWrite(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n",
797 &FileObject->FileName,
798 FileOffset->HighPart,
799 FileOffset->LowPart,
800 Length->HighPart,
801 Length->LowPart,
802 Key);
803 ToFind.Exclusive.FileLock.StartingByte = *FileOffset;
804 ToFind.Exclusive.FileLock.EndingByte.QuadPart =
805 FileOffset->QuadPart + Length->QuadPart;
806 if (!FileLock->LockInformation) {
807 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject->FileName);
808 return TRUE;
809 }
810 Found = RtlLookupElementGenericTable
811 (FileLock->LockInformation,
812 &ToFind);
813 if (!Found) {
814 DPRINT("CheckForWrite(%wZ) => TRUE\n", &FileObject->FileName);
815 return TRUE;
816 }
817 Result = Found->Exclusive.FileLock.Key == Key &&
818 Found->Exclusive.FileLock.ProcessId == EProcess->UniqueProcessId;
819 DPRINT("CheckForWrite(%wZ) => %s\n", &FileObject->FileName, Result ? "TRUE" : "FALSE");
820 return Result;
821 }
822
823 /*
824 * @implemented
825 */
826 NTSTATUS
827 NTAPI
828 FsRtlFastUnlockSingle(IN PFILE_LOCK FileLock,
829 IN PFILE_OBJECT FileObject,
830 IN PLARGE_INTEGER FileOffset,
831 IN PLARGE_INTEGER Length,
832 IN PEPROCESS Process,
833 IN ULONG Key,
834 IN PVOID Context OPTIONAL,
835 IN BOOLEAN AlreadySynchronized)
836 {
837 BOOLEAN FoundShared = FALSE;
838 PLIST_ENTRY SharedEntry;
839 PLOCK_SHARED_RANGE SharedRange = NULL;
840 COMBINED_LOCK_ELEMENT Find;
841 PCOMBINED_LOCK_ELEMENT Entry;
842 PIRP NextMatchingLockIrp;
843 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
844 DPRINT("FsRtlFastUnlockSingle(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x)\n",
845 &FileObject->FileName,
846 FileOffset->HighPart,
847 FileOffset->LowPart,
848 (int)FileOffset->QuadPart,
849 Length->HighPart,
850 Length->LowPart,
851 (int)Length->QuadPart,
852 Key);
853 // The region to unlock must correspond exactly to a previously locked region
854 // -- msdn
855 // But Windows 2003 doesn't assert on it and simply ignores that parameter
856 // ASSERT(AlreadySynchronized);
857 Find.Exclusive.FileLock.StartingByte = *FileOffset;
858 Find.Exclusive.FileLock.EndingByte.QuadPart =
859 FileOffset->QuadPart + Length->QuadPart;
860 ASSERT(InternalInfo);
861 Entry = RtlLookupElementGenericTable(&InternalInfo->RangeTable, &Find);
862 if (!Entry) {
863 DPRINT("Range not locked %wZ\n", &FileObject->FileName);
864 return STATUS_RANGE_NOT_LOCKED;
865 }
866
867 DPRINT("Found lock entry: Exclusive %d %08x%08x:%08x%08x %wZ\n",
868 Entry->Exclusive.FileLock.ExclusiveLock,
869 Entry->Exclusive.FileLock.StartingByte.HighPart,
870 Entry->Exclusive.FileLock.StartingByte.LowPart,
871 Entry->Exclusive.FileLock.EndingByte.HighPart,
872 Entry->Exclusive.FileLock.EndingByte.LowPart,
873 &FileObject->FileName);
874
875 if (Entry->Exclusive.FileLock.ExclusiveLock)
876 {
877 if (Entry->Exclusive.FileLock.Key != Key ||
878 Entry->Exclusive.FileLock.ProcessId != Process->UniqueProcessId ||
879 Entry->Exclusive.FileLock.StartingByte.QuadPart != FileOffset->QuadPart ||
880 Entry->Exclusive.FileLock.EndingByte.QuadPart !=
881 FileOffset->QuadPart + Length->QuadPart)
882 {
883 DPRINT("Range not locked %wZ\n", &FileObject->FileName);
884 return STATUS_RANGE_NOT_LOCKED;
885 }
886 RtlCopyMemory(&Find, Entry, sizeof(Find));
887 // Remove the old exclusive lock region
888 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry);
889 }
890 else
891 {
892 DPRINT("Shared lock %wZ Start %08x%08x End %08x%08x\n",
893 &FileObject->FileName,
894 Entry->Exclusive.FileLock.StartingByte.HighPart,
895 Entry->Exclusive.FileLock.StartingByte.LowPart,
896 Entry->Exclusive.FileLock.EndingByte.HighPart,
897 Entry->Exclusive.FileLock.EndingByte.LowPart);
898 for (SharedEntry = InternalInfo->SharedLocks.Flink;
899 SharedEntry != &InternalInfo->SharedLocks;
900 SharedEntry = SharedEntry->Flink)
901 {
902 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
903 if (SharedRange->Start.QuadPart == FileOffset->QuadPart &&
904 SharedRange->End.QuadPart == FileOffset->QuadPart + Length->QuadPart &&
905 SharedRange->Key == Key &&
906 SharedRange->ProcessId == Process->UniqueProcessId)
907 {
908 FoundShared = TRUE;
909 DPRINT("Found shared element to delete %wZ Start %08x%08x End %08x%08x Key %x\n",
910 &FileObject->FileName,
911 SharedRange->Start.HighPart,
912 SharedRange->Start.LowPart,
913 SharedRange->End.HighPart,
914 SharedRange->End.LowPart,
915 SharedRange->Key);
916 break;
917 }
918 }
919 if (FoundShared)
920 {
921 PLIST_ENTRY SharedRangeEntry;
922 PLOCK_SHARED_RANGE WatchSharedRange;
923 COMBINED_LOCK_ELEMENT RemadeElement;
924 Find.Exclusive.FileLock.StartingByte = SharedRange->Start;
925 Find.Exclusive.FileLock.EndingByte = SharedRange->End;
926 SharedEntry = SharedRange->Entry.Flink;
927 RemoveEntryList(&SharedRange->Entry);
928 ExFreePool(SharedRange);
929 /* We need to rebuild the list of shared ranges. */
930 DPRINT("Removing the lock entry %wZ (%08x%08x:%08x%08x)\n",
931 &FileObject->FileName,
932 Entry->Exclusive.FileLock.StartingByte.HighPart,
933 Entry->Exclusive.FileLock.StartingByte.LowPart,
934 Entry->Exclusive.FileLock.EndingByte.HighPart,
935 Entry->Exclusive.FileLock.EndingByte.LowPart);
936 /* Copy */
937 RemadeElement = *Entry;
938 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry);
939 /* Put shared locks back in place */
940 for (SharedRangeEntry = InternalInfo->SharedLocks.Flink;
941 SharedRangeEntry != &InternalInfo->SharedLocks;
942 SharedRangeEntry = SharedRangeEntry->Flink)
943 {
944 COMBINED_LOCK_ELEMENT LockElement;
945 WatchSharedRange = CONTAINING_RECORD(SharedRangeEntry, LOCK_SHARED_RANGE, Entry);
946 LockElement.Exclusive.FileLock.StartingByte = WatchSharedRange->Start;
947 LockElement.Exclusive.FileLock.EndingByte = WatchSharedRange->End;
948 if (LockCompare(&InternalInfo->RangeTable, &RemadeElement, &LockElement) != GenericEqual)
949 {
950 DPRINT("Skipping range %08x%08x:%08x%08x\n",
951 LockElement.Exclusive.FileLock.StartingByte.HighPart,
952 LockElement.Exclusive.FileLock.StartingByte.LowPart,
953 LockElement.Exclusive.FileLock.EndingByte.HighPart,
954 LockElement.Exclusive.FileLock.EndingByte.LowPart);
955 continue;
956 }
957 DPRINT("Re-creating range %08x%08x:%08x%08x\n",
958 LockElement.Exclusive.FileLock.StartingByte.HighPart,
959 LockElement.Exclusive.FileLock.StartingByte.LowPart,
960 LockElement.Exclusive.FileLock.EndingByte.HighPart,
961 LockElement.Exclusive.FileLock.EndingByte.LowPart);
962 RtlZeroMemory(&RemadeElement, sizeof(RemadeElement));
963 RemadeElement.Exclusive.FileLock.StartingByte = WatchSharedRange->Start;
964 RemadeElement.Exclusive.FileLock.EndingByte = WatchSharedRange->End;
965 FsRtlpRebuildSharedLockRange(FileLock, InternalInfo, &RemadeElement);
966 }
967 }
968 else
969 {
970 return STATUS_RANGE_NOT_LOCKED;
971 }
972 }
973
974 DPRINT("Lock still has:\n");
975 for (SharedEntry = InternalInfo->SharedLocks.Flink;
976 SharedEntry != &InternalInfo->SharedLocks;
977 SharedEntry = SharedEntry->Flink)
978 {
979 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
980 DPRINT("Shared element %wZ Offset %08x%08x Length %08x%08x Key %x\n",
981 &FileObject->FileName,
982 SharedRange->Start.HighPart,
983 SharedRange->Start.LowPart,
984 SharedRange->End.HighPart,
985 SharedRange->End.LowPart,
986 SharedRange->Key);
987 }
988
989 // this is definitely the thing we want
990 InternalInfo->Generation++;
991 while ((NextMatchingLockIrp = IoCsqRemoveNextIrp(&InternalInfo->Csq, &Find)))
992 {
993 if (NextMatchingLockIrp->IoStatus.Information == InternalInfo->Generation)
994 {
995 // We've already looked at this one, meaning that we looped.
996 // Put it back and exit.
997 IoCsqInsertIrpEx
998 (&InternalInfo->Csq,
999 NextMatchingLockIrp,
1000 NULL,
1001 NULL);
1002 break;
1003 }
1004 // Got a new lock irp... try to do the new lock operation
1005 // Note that we pick an operation that would succeed at the time
1006 // we looked, but can't guarantee that it won't just be re-queued
1007 // because somebody else snatched part of the range in a new thread.
1008 DPRINT("Locking another IRP %p for %p %wZ\n",
1009 &FileObject->FileName, FileLock, NextMatchingLockIrp);
1010 FsRtlProcessFileLock(InternalInfo->BelongsTo, NextMatchingLockIrp, NULL);
1011 }
1012
1013 DPRINT("Success %wZ\n", &FileObject->FileName);
1014 return STATUS_SUCCESS;
1015 }
1016
1017 /*
1018 * @implemented
1019 */
1020 NTSTATUS
1021 NTAPI
1022 FsRtlFastUnlockAll(IN PFILE_LOCK FileLock,
1023 IN PFILE_OBJECT FileObject,
1024 IN PEPROCESS Process,
1025 IN PVOID Context OPTIONAL)
1026 {
1027 PLIST_ENTRY ListEntry;
1028 PCOMBINED_LOCK_ELEMENT Entry;
1029 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1030 DPRINT("FsRtlFastUnlockAll(%wZ)\n", &FileObject->FileName);
1031 // XXX Synchronize somehow
1032 if (!FileLock->LockInformation) {
1033 DPRINT("Not locked %wZ\n", &FileObject->FileName);
1034 return STATUS_RANGE_NOT_LOCKED; // no locks
1035 }
1036 for (ListEntry = InternalInfo->SharedLocks.Flink;
1037 ListEntry != &InternalInfo->SharedLocks;)
1038 {
1039 LARGE_INTEGER Length;
1040 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry);
1041 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart;
1042 ListEntry = ListEntry->Flink;
1043 if (Range->ProcessId != Process->UniqueProcessId)
1044 continue;
1045 FsRtlFastUnlockSingle
1046 (FileLock,
1047 FileObject,
1048 &Range->Start,
1049 &Length,
1050 Range->ProcessId,
1051 Range->Key,
1052 Context,
1053 TRUE);
1054 }
1055 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE);
1056 Entry;
1057 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE))
1058 {
1059 LARGE_INTEGER Length;
1060 // We'll take the first one to be the list head, and free the others first...
1061 Length.QuadPart =
1062 Entry->Exclusive.FileLock.EndingByte.QuadPart -
1063 Entry->Exclusive.FileLock.StartingByte.QuadPart;
1064 FsRtlFastUnlockSingle
1065 (FileLock,
1066 Entry->Exclusive.FileLock.FileObject,
1067 &Entry->Exclusive.FileLock.StartingByte,
1068 &Length,
1069 Entry->Exclusive.FileLock.ProcessId,
1070 Entry->Exclusive.FileLock.Key,
1071 Context,
1072 TRUE);
1073 }
1074 DPRINT("Done %wZ\n", &FileObject->FileName);
1075 return STATUS_SUCCESS;
1076 }
1077
1078 /*
1079 * @implemented
1080 */
1081 NTSTATUS
1082 NTAPI
1083 FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock,
1084 IN PFILE_OBJECT FileObject,
1085 IN PEPROCESS Process,
1086 IN ULONG Key,
1087 IN PVOID Context OPTIONAL)
1088 {
1089 PLIST_ENTRY ListEntry;
1090 PCOMBINED_LOCK_ELEMENT Entry;
1091 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1092
1093 DPRINT("FsRtlFastUnlockAllByKey(%wZ,Key %x)\n", &FileObject->FileName, Key);
1094
1095 // XXX Synchronize somehow
1096 if (!FileLock->LockInformation) return STATUS_RANGE_NOT_LOCKED; // no locks
1097 for (ListEntry = InternalInfo->SharedLocks.Flink;
1098 ListEntry != &InternalInfo->SharedLocks;)
1099 {
1100 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry);
1101 LARGE_INTEGER Length;
1102 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart;
1103 ListEntry = ListEntry->Flink;
1104 if (Range->ProcessId != Process->UniqueProcessId ||
1105 Range->Key != Key)
1106 continue;
1107 FsRtlFastUnlockSingle
1108 (FileLock,
1109 FileObject,
1110 &Range->Start,
1111 &Length,
1112 Range->ProcessId,
1113 Range->Key,
1114 Context,
1115 TRUE);
1116 }
1117 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE);
1118 Entry;
1119 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE))
1120 {
1121 LARGE_INTEGER Length;
1122 // We'll take the first one to be the list head, and free the others first...
1123 Length.QuadPart =
1124 Entry->Exclusive.FileLock.EndingByte.QuadPart -
1125 Entry->Exclusive.FileLock.StartingByte.QuadPart;
1126 if (Entry->Exclusive.FileLock.Key == Key &&
1127 Entry->Exclusive.FileLock.ProcessId == Process->UniqueProcessId)
1128 {
1129 FsRtlFastUnlockSingle
1130 (FileLock,
1131 Entry->Exclusive.FileLock.FileObject,
1132 &Entry->Exclusive.FileLock.StartingByte,
1133 &Length,
1134 Entry->Exclusive.FileLock.ProcessId,
1135 Entry->Exclusive.FileLock.Key,
1136 Context,
1137 TRUE);
1138 }
1139 }
1140
1141 return STATUS_SUCCESS;
1142 }
1143
1144 /*
1145 * @implemented
1146 */
1147 NTSTATUS
1148 NTAPI
1149 FsRtlProcessFileLock(IN PFILE_LOCK FileLock,
1150 IN PIRP Irp,
1151 IN PVOID Context OPTIONAL)
1152 {
1153 PIO_STACK_LOCATION IoStackLocation;
1154 NTSTATUS Status;
1155 IO_STATUS_BLOCK IoStatusBlock;
1156
1157 /* Get the I/O Stack location */
1158 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
1159 ASSERT(IoStackLocation->MajorFunction == IRP_MJ_LOCK_CONTROL);
1160
1161 /* Clear the I/O status block and check what function this is */
1162 IoStatusBlock.Information = 0;
1163
1164 DPRINT("FsRtlProcessFileLock(%wZ, MinorFunction %x)\n",
1165 &IoStackLocation->FileObject->FileName,
1166 IoStackLocation->MinorFunction);
1167
1168 switch(IoStackLocation->MinorFunction)
1169 {
1170 /* A lock */
1171 case IRP_MN_LOCK:
1172
1173 /* Call the private lock routine */
1174 FsRtlPrivateLock(FileLock,
1175 IoStackLocation->FileObject,
1176 &IoStackLocation->
1177 Parameters.LockControl.ByteOffset,
1178 IoStackLocation->Parameters.LockControl.Length,
1179 IoGetRequestorProcess(Irp),
1180 IoStackLocation->Parameters.LockControl.Key,
1181 IoStackLocation->Flags & SL_FAIL_IMMEDIATELY,
1182 IoStackLocation->Flags & SL_EXCLUSIVE_LOCK,
1183 &IoStatusBlock,
1184 Irp,
1185 Context,
1186 FALSE);
1187 return IoStatusBlock.Status;
1188
1189 /* A single unlock */
1190 case IRP_MN_UNLOCK_SINGLE:
1191
1192 /* Call fast unlock */
1193 IoStatusBlock.Status =
1194 FsRtlFastUnlockSingle(FileLock,
1195 IoStackLocation->FileObject,
1196 &IoStackLocation->Parameters.LockControl.
1197 ByteOffset,
1198 IoStackLocation->Parameters.LockControl.
1199 Length,
1200 IoGetRequestorProcess(Irp),
1201 IoStackLocation->Parameters.LockControl.
1202 Key,
1203 Context,
1204 FALSE);
1205 break;
1206
1207 /* Total unlock */
1208 case IRP_MN_UNLOCK_ALL:
1209
1210 /* Do a fast unlock */
1211 IoStatusBlock.Status = FsRtlFastUnlockAll(FileLock,
1212 IoStackLocation->
1213 FileObject,
1214 IoGetRequestorProcess(Irp),
1215 Context);
1216 break;
1217
1218 /* Unlock by key */
1219 case IRP_MN_UNLOCK_ALL_BY_KEY:
1220
1221 /* Do it */
1222 IoStatusBlock.Status =
1223 FsRtlFastUnlockAllByKey(FileLock,
1224 IoStackLocation->FileObject,
1225 IoGetRequestorProcess(Irp),
1226 IoStackLocation->Parameters.
1227 LockControl.Key,
1228 Context);
1229 break;
1230
1231 /* Invalid request */
1232 default:
1233
1234 /* Complete it */
1235 FsRtlCompleteRequest(Irp, STATUS_INVALID_DEVICE_REQUEST);
1236 IoStatusBlock.Status = STATUS_INVALID_DEVICE_REQUEST;
1237 return STATUS_INVALID_DEVICE_REQUEST;
1238 }
1239
1240 /* Return the status */
1241 DPRINT("Lock IRP %p %x\n", Irp, IoStatusBlock.Status);
1242 FsRtlCompleteLockIrpReal
1243 (FileLock->CompleteLockIrpRoutine,
1244 Context,
1245 Irp,
1246 IoStatusBlock.Status,
1247 &Status,
1248 NULL);
1249 return IoStatusBlock.Status;
1250 }
1251
1252 /*
1253 * @implemented
1254 */
1255 VOID
1256 NTAPI
1257 FsRtlInitializeFileLock (IN PFILE_LOCK FileLock,
1258 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1259 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1260 {
1261 /* Setup the lock */
1262 RtlZeroMemory(FileLock, sizeof(*FileLock));
1263 FileLock->FastIoIsQuestionable = FALSE;
1264 FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
1265 FileLock->UnlockRoutine = UnlockRoutine;
1266 FileLock->LockInformation = NULL;
1267 }
1268
1269 /*
1270 * @implemented
1271 */
1272 VOID
1273 NTAPI
1274 FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock)
1275 {
1276 if (FileLock->LockInformation)
1277 {
1278 PIRP Irp;
1279 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1280 PCOMBINED_LOCK_ELEMENT Entry;
1281 PLIST_ENTRY SharedEntry;
1282 PLOCK_SHARED_RANGE SharedRange;
1283 // MSDN: this completes any remaining lock IRPs
1284 for (SharedEntry = InternalInfo->SharedLocks.Flink;
1285 SharedEntry != &InternalInfo->SharedLocks;)
1286 {
1287 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
1288 SharedEntry = SharedEntry->Flink;
1289 RemoveEntryList(SharedEntry);
1290 ExFreePool(SharedRange);
1291 }
1292 while ((Entry = RtlGetElementGenericTable(&InternalInfo->RangeTable, 0)) != NULL)
1293 {
1294 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry);
1295 }
1296 while ((Irp = IoCsqRemoveNextIrp(&InternalInfo->Csq, NULL)) != NULL)
1297 {
1298 FsRtlProcessFileLock(FileLock, Irp, NULL);
1299 }
1300 ExFreePoolWithTag(InternalInfo, 'FLCK');
1301 FileLock->LockInformation = NULL;
1302 }
1303 }
1304
1305 /*
1306 * @implemented
1307 */
1308 PFILE_LOCK
1309 NTAPI
1310 FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1311 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1312 {
1313 PFILE_LOCK FileLock;
1314
1315 /* Try to allocate it */
1316 FileLock = ExAllocateFromPagedLookasideList(&FsRtlFileLockLookasideList);
1317 if (FileLock)
1318 {
1319 /* Initialize it */
1320 FsRtlInitializeFileLock(FileLock,
1321 CompleteLockIrpRoutine,
1322 UnlockRoutine);
1323 }
1324
1325 /* Return the lock */
1326 return FileLock;
1327 }
1328
1329 /*
1330 * @implemented
1331 */
1332 VOID
1333 NTAPI
1334 FsRtlFreeFileLock(IN PFILE_LOCK FileLock)
1335 {
1336 /* Uninitialize and free the lock */
1337 FsRtlUninitializeFileLock(FileLock);
1338 ExFreeToPagedLookasideList(&FsRtlFileLockLookasideList, FileLock);
1339 }