Author: arty Date: Fri Nov 20 02:56:45 2009 New Revision: 44230
URL: http://svn.reactos.org/svn/reactos?rev=44230&view=rev Log: Rough cut at file locks. Not complete yet (probably).
Modified: branches/arty-newcc/ntoskrnl/fsrtl/filelock.c
Modified: branches/arty-newcc/ntoskrnl/fsrtl/filelock.c URL: http://svn.reactos.org/svn/reactos/branches/arty-newcc/ntoskrnl/fsrtl/filelo... ============================================================================== --- branches/arty-newcc/ntoskrnl/fsrtl/filelock.c [iso-8859-1] (original) +++ branches/arty-newcc/ntoskrnl/fsrtl/filelock.c [iso-8859-1] Fri Nov 20 02:56:45 2009 @@ -9,14 +9,176 @@ /* INCLUDES ******************************************************************/
#include <ntoskrnl.h> -#define NDEBUG +//#define NDEBUG #include <debug.h>
/* GLOBALS *******************************************************************/
PAGED_LOOKASIDE_LIST FsRtlFileLockLookasideList;
+/* Note: this aligns the two types of lock entry structs so we can access the + FILE_LOCK_INFO part in common. Add elements after Shared if new stuff is needed. +*/ +typedef union _COMBINED_LOCK_ELEMENT +{ + struct + { + LIST_ENTRY dummy; + FILE_SHARED_LOCK_ENTRY Shared; + }; + FILE_EXCLUSIVE_LOCK_ENTRY Exclusive; +} +COMBINED_LOCK_ELEMENT, *PCOMBINED_LOCK_ELEMENT; + +typedef struct _LOCK_INFORMATION +{ + RTL_GENERIC_TABLE RangeTable; + IO_CSQ Csq; + KSPIN_LOCK CsqLock; + PFILE_LOCK BelongsTo; +} +LOCK_INFORMATION, *PLOCK_INFORMATION; + /* PRIVATE FUNCTIONS *********************************************************/ + +VOID +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); + +/* Generic table methods */ + +static PVOID NTAPI LockAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes) +{ + PVOID Result; + Result = ExAllocatePoolWithTag(NonPagedPool, Bytes, 'FLCK'); + DPRINT("LockAllocate(%d) => %p\n", Bytes, Result); + return Result; +} + +static VOID NTAPI LockFree(PRTL_GENERIC_TABLE Table, PVOID Buffer) +{ + DPRINT("LockFree(%p)\n", Buffer); + ExFreePoolWithTag(Buffer, 'FLCK'); +} + +static RTL_GENERIC_COMPARE_RESULTS NTAPI LockCompare +(PRTL_GENERIC_TABLE Table, PVOID PtrA, PVOID PtrB) +{ + PCOMBINED_LOCK_ELEMENT A = PtrA, B = PtrB; + RTL_GENERIC_COMPARE_RESULTS Result; + DPRINT("Starting to compare element %x to element %x\n", PtrA, PtrB); + Result = + (A->Exclusive.FileLock.EndingByte.QuadPart < + B->Exclusive.FileLock.StartingByte.QuadPart) ? GenericLessThan : + (A->Exclusive.FileLock.StartingByte.QuadPart > + B->Exclusive.FileLock.EndingByte.QuadPart) ? GenericGreaterThan : + GenericEqual; + DPRINT("Compare(%x:%x) %x-%x to %x-%x => %d\n", + A,B, + A->Exclusive.FileLock.StartingByte.LowPart, + A->Exclusive.FileLock.EndingByte.LowPart, + B->Exclusive.FileLock.StartingByte.LowPart, + B->Exclusive.FileLock.EndingByte.LowPart, + Result); + return Result; +} + +/* CSQ methods */ + +static NTSTATUS NTAPI LockInsertIrpEx +(PIO_CSQ Csq, + PIRP Irp, + PVOID InsertContext) +{ + PCOMBINED_LOCK_ELEMENT LockElement = InsertContext; + InsertTailList(&LockElement->Exclusive.ListEntry, &Irp->Tail.Overlay.ListEntry); + return STATUS_SUCCESS; +} + +static VOID NTAPI LockRemoveIrp(PIO_CSQ Csq, PIRP Irp) +{ + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); +} + +static PIRP NTAPI LockPeekNextIrp(PIO_CSQ Csq, PIRP Irp, PVOID PeekContext) +{ + // Context will be a COMBINED_LOCK_ELEMENT. We're looking for a + // lock that can be acquired, now that the lock matching PeekContext + // has been removed. + COMBINED_LOCK_ELEMENT LockElement; + PCOMBINED_LOCK_ELEMENT WhereUnlocked = PeekContext, Matching; + PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); + PFILE_LOCK FileLock = LockInfo->BelongsTo; + if (!PeekContext) + return CONTAINING_RECORD + (Irp->Tail.Overlay.ListEntry.Flink, + IRP, + Tail.Overlay.ListEntry); + else + { + PLIST_ENTRY Following; + if (!FileLock->LockInformation) + { + return CONTAINING_RECORD + (Irp->Tail.Overlay.ListEntry.Flink, + IRP, + Tail.Overlay.ListEntry); + } + for (Following = Irp->Tail.Overlay.ListEntry.Flink; + Following != &WhereUnlocked->Exclusive.ListEntry; + Following = Following->Flink) + { + PIRP Irp = CONTAINING_RECORD(Following, IRP, Tail.Overlay.ListEntry); + PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); + LockElement.Exclusive.FileLock.StartingByte = + IoStack->Parameters.LockControl.ByteOffset; + LockElement.Exclusive.FileLock.EndingByte.QuadPart = + LockElement.Exclusive.FileLock.StartingByte.QuadPart + + IoStack->Parameters.LockControl.Length->QuadPart; + Matching = RtlLookupElementGenericTable + (FileLock->LockInformation, &LockElement); + if (!Matching) + { + // This IRP is fine... + return Irp; + } + } + return NULL; + } +} + +static VOID NTAPI +LockAcquireQueueLock(PIO_CSQ Csq, PKIRQL Irql) +{ + PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); + KeAcquireSpinLock(&LockInfo->CsqLock, Irql); +} + +static VOID NTAPI +LockReleaseQueueLock(PIO_CSQ Csq, KIRQL Irql) +{ + PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); + KeReleaseSpinLock(&LockInfo->CsqLock, Irql); +} + +static VOID NTAPI +LockCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp) +{ + NTSTATUS Status; + PLOCK_INFORMATION LockInfo = CONTAINING_RECORD(Csq, LOCK_INFORMATION, Csq); + FsRtlCompleteLockIrpReal + (LockInfo->BelongsTo->CompleteLockIrpRoutine, + NULL, + Irp, + STATUS_CANCELLED, + &Status, + NULL); +}
VOID NTAPI @@ -55,8 +217,11 @@ FsRtlGetNextFileLock(IN PFILE_LOCK FileLock, IN BOOLEAN Restart) { - KeBugCheck(FILE_SYSTEM); - return NULL; + PCOMBINED_LOCK_ELEMENT Entry; + if (!FileLock->LockInformation) return NULL; + Entry = RtlEnumerateGenericTable(FileLock->LockInformation, Restart); + if (!Entry) return NULL; + else return &Entry->Exclusive.FileLock; }
/* @@ -78,31 +243,137 @@ IN BOOLEAN AlreadySynchronized) { NTSTATUS Status; + COMBINED_LOCK_ELEMENT ToInsert; + PCOMBINED_LOCK_ELEMENT Conflict; + PLOCK_INFORMATION LockInfo; + BOOLEAN InsertedNew;
DPRINT1("FsRtlPrivateLock() is stubplemented!\n"); + ASSERT(AlreadySynchronized);
/* Initialize the lock, if necessary */ if (!FileLock->LockInformation) { - DPRINT("LockInformation is uninitialized!\n"); + LockInfo = ExAllocatePool(PagedPool, sizeof(LOCK_INFORMATION)); + FileLock->LockInformation = LockInfo; + if (!FileLock) + return FALSE; + + LockInfo->BelongsTo = FileLock; + + RtlInitializeGenericTable + (&LockInfo->RangeTable, + LockCompare, + LockAllocate, + LockFree, + NULL); + + KeInitializeSpinLock(&LockInfo->CsqLock); + + IoCsqInitializeEx + (&LockInfo->Csq, + LockInsertIrpEx, + LockRemoveIrp, + LockPeekNextIrp, + LockAcquireQueueLock, + LockReleaseQueueLock, + LockCompleteCanceledIrp); }
- /* Assume all is cool, and lock is set */ - IoStatus->Status = STATUS_SUCCESS; - - if (Irp) - { - /* Complete the request */ - FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine, - Context, - Irp, - IoStatus->Status, - &Status, - FileObject); - - /* Update the status */ - IoStatus->Status = Status; - } + LockInfo = FileLock->LockInformation; + ToInsert.Exclusive.FileLock.FileObject = FileObject; + ToInsert.Exclusive.FileLock.StartingByte = *FileOffset; + ToInsert.Exclusive.FileLock.EndingByte.QuadPart = FileOffset->QuadPart + Length->QuadPart; + ToInsert.Exclusive.FileLock.ProcessId = Process->UniqueProcessId; + ToInsert.Exclusive.FileLock.Key = Key; + ToInsert.Exclusive.FileLock.ExclusiveLock = ExclusiveLock; + + Conflict = RtlInsertElementGenericTable + (FileLock->LockInformation, + &ToInsert, + sizeof(ToInsert), + &InsertedNew); + + if (Conflict && !InsertedNew) + { + if (Conflict->Exclusive.FileLock.ExclusiveLock || ExclusiveLock) + { + if (FailImmediately) + { + IoStatus->Status = STATUS_FILE_LOCK_CONFLICT; + if (Irp) + { + FsRtlCompleteLockIrpReal + (FileLock->CompleteLockIrpRoutine, + Context, + Irp, + IoStatus->Status, + &Status, + FileObject); + } + return FALSE; + } + else + { + IoStatus->Status = STATUS_PENDING; + if (Irp) + { + IoMarkIrpPending(Irp); + IoCsqInsertIrpEx + (&LockInfo->Csq, + Irp, + NULL, + NULL); + } + } + return FALSE; + } + else + { + IoStatus->Status = STATUS_SUCCESS; + if (Irp) + { + FsRtlCompleteLockIrpReal + (FileLock->CompleteLockIrpRoutine, + Context, + Irp, + IoStatus->Status, + &Status, + FileObject); + } + return FALSE; + } + } + else if (!Conflict) + { + /* Conflict here is (or would be) the newly inserted element, but we ran + * out of space probably. */ + IoStatus->Status = STATUS_NO_MEMORY; + return FALSE; + } + else + { + /* Assume all is cool, and lock is set */ + IoStatus->Status = STATUS_SUCCESS; + + // Initialize our resource ... We'll use this to mediate access to the + // irp queue. + ExInitializeResourceLite(&Conflict->Exclusive.Resource); + + if (Irp) + { + /* Complete the request */ + FsRtlCompleteLockIrpReal(FileLock->CompleteLockIrpRoutine, + Context, + Irp, + IoStatus->Status, + &Status, + FileObject); + + /* Update the status */ + IoStatus->Status = Status; + } + }
return TRUE; } @@ -115,8 +386,20 @@ FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock, IN PIRP Irp) { - KeBugCheck(FILE_SYSTEM); - return FALSE; + PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); + COMBINED_LOCK_ELEMENT ToFind; + PCOMBINED_LOCK_ELEMENT Found; + if (!FileLock->LockInformation) return TRUE; + ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Read.ByteOffset; + ToFind.Exclusive.FileLock.EndingByte.QuadPart = + ToFind.Exclusive.FileLock.StartingByte.QuadPart + + IoStack->Parameters.Read.Length; + Found = RtlLookupElementGenericTable + (FileLock->LockInformation, + &ToFind); + if (!Found) return TRUE; + return !Found->Exclusive.FileLock.ExclusiveLock || + IoStack->Parameters.Read.Key == Found->Exclusive.FileLock.Key; }
/* @@ -127,8 +410,20 @@ FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock, IN PIRP Irp) { - KeBugCheck(FILE_SYSTEM); - return FALSE; + PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); + COMBINED_LOCK_ELEMENT ToFind; + PCOMBINED_LOCK_ELEMENT Found; + PEPROCESS Process = Irp->Tail.Overlay.Thread->ThreadsProcess; + if (!FileLock->LockInformation) return TRUE; + ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Write.ByteOffset; + ToFind.Exclusive.FileLock.EndingByte.QuadPart = + ToFind.Exclusive.FileLock.StartingByte.QuadPart + + IoStack->Parameters.Write.Length; + Found = RtlLookupElementGenericTable + (FileLock->LockInformation, + &ToFind); + if (!Found) return TRUE; + return Process->UniqueProcessId == Found->Exclusive.FileLock.ProcessId; }
/* @@ -143,8 +438,19 @@ IN PFILE_OBJECT FileObject, IN PVOID Process) { - KeBugCheck(FILE_SYSTEM); - return FALSE; + PEPROCESS EProcess = Process; + COMBINED_LOCK_ELEMENT ToFind; + PCOMBINED_LOCK_ELEMENT Found; + ToFind.Exclusive.FileLock.StartingByte = *FileOffset; + ToFind.Exclusive.FileLock.EndingByte.QuadPart = + FileOffset->QuadPart + Length->QuadPart; + if (!FileLock->LockInformation) return TRUE; + Found = RtlLookupElementGenericTable + (FileLock->LockInformation, + &ToFind); + if (!Found || !Found->Exclusive.FileLock.ExclusiveLock) return TRUE; + return Found->Exclusive.FileLock.Key == Key && + Found->Exclusive.FileLock.ProcessId == EProcess->UniqueProcessId; }
/* @@ -159,8 +465,19 @@ IN PFILE_OBJECT FileObject, IN PVOID Process) { - KeBugCheck(FILE_SYSTEM); - return FALSE; + PEPROCESS EProcess = Process; + COMBINED_LOCK_ELEMENT ToFind; + PCOMBINED_LOCK_ELEMENT Found; + ToFind.Exclusive.FileLock.StartingByte = *FileOffset; + ToFind.Exclusive.FileLock.EndingByte.QuadPart = + FileOffset->QuadPart + Length->QuadPart; + if (!FileLock->LockInformation) return TRUE; + Found = RtlLookupElementGenericTable + (FileLock->LockInformation, + &ToFind); + if (!Found) return TRUE; + return Found->Exclusive.FileLock.Key == Key && + Found->Exclusive.FileLock.ProcessId == EProcess->UniqueProcessId; }
/* @@ -177,9 +494,38 @@ IN PVOID Context OPTIONAL, IN BOOLEAN AlreadySynchronized) { - DPRINT1("FsRtlFastUnlockSingle() is stubplemented!\n"); - - return STATUS_SUCCESS; + COMBINED_LOCK_ELEMENT Find; + PCOMBINED_LOCK_ELEMENT Entry; + PIRP NextMatchingLockIrp; + PLOCK_INFORMATION InternalInfo = FileLock->LockInformation; + // The region to unlock must correspond exactly to a previously locked region + // -- msdn + ASSERT(AlreadySynchronized); + Find.Exclusive.FileLock.StartingByte = *FileOffset; + Find.Exclusive.FileLock.EndingByte.QuadPart = + FileOffset->QuadPart + Length->QuadPart; + Entry = RtlLookupElementGenericTable(&InternalInfo->RangeTable, &Find); + if (!Entry) return STATUS_RANGE_NOT_LOCKED; + if (Entry->Exclusive.FileLock.Key != Key || + Entry->Exclusive.FileLock.ProcessId != Process->UniqueProcessId) + return STATUS_RANGE_NOT_LOCKED; + if (Entry->Exclusive.FileLock.StartingByte.QuadPart != FileOffset->QuadPart || + Entry->Exclusive.FileLock.EndingByte.QuadPart != + FileOffset->QuadPart + Length->QuadPart) + return STATUS_RANGE_NOT_LOCKED; + // this is definitely the thing we want + RtlCopyMemory(&Find, Entry, sizeof(Find)); + RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry); + NextMatchingLockIrp = IoCsqRemoveNextIrp(&InternalInfo->Csq, &Find); + if (NextMatchingLockIrp) + { + // Got a new lock irp... try to do the new lock operation + // Note that we pick an operation that would succeed at the time + // we looked, but can't guarantee that it won't just be re-queued + // because somebody else snatched part of the range in a new thread. + FsRtlProcessFileLock(InternalInfo->BelongsTo, NextMatchingLockIrp, NULL); + } + return STATUS_SUCCESS; }
/* @@ -192,8 +538,32 @@ IN PEPROCESS Process, IN PVOID Context OPTIONAL) { - KeBugCheck(FILE_SYSTEM); - return STATUS_UNSUCCESSFUL; + PCOMBINED_LOCK_ELEMENT Entry; + PRTL_GENERIC_TABLE InternalInfo = FileLock->LockInformation; + + // XXX Synchronize somehow + if (!FileLock->LockInformation) return STATUS_RANGE_NOT_LOCKED; // no locks + for (Entry = RtlEnumerateGenericTable(InternalInfo, TRUE); + Entry; + Entry = RtlEnumerateGenericTable(InternalInfo, FALSE)) + { + LARGE_INTEGER Length; + // We'll take the first one to be the list head, and free the others first... + Length.QuadPart = + Entry->Exclusive.FileLock.EndingByte.QuadPart - + Entry->Exclusive.FileLock.StartingByte.QuadPart; + FsRtlFastUnlockSingle + (FileLock, + Entry->Exclusive.FileLock.FileObject, + &Entry->Exclusive.FileLock.StartingByte, + &Length, + Entry->Exclusive.FileLock.ProcessId, + Entry->Exclusive.FileLock.Key, + Context, + TRUE); + } + + return STATUS_SUCCESS; }
/* @@ -207,8 +577,36 @@ IN ULONG Key, IN PVOID Context OPTIONAL) { - KeBugCheck(FILE_SYSTEM); - return STATUS_UNSUCCESSFUL; + PCOMBINED_LOCK_ELEMENT Entry; + PRTL_GENERIC_TABLE InternalInfo = FileLock->LockInformation; + + // XXX Synchronize somehow + if (!FileLock->LockInformation) return STATUS_RANGE_NOT_LOCKED; // no locks + for (Entry = RtlEnumerateGenericTable(InternalInfo, TRUE); + Entry; + Entry = RtlEnumerateGenericTable(InternalInfo, FALSE)) + { + LARGE_INTEGER Length; + // We'll take the first one to be the list head, and free the others first... + Length.QuadPart = + Entry->Exclusive.FileLock.EndingByte.QuadPart - + Entry->Exclusive.FileLock.StartingByte.QuadPart; + if (Entry->Exclusive.FileLock.Key == Key && + Entry->Exclusive.FileLock.ProcessId == Process->UniqueProcessId) + { + FsRtlFastUnlockSingle + (FileLock, + Entry->Exclusive.FileLock.FileObject, + &Entry->Exclusive.FileLock.StartingByte, + &Length, + Entry->Exclusive.FileLock.ProcessId, + Entry->Exclusive.FileLock.Key, + Context, + TRUE); + } + } + + return STATUS_SUCCESS; }
/* @@ -353,7 +751,12 @@ NTAPI FsRtlUninitializeFileLock(IN PFILE_LOCK FileLock) { - return; + if (FileLock->LockInformation) + { + ASSERT(!RtlNumberGenericTableElements(FileLock->LockInformation)); + ExFreePool(FileLock->LockInformation); + FileLock->LockInformation = NULL; + } }
/* @@ -391,4 +794,3 @@ FsRtlUninitializeFileLock(FileLock); ExFreeToPagedLookasideList(&FsRtlFileLockLookasideList, FileLock); } -