Added a keep-alive reference to each key object. Lock the registry while accessing sub keys of a key object. Implemented a worker thread which removes all unused key objects. Fixed a bug which shows keys twice if a key is already opened. Modified: trunk/reactos/ntoskrnl/cm/cm.h Modified: trunk/reactos/ntoskrnl/cm/ntfunc.c Modified: trunk/reactos/ntoskrnl/cm/registry.c Modified: trunk/reactos/ntoskrnl/cm/regobj.c _____
Modified: trunk/reactos/ntoskrnl/cm/cm.h --- trunk/reactos/ntoskrnl/cm/cm.h 2005-03-13 17:01:59 UTC (rev 14016) +++ trunk/reactos/ntoskrnl/cm/cm.h 2005-03-13 17:03:42 UTC (rev 14017) @@ -353,6 +353,12 @@
/* List of subkeys loaded */ struct _KEY_OBJECT **SubKeys; + + /* List entry into the global key object list */ + LIST_ENTRY ListEntry; + + /* Time stamp for the last access by the parse routine */ + ULONG TimeStamp; } KEY_OBJECT, *PKEY_OBJECT;
/* Bits 31-22 (top 10 bits) of the cell index is the directory index */ @@ -529,10 +535,11 @@ NTSTATUS CmiRemoveKeyFromList(IN PKEY_OBJECT NewKey);
-PKEY_OBJECT +NTSTATUS CmiScanKeyList(IN PKEY_OBJECT Parent, IN PUNICODE_STRING KeyName, - IN ULONG Attributes); + IN ULONG Attributes, + PKEY_OBJECT* ReturnedObject);
NTSTATUS CmiCreateVolatileHive(PREGISTRY_HIVE *RegistryHive); _____
Modified: trunk/reactos/ntoskrnl/cm/ntfunc.c --- trunk/reactos/ntoskrnl/cm/ntfunc.c 2005-03-13 17:01:59 UTC (rev 14016) +++ trunk/reactos/ntoskrnl/cm/ntfunc.c 2005-03-13 17:03:42 UTC (rev 14017) @@ -21,6 +21,7 @@
extern POBJECT_TYPE CmiKeyType; extern PREGISTRY_HIVE CmiVolatileHive; +extern LIST_ENTRY CmiKeyObjectListHead;
static BOOLEAN CmiRegistryInitialized = FALSE;
@@ -300,6 +301,8 @@ KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
+ InsertTailList(&CmiKeyObjectListHead, &KeyObject->ListEntry); + /* add key to subkeys of parent if needed */ Status = CmiAddSubKey(KeyObject->RegistryHive, KeyObject->ParentKey, @@ -355,7 +358,7 @@ ExReleaseResourceLite(&CmiRegistryLock); KeLeaveCriticalRegion();
- ObDereferenceObject(KeyObject); + ObDereferenceObject(Object);
if (Disposition) @@ -419,6 +422,9 @@
/* Dereference the object */ ObDereferenceObject(KeyObject); + /* Remove the keep-alive reference */ + ObDereferenceObject(KeyObject); + if (KeyObject->RegistryHive != KeyObject->ParentKey->RegistryHive) ObDereferenceObject(KeyObject);
@@ -513,8 +519,7 @@ for (i = 0; i < KeyObject->NumberOfSubKeys; i++) { CurKey = KeyObject->SubKeys[i]; - if (CurKey->RegistryHive == CmiVolatileHive || - CurKey->RegistryHive != RegistryHive) + if (CurKey->RegistryHive != RegistryHive) { if (j == Index) break; _____
Modified: trunk/reactos/ntoskrnl/cm/registry.c --- trunk/reactos/ntoskrnl/cm/registry.c 2005-03-13 17:01:59 UTC (rev 14016) +++ trunk/reactos/ntoskrnl/cm/registry.c 2005-03-13 17:03:42 UTC (rev 14017) @@ -20,12 +20,15 @@
POBJECT_TYPE CmiKeyType = NULL; PREGISTRY_HIVE CmiVolatileHive = NULL; -KSPIN_LOCK CmiKeyListLock;
LIST_ENTRY CmiHiveListHead;
ERESOURCE CmiRegistryLock;
+KTIMER CmiWorkerTimer; +LIST_ENTRY CmiKeyObjectListHead; +ULONG CmiTimer = 0; + volatile BOOLEAN CmiHiveSyncEnabled = FALSE; volatile BOOLEAN CmiHiveSyncPending = FALSE; KDPC CmiHiveSyncDpc; @@ -241,6 +244,67 @@ CmiCheckByName(Verbose, L"User"); }
+VOID STDCALL +CmiWorkerThread(PVOID Param) +{ + NTSTATUS Status; + PLIST_ENTRY CurrentEntry; + PKEY_OBJECT CurrentKey; + ULONG Count; + + + while (1) + { + Status = KeWaitForSingleObject(&CmiWorkerTimer, + Executive, + KernelMode, + FALSE, + NULL); + if (Status == STATUS_SUCCESS) + { + DPRINT("CmiWorkerThread\n"); + + /* Acquire hive lock */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); + + CmiTimer++; + + Count = 0; + CurrentEntry = CmiKeyObjectListHead.Blink; + while (CurrentEntry != &CmiKeyObjectListHead) + { + CurrentKey = CONTAINING_RECORD(CurrentEntry, KEY_OBJECT, ListEntry); + if (CurrentKey->TimeStamp + 120 > CmiTimer) + { + /* The object was accessed in the last 10min */ + break; + } + if (1 == ObGetObjectPointerCount(CurrentKey) && + !(CurrentKey->Flags & KO_MARKED_FOR_DELETE)) + { + ObDereferenceObject(CurrentKey); + CurrentEntry = CmiKeyObjectListHead.Blink; + Count++; + } + else + { + CurrentEntry = CurrentEntry->Blink; + } + } + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + + DPRINT("Removed %d key objects\n", Count); + + } + else + { + KEBUGCHECK(0); + } + } +} + VOID INIT_FUNCTION STDCALL @@ -279,6 +343,9 @@ HANDLE RootKeyHandle; HANDLE KeyHandle; NTSTATUS Status; + LARGE_INTEGER DueTime; + HANDLE ThreadHandle; + CLIENT_ID ThreadId;
/* Initialize the Key object type */ CmiKeyType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); @@ -311,6 +378,29 @@ /* Initialize registry lock */ ExInitializeResourceLite(&CmiRegistryLock);
+ /* Initialize the key object list */ + InitializeListHead(&CmiKeyObjectListHead); + + /* Initialize the worker timer */ + KeInitializeTimerEx(&CmiWorkerTimer, SynchronizationTimer); + + /* Initialize the worker thread */ + Status = PsCreateSystemThread(&ThreadHandle, + THREAD_ALL_ACCESS, + NULL, + NULL, + &ThreadId, + CmiWorkerThread, + NULL); + if (!NT_SUCCESS(Status)) + { + KEBUGCHECK(0); + } + + /* Start the timer */ + DueTime.QuadPart = -1; + KeSetTimerEx(&CmiWorkerTimer, DueTime, 5000, NULL); /* 5sec */ + /* Build volatile registry store */ Status = CmiCreateVolatileHive (&CmiVolatileHive); ASSERT(NT_SUCCESS(Status)); @@ -346,6 +436,7 @@ RootKey->NumberOfSubKeys = 0; RootKey->SubKeys = NULL; RootKey->SizeOfSubKeys = 0; + InsertTailList(&CmiKeyObjectListHead, &RootKey->ListEntry); Status = RtlpCreateUnicodeString(&RootKey->Name, L"Registry", NonPagedPool); ASSERT(NT_SUCCESS(Status));
@@ -672,6 +763,7 @@ NewKey->KeyCell = CmiGetCell (RegistryHive, NewKey->KeyCellOffset, NULL); NewKey->Flags = 0; NewKey->NumberOfSubKeys = 0; + InsertTailList(&CmiKeyObjectListHead, &NewKey->ListEntry); if (NewKey->KeyCell->NumberOfSubKeys != 0) { NewKey->SubKeys = ExAllocatePool(NonPagedPool, @@ -726,6 +818,8 @@ PREGISTRY_HIVE Hive; HANDLE KeyHandle; NTSTATUS Status; + PLIST_ENTRY CurrentEntry; + PKEY_OBJECT CurrentKey;
DPRINT("CmiDisconnectHive() called\n");
@@ -765,11 +859,36 @@ return STATUS_INVALID_PARAMETER; }
+ /* Acquire registry lock exclusively */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); + + CurrentEntry = CmiKeyObjectListHead.Flink; + while (CurrentEntry != &CmiKeyObjectListHead) + { + CurrentKey = CONTAINING_RECORD(CurrentEntry, KEY_OBJECT, ListEntry); + if (1 == ObGetObjectPointerCount(CurrentKey) && + !(CurrentKey->Flags & KO_MARKED_FOR_DELETE)) + { + ObDereferenceObject(CurrentKey); + CurrentEntry = CmiKeyObjectListHead.Flink; + } + else + { + CurrentEntry = CurrentEntry->Flink; + } + } + if (ObGetObjectHandleCount (KeyObject) != 0 || ObGetObjectPointerCount (KeyObject) != 2) { - DPRINT1 ("Hive is still in use\n"); + DPRINT1 ("Hive is still in use (hc %d, rc %d)\n", ObGetObjectHandleCount (KeyObject), ObGetObjectPointerCount (KeyObject)); ObDereferenceObject (KeyObject); + + /* Release registry lock */ + ExReleaseResourceLite (&CmiRegistryLock); + KeLeaveCriticalRegion(); + return STATUS_UNSUCCESSFUL; }
@@ -781,6 +900,10 @@
*RegistryHive = Hive;
+ /* Release registry lock */ + ExReleaseResourceLite (&CmiRegistryLock); + KeLeaveCriticalRegion(); + DPRINT ("CmiDisconnectHive() done\n");
return STATUS_SUCCESS; _____
Modified: trunk/reactos/ntoskrnl/cm/regobj.c --- trunk/reactos/ntoskrnl/cm/regobj.c 2005-03-13 17:01:59 UTC (rev 14016) +++ trunk/reactos/ntoskrnl/cm/regobj.c 2005-03-13 17:03:42 UTC (rev 14017) @@ -14,6 +14,8 @@
#include "cm.h"
+extern LIST_ENTRY CmiKeyObjectListHead; +extern ULONG CmiTimer;
static NTSTATUS CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive, @@ -76,10 +78,22 @@ KeyName.Length); KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] = 0;
+ /* Acquire hive lock */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE);
- FoundObject = CmiScanKeyList(ParsedKey, - &KeyName, - Attributes); + + Status = CmiScanKeyList(ParsedKey, + &KeyName, + Attributes, + &FoundObject); + if (!NT_SUCCESS(Status)) + { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + RtlFreeUnicodeString(&KeyName); + return Status; + } if (FoundObject == NULL) { Status = CmiScanForSubKey(ParsedKey->RegistryHive, @@ -91,6 +105,8 @@ Attributes); if (!NT_SUCCESS(Status) || (SubKeyCell == NULL)) { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); RtlFreeUnicodeString(&KeyName); return(STATUS_UNSUCCESSFUL); } @@ -104,6 +120,9 @@ &LinkPath); if (NT_SUCCESS(Status)) { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + DPRINT("LinkPath '%wZ'\n", &LinkPath);
/* build new FullPath for reparsing */ @@ -140,7 +159,7 @@ }
/* Create new key object and put into linked list */ - DPRINT("CmiObjectParse: %s\n", Path); + DPRINT("CmiObjectParse: %S\n", *Path); Status = ObCreateObject(KernelMode, CmiKeyType, NULL, @@ -152,14 +171,19 @@ (PVOID*)&FoundObject); if (!NT_SUCCESS(Status)) { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); RtlFreeUnicodeString(&KeyName); return(Status); } + /* Add the keep-alive reference */ + ObReferenceObject(FoundObject);
FoundObject->Flags = 0; FoundObject->KeyCell = SubKeyCell; FoundObject->KeyCellOffset = BlockOffset; FoundObject->RegistryHive = ParsedKey->RegistryHive; + InsertTailList(&CmiKeyObjectListHead, &FoundObject->ListEntry); RtlpCreateUnicodeString(&FoundObject->Name, KeyName.Buffer, NonPagedPool); CmiAddKeyToList(ParsedKey, FoundObject); @@ -179,7 +203,12 @@ if (NT_SUCCESS(Status)) { DPRINT("LinkPath '%wZ'\n", &LinkPath); + + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion();
+ ObDereferenceObject(FoundObject); + /* build new FullPath for reparsing */ TargetPath.MaximumLength = LinkPath.MaximumLength; if (EndPtr != NULL) @@ -212,13 +241,15 @@ return(STATUS_REPARSE); } } - - ObReferenceObjectByPointer(FoundObject, - STANDARD_RIGHTS_REQUIRED, - NULL, - UserMode); }
+ RemoveEntryList(&FoundObject->ListEntry); + InsertHeadList(&CmiKeyObjectListHead, &FoundObject->ListEntry); + FoundObject->TimeStamp = CmiTimer; + + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + DPRINT("CmiObjectParse: %s\n", FoundObject->Name);
*Path = EndPtr; @@ -274,11 +305,16 @@
ObReferenceObject (ParentKeyObject);
+ /* Acquire hive lock */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); + if (!NT_SUCCESS(CmiRemoveKeyFromList(KeyObject))) { DPRINT1("Key not found in parent list ???\n"); }
+ RemoveEntryList(&KeyObject->ListEntry); RtlFreeUnicodeString(&KeyObject->Name);
if (KeyObject->Flags & KO_MARKED_FOR_DELETE) @@ -302,6 +338,9 @@
ObDereferenceObject (ParentKeyObject);
+ ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + if (KeyObject->NumberOfSubKeys) { KEBUGCHECK(REGISTRY_ERROR); @@ -532,11 +571,9 @@ CmiAddKeyToList(PKEY_OBJECT ParentKey, PKEY_OBJECT NewKey) { - KIRQL OldIrql;
DPRINT("ParentKey %.08x\n", ParentKey);
- KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys) { @@ -568,7 +605,6 @@ NULL, UserMode); NewKey->ParentKey = ParentKey; - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); }
@@ -576,11 +612,9 @@ CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove) { PKEY_OBJECT ParentKey; - KIRQL OldIrql; DWORD Index;
ParentKey = KeyToRemove->ParentKey; - KeAcquireSpinLock(&CmiKeyListLock, &OldIrql); /* FIXME: If list maintained in alphabetic order, use dichotomic search */ for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++) { @@ -591,7 +625,6 @@ &ParentKey->SubKeys[Index + 1], (ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT)); ParentKey->NumberOfSubKeys--; - KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
DPRINT("Dereference parent key: 0x%x\n", ParentKey); @@ -599,25 +632,23 @@ return STATUS_SUCCESS; } } - KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
return STATUS_UNSUCCESSFUL; }
-PKEY_OBJECT +NTSTATUS CmiScanKeyList(PKEY_OBJECT Parent, PUNICODE_STRING KeyName, - ULONG Attributes) + ULONG Attributes, + PKEY_OBJECT* ReturnedObject) { PKEY_OBJECT CurKey; - KIRQL OldIrql; ULONG Index; - + DPRINT("Scanning key list for: %wZ (Parent: %wZ)\n", KeyName, &Parent->Name);
- KeAcquireSpinLock(&CmiKeyListLock, &OldIrql); /* FIXME: if list maintained in alphabetic order, use dichotomic search */ for (Index=0; Index < Parent->NumberOfSubKeys; Index++) { @@ -627,8 +658,7 @@ if ((KeyName->Length == CurKey->Name.Length) && (_wcsicmp(KeyName->Buffer, CurKey->Name.Buffer) == 0)) { - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); - return CurKey; + break; } } else @@ -636,14 +666,26 @@ if ((KeyName->Length == CurKey->Name.Length) && (wcscmp(KeyName->Buffer, CurKey->Name.Buffer) == 0)) { - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); - return CurKey; + break; } } } - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); - - return NULL; + + if (Index < Parent->NumberOfSubKeys) + { + if (CurKey->Flags & KO_MARKED_FOR_DELETE) + { + *ReturnedObject = NULL; + return STATUS_UNSUCCESSFUL; + } + ObReferenceObject(CurKey); + *ReturnedObject = CurKey; + } + else + { + *ReturnedObject = NULL; + } + return STATUS_SUCCESS; }