[ros-diffs] [arty] 44230: Rough cut at file locks. Not complete yet (probably).

arty at svn.reactos.org arty at svn.reactos.org
Fri Nov 20 02:56:45 CET 2009


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/filelock.c?rev=44230&r1=44229&r2=44230&view=diff
==============================================================================
--- 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);
 }
-




More information about the Ros-diffs mailing list