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
LockAllocate(PRTL_GENERIC_TABLE Table,CLONG Bytes)67 static PVOID NTAPI LockAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes)
68 {
69 PVOID Result;
70 Result = ExAllocatePoolWithTag(NonPagedPool, Bytes, TAG_TABLE);
71 DPRINT("LockAllocate(%lu) => %p\n", Bytes, Result);
72 return Result;
73 }
74
LockFree(PRTL_GENERIC_TABLE Table,PVOID Buffer)75 static VOID NTAPI LockFree(PRTL_GENERIC_TABLE Table, PVOID Buffer)
76 {
77 DPRINT("LockFree(%p)\n", Buffer);
78 ExFreePoolWithTag(Buffer, TAG_TABLE);
79 }
80
LockCompare(PRTL_GENERIC_TABLE Table,PVOID PtrA,PVOID PtrB)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
LockInsertIrpEx(PIO_CSQ Csq,PIRP Irp,PVOID InsertContext)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
LockRemoveIrp(PIO_CSQ Csq,PIRP Irp)130 static VOID NTAPI LockRemoveIrp(PIO_CSQ Csq, PIRP Irp)
131 {
132 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
133 }
134
LockPeekNextIrp(PIO_CSQ Csq,PIRP Irp,PVOID PeekContext)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
LockAcquireQueueLock(PIO_CSQ Csq,PKIRQL Irql)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
LockReleaseQueueLock(PIO_CSQ Csq,KIRQL Irql)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
LockCompleteCanceledIrp(PIO_CSQ Csq,PIRP Irp)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
FsRtlCompleteLockIrpReal(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteRoutine,IN PVOID Context,IN PIRP Irp,IN NTSTATUS Status,OUT PNTSTATUS NewStatus,IN PFILE_OBJECT FileObject OPTIONAL)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
FsRtlGetNextFileLock(IN PFILE_LOCK FileLock,IN BOOLEAN Restart)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
FsRtlpExpandLockElement(PCOMBINED_LOCK_ELEMENT ToExpand,PCOMBINED_LOCK_ELEMENT Conflict)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
FsRtlpRebuildSharedLockRange(PFILE_LOCK FileLock,PLOCK_INFORMATION LockInfo,PCOMBINED_LOCK_ELEMENT Conflict)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
FsRtlPrivateLock(IN PFILE_LOCK FileLock,IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN PLARGE_INTEGER Length,IN PEPROCESS Process,IN ULONG Key,IN BOOLEAN FailImmediately,IN BOOLEAN ExclusiveLock,OUT PIO_STATUS_BLOCK IoStatus,IN PIRP Irp OPTIONAL,IN PVOID Context OPTIONAL,IN BOOLEAN AlreadySynchronized)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 %u, Exclusive %u)\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), TAG_FLOCK);
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;
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 %u (Want Exc %u)\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), TAG_RANGE);
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 %u\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), TAG_RANGE);
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;
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
FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock,IN PIRP Irp)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
FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock,IN PIRP Irp)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 == 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
FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock,IN PLARGE_INTEGER FileOffset,IN PLARGE_INTEGER Length,IN ULONG Key,IN PFILE_OBJECT FileObject,IN PVOID Process)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;
775 }
776
777 /*
778 * @implemented
779 */
780 BOOLEAN
781 NTAPI
FsRtlFastCheckLockForWrite(IN PFILE_LOCK FileLock,IN PLARGE_INTEGER FileOffset,IN PLARGE_INTEGER Length,IN ULONG Key,IN PFILE_OBJECT FileObject,IN PVOID Process)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;
816 DPRINT("CheckForWrite(%wZ) => %s\n", &FileObject->FileName, Result ? "TRUE" : "FALSE");
817 return Result;
818 }
819
820 /*
821 * @implemented
822 */
823 NTSTATUS
824 NTAPI
FsRtlFastUnlockSingle(IN PFILE_LOCK FileLock,IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN PLARGE_INTEGER Length,IN PEPROCESS Process,IN ULONG Key,IN PVOID Context OPTIONAL,IN BOOLEAN AlreadySynchronized)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 if (!InternalInfo) {
858 DPRINT("File not previously locked (ever)\n");
859 return STATUS_RANGE_NOT_LOCKED;
860 }
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 %u %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 ||
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)
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 /* Remove the found range from the shared range lists */
922 RemoveEntryList(&SharedRange->Entry);
923 ExFreePoolWithTag(SharedRange, TAG_RANGE);
924 /* We need to rebuild the list of shared ranges. */
925 DPRINT("Removing the lock entry %wZ (%08x%08x:%08x%08x)\n",
926 &FileObject->FileName,
927 Entry->Exclusive.FileLock.StartingByte.HighPart,
928 Entry->Exclusive.FileLock.StartingByte.LowPart,
929 Entry->Exclusive.FileLock.EndingByte.HighPart,
930 Entry->Exclusive.FileLock.EndingByte.LowPart);
931
932 /* Remember what was in there and remove it from the table */
933 Find = *Entry;
934 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, &Find);
935 /* Put shared locks back in place */
936 for (SharedEntry = InternalInfo->SharedLocks.Flink;
937 SharedEntry != &InternalInfo->SharedLocks;
938 SharedEntry = SharedEntry->Flink)
939 {
940 COMBINED_LOCK_ELEMENT LockElement;
941 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
942 LockElement.Exclusive.FileLock.FileObject = FileObject;
943 LockElement.Exclusive.FileLock.StartingByte = SharedRange->Start;
944 LockElement.Exclusive.FileLock.EndingByte = SharedRange->End;
945 LockElement.Exclusive.FileLock.ProcessId = SharedRange->ProcessId;
946 LockElement.Exclusive.FileLock.Key = SharedRange->Key;
947 LockElement.Exclusive.FileLock.ExclusiveLock = FALSE;
948
949 if (LockCompare(&InternalInfo->RangeTable, &Find, &LockElement) != GenericEqual)
950 {
951 DPRINT("Skipping range %08x%08x:%08x%08x\n",
952 LockElement.Exclusive.FileLock.StartingByte.HighPart,
953 LockElement.Exclusive.FileLock.StartingByte.LowPart,
954 LockElement.Exclusive.FileLock.EndingByte.HighPart,
955 LockElement.Exclusive.FileLock.EndingByte.LowPart);
956 continue;
957 }
958 DPRINT("Re-creating range %08x%08x:%08x%08x\n",
959 LockElement.Exclusive.FileLock.StartingByte.HighPart,
960 LockElement.Exclusive.FileLock.StartingByte.LowPart,
961 LockElement.Exclusive.FileLock.EndingByte.HighPart,
962 LockElement.Exclusive.FileLock.EndingByte.LowPart);
963 FsRtlpRebuildSharedLockRange(FileLock, InternalInfo, &LockElement);
964 }
965 }
966 else
967 {
968 return STATUS_RANGE_NOT_LOCKED;
969 }
970 }
971
972 #ifndef NDEBUG
973 DPRINT("Lock still has:\n");
974 for (SharedEntry = InternalInfo->SharedLocks.Flink;
975 SharedEntry != &InternalInfo->SharedLocks;
976 SharedEntry = SharedEntry->Flink)
977 {
978 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
979 DPRINT("Shared element %wZ Offset %08x%08x Length %08x%08x Key %x\n",
980 &FileObject->FileName,
981 SharedRange->Start.HighPart,
982 SharedRange->Start.LowPart,
983 SharedRange->End.HighPart,
984 SharedRange->End.LowPart,
985 SharedRange->Key);
986 }
987 #endif
988
989 // this is definitely the thing we want
990 InternalInfo->Generation++;
991 while ((NextMatchingLockIrp = IoCsqRemoveNextIrp(&InternalInfo->Csq, &Find)))
992 {
993 NTSTATUS Status;
994 if (NextMatchingLockIrp->IoStatus.Information == InternalInfo->Generation)
995 {
996 // We've already looked at this one, meaning that we looped.
997 // Put it back and exit.
998 IoCsqInsertIrpEx
999 (&InternalInfo->Csq,
1000 NextMatchingLockIrp,
1001 NULL,
1002 NULL);
1003 break;
1004 }
1005 // Got a new lock irp... try to do the new lock operation
1006 // Note that we pick an operation that would succeed at the time
1007 // we looked, but can't guarantee that it won't just be re-queued
1008 // because somebody else snatched part of the range in a new thread.
1009 DPRINT("Locking another IRP %p for %p %wZ\n",
1010 NextMatchingLockIrp, FileLock, &FileObject->FileName);
1011 Status = FsRtlProcessFileLock(InternalInfo->BelongsTo, NextMatchingLockIrp, NULL);
1012 if (!NT_SUCCESS(Status))
1013 return Status;
1014 }
1015
1016 DPRINT("Success %wZ\n", &FileObject->FileName);
1017 return STATUS_SUCCESS;
1018 }
1019
1020 /*
1021 * @implemented
1022 */
1023 NTSTATUS
1024 NTAPI
FsRtlFastUnlockAll(IN PFILE_LOCK FileLock,IN PFILE_OBJECT FileObject,IN PEPROCESS Process,IN PVOID Context OPTIONAL)1025 FsRtlFastUnlockAll(IN PFILE_LOCK FileLock,
1026 IN PFILE_OBJECT FileObject,
1027 IN PEPROCESS Process,
1028 IN PVOID Context OPTIONAL)
1029 {
1030 PLIST_ENTRY ListEntry;
1031 PCOMBINED_LOCK_ELEMENT Entry;
1032 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1033 DPRINT("FsRtlFastUnlockAll(%wZ)\n", &FileObject->FileName);
1034 // XXX Synchronize somehow
1035 if (!FileLock->LockInformation) {
1036 DPRINT("Not locked %wZ\n", &FileObject->FileName);
1037 return STATUS_RANGE_NOT_LOCKED; // no locks
1038 }
1039 for (ListEntry = InternalInfo->SharedLocks.Flink;
1040 ListEntry != &InternalInfo->SharedLocks;)
1041 {
1042 LARGE_INTEGER Length;
1043 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry);
1044 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart;
1045 ListEntry = ListEntry->Flink;
1046 if (Range->ProcessId != Process)
1047 continue;
1048 FsRtlFastUnlockSingle
1049 (FileLock,
1050 FileObject,
1051 &Range->Start,
1052 &Length,
1053 Range->ProcessId,
1054 Range->Key,
1055 Context,
1056 TRUE);
1057 }
1058 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE);
1059 Entry;
1060 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE))
1061 {
1062 LARGE_INTEGER Length;
1063 // We'll take the first one to be the list head, and free the others first...
1064 Length.QuadPart =
1065 Entry->Exclusive.FileLock.EndingByte.QuadPart -
1066 Entry->Exclusive.FileLock.StartingByte.QuadPart;
1067 FsRtlFastUnlockSingle
1068 (FileLock,
1069 Entry->Exclusive.FileLock.FileObject,
1070 &Entry->Exclusive.FileLock.StartingByte,
1071 &Length,
1072 Entry->Exclusive.FileLock.ProcessId,
1073 Entry->Exclusive.FileLock.Key,
1074 Context,
1075 TRUE);
1076 }
1077 DPRINT("Done %wZ\n", &FileObject->FileName);
1078 return STATUS_SUCCESS;
1079 }
1080
1081 /*
1082 * @implemented
1083 */
1084 NTSTATUS
1085 NTAPI
FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock,IN PFILE_OBJECT FileObject,IN PEPROCESS Process,IN ULONG Key,IN PVOID Context OPTIONAL)1086 FsRtlFastUnlockAllByKey(IN PFILE_LOCK FileLock,
1087 IN PFILE_OBJECT FileObject,
1088 IN PEPROCESS Process,
1089 IN ULONG Key,
1090 IN PVOID Context OPTIONAL)
1091 {
1092 PLIST_ENTRY ListEntry;
1093 PCOMBINED_LOCK_ELEMENT Entry;
1094 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1095
1096 DPRINT("FsRtlFastUnlockAllByKey(%wZ,Key %x)\n", &FileObject->FileName, Key);
1097
1098 // XXX Synchronize somehow
1099 if (!FileLock->LockInformation) return STATUS_RANGE_NOT_LOCKED; // no locks
1100 for (ListEntry = InternalInfo->SharedLocks.Flink;
1101 ListEntry != &InternalInfo->SharedLocks;)
1102 {
1103 PLOCK_SHARED_RANGE Range = CONTAINING_RECORD(ListEntry, LOCK_SHARED_RANGE, Entry);
1104 LARGE_INTEGER Length;
1105 Length.QuadPart = Range->End.QuadPart - Range->Start.QuadPart;
1106 ListEntry = ListEntry->Flink;
1107 if (Range->ProcessId != Process ||
1108 Range->Key != Key)
1109 continue;
1110 FsRtlFastUnlockSingle
1111 (FileLock,
1112 FileObject,
1113 &Range->Start,
1114 &Length,
1115 Range->ProcessId,
1116 Range->Key,
1117 Context,
1118 TRUE);
1119 }
1120 for (Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, TRUE);
1121 Entry;
1122 Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE))
1123 {
1124 LARGE_INTEGER Length;
1125 // We'll take the first one to be the list head, and free the others first...
1126 Length.QuadPart =
1127 Entry->Exclusive.FileLock.EndingByte.QuadPart -
1128 Entry->Exclusive.FileLock.StartingByte.QuadPart;
1129 if (Entry->Exclusive.FileLock.Key == Key &&
1130 Entry->Exclusive.FileLock.ProcessId == Process)
1131 {
1132 FsRtlFastUnlockSingle
1133 (FileLock,
1134 Entry->Exclusive.FileLock.FileObject,
1135 &Entry->Exclusive.FileLock.StartingByte,
1136 &Length,
1137 Entry->Exclusive.FileLock.ProcessId,
1138 Entry->Exclusive.FileLock.Key,
1139 Context,
1140 TRUE);
1141 }
1142 }
1143
1144 return STATUS_SUCCESS;
1145 }
1146
1147 /*
1148 * @implemented
1149 */
1150 NTSTATUS
1151 NTAPI
FsRtlProcessFileLock(IN PFILE_LOCK FileLock,IN PIRP Irp,IN PVOID Context OPTIONAL)1152 FsRtlProcessFileLock(IN PFILE_LOCK FileLock,
1153 IN PIRP Irp,
1154 IN PVOID Context OPTIONAL)
1155 {
1156 PIO_STACK_LOCATION IoStackLocation;
1157 NTSTATUS Status;
1158 IO_STATUS_BLOCK IoStatusBlock;
1159
1160 /* Get the I/O Stack location */
1161 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
1162 ASSERT(IoStackLocation->MajorFunction == IRP_MJ_LOCK_CONTROL);
1163
1164 /* Clear the I/O status block and check what function this is */
1165 IoStatusBlock.Information = 0;
1166
1167 DPRINT("FsRtlProcessFileLock(%wZ, MinorFunction %x)\n",
1168 &IoStackLocation->FileObject->FileName,
1169 IoStackLocation->MinorFunction);
1170
1171 switch(IoStackLocation->MinorFunction)
1172 {
1173 /* A lock */
1174 case IRP_MN_LOCK:
1175 {
1176 /* Call the private lock routine */
1177 BOOLEAN Result = FsRtlPrivateLock(FileLock,
1178 IoStackLocation->FileObject,
1179 &IoStackLocation->Parameters.LockControl.ByteOffset,
1180 IoStackLocation->Parameters.LockControl.Length,
1181 IoGetRequestorProcess(Irp),
1182 IoStackLocation->Parameters.LockControl.Key,
1183 IoStackLocation->Flags & SL_FAIL_IMMEDIATELY,
1184 IoStackLocation->Flags & SL_EXCLUSIVE_LOCK,
1185 &IoStatusBlock,
1186 Irp,
1187 Context,
1188 FALSE);
1189 /* FsRtlPrivateLock has _Must_inspect_result_. Just check this is consistent on debug builds */
1190 NT_ASSERT(Result == NT_SUCCESS(IoStatusBlock.Status));
1191 (void)Result;
1192 return IoStatusBlock.Status;
1193 }
1194 /* A single unlock */
1195 case IRP_MN_UNLOCK_SINGLE:
1196
1197 /* Call fast unlock */
1198 IoStatusBlock.Status =
1199 FsRtlFastUnlockSingle(FileLock,
1200 IoStackLocation->FileObject,
1201 &IoStackLocation->Parameters.LockControl.
1202 ByteOffset,
1203 IoStackLocation->Parameters.LockControl.
1204 Length,
1205 IoGetRequestorProcess(Irp),
1206 IoStackLocation->Parameters.LockControl.
1207 Key,
1208 Context,
1209 FALSE);
1210 break;
1211
1212 /* Total unlock */
1213 case IRP_MN_UNLOCK_ALL:
1214
1215 /* Do a fast unlock */
1216 IoStatusBlock.Status = FsRtlFastUnlockAll(FileLock,
1217 IoStackLocation->
1218 FileObject,
1219 IoGetRequestorProcess(Irp),
1220 Context);
1221 break;
1222
1223 /* Unlock by key */
1224 case IRP_MN_UNLOCK_ALL_BY_KEY:
1225
1226 /* Do it */
1227 IoStatusBlock.Status =
1228 FsRtlFastUnlockAllByKey(FileLock,
1229 IoStackLocation->FileObject,
1230 IoGetRequestorProcess(Irp),
1231 IoStackLocation->Parameters.
1232 LockControl.Key,
1233 Context);
1234 break;
1235
1236 /* Invalid request */
1237 default:
1238
1239 /* Complete it */
1240 FsRtlCompleteRequest(Irp, STATUS_INVALID_DEVICE_REQUEST);
1241 IoStatusBlock.Status = STATUS_INVALID_DEVICE_REQUEST;
1242 return STATUS_INVALID_DEVICE_REQUEST;
1243 }
1244
1245 /* Return the status */
1246 DPRINT("Lock IRP %p %x\n", Irp, IoStatusBlock.Status);
1247 FsRtlCompleteLockIrpReal
1248 (FileLock->CompleteLockIrpRoutine,
1249 Context,
1250 Irp,
1251 IoStatusBlock.Status,
1252 &Status,
1253 NULL);
1254 return IoStatusBlock.Status;
1255 }
1256
1257 /*
1258 * @implemented
1259 */
1260 VOID
1261 NTAPI
FsRtlInitializeFileLock(IN PFILE_LOCK FileLock,IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)1262 FsRtlInitializeFileLock (IN PFILE_LOCK FileLock,
1263 IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1264 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1265 {
1266 /* Setup the lock */
1267 RtlZeroMemory(FileLock, sizeof(*FileLock));
1268 FileLock->FastIoIsQuestionable = FALSE;
1269 FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
1270 FileLock->UnlockRoutine = UnlockRoutine;
1271 FileLock->LockInformation = NULL;
1272 }
1273
1274 /*
1275 * @implemented
1276 */
1277 VOID
1278 NTAPI
FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock)1279 FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock)
1280 {
1281 if (FileLock->LockInformation)
1282 {
1283 PIRP Irp;
1284 PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
1285 PCOMBINED_LOCK_ELEMENT Entry;
1286 PLIST_ENTRY SharedEntry;
1287 PLOCK_SHARED_RANGE SharedRange;
1288 // MSDN: this completes any remaining lock IRPs
1289 for (SharedEntry = InternalInfo->SharedLocks.Flink;
1290 SharedEntry != &InternalInfo->SharedLocks;)
1291 {
1292 SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
1293 SharedEntry = SharedEntry->Flink;
1294 RemoveEntryList(&SharedRange->Entry);
1295 ExFreePoolWithTag(SharedRange, TAG_RANGE);
1296 }
1297 while ((Entry = RtlGetElementGenericTable(&InternalInfo->RangeTable, 0)) != NULL)
1298 {
1299 RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry);
1300 }
1301 while ((Irp = IoCsqRemoveNextIrp(&InternalInfo->Csq, NULL)) != NULL)
1302 {
1303 NTSTATUS Status = FsRtlProcessFileLock(FileLock, Irp, NULL);
1304 /* FsRtlProcessFileLock has _Must_inspect_result_ */
1305 NT_ASSERT(NT_SUCCESS(Status));
1306 (void)Status;
1307 }
1308 ExFreePoolWithTag(InternalInfo, TAG_FLOCK);
1309 FileLock->LockInformation = NULL;
1310 }
1311 }
1312
1313 /*
1314 * @implemented
1315 */
1316 PFILE_LOCK
1317 NTAPI
FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)1318 FsRtlAllocateFileLock(IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
1319 IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL)
1320 {
1321 PFILE_LOCK FileLock;
1322
1323 /* Try to allocate it */
1324 FileLock = ExAllocateFromPagedLookasideList(&FsRtlFileLockLookasideList);
1325 if (FileLock)
1326 {
1327 /* Initialize it */
1328 FsRtlInitializeFileLock(FileLock,
1329 CompleteLockIrpRoutine,
1330 UnlockRoutine);
1331 }
1332
1333 /* Return the lock */
1334 return FileLock;
1335 }
1336
1337 /*
1338 * @implemented
1339 */
1340 VOID
1341 NTAPI
FsRtlFreeFileLock(IN PFILE_LOCK FileLock)1342 FsRtlFreeFileLock(IN PFILE_LOCK FileLock)
1343 {
1344 /* Uninitialize and free the lock */
1345 FsRtlUninitializeFileLock(FileLock);
1346 ExFreeToPagedLookasideList(&FsRtlFileLockLookasideList, FileLock);
1347 }
1348