-/*\r
- * PROJECT: ReactOS Kernel\r
- * LICENSE: GPL - See COPYING in the top level directory\r
- * FILE: ntoskrnl/config/cmkcbncb.c\r
- * PURPOSE: Routines for handling KCBs, NCBs, as well as key hashes.\r
- * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)\r
- */\r
-\r
-/* INCLUDES ******************************************************************/\r
-\r
-#include <ntoskrnl.h>\r
-#define NDEBUG\r
-#include <debug.h>\r
-#include "cm.h"\r
-\r
-/* GLOBALS *******************************************************************/\r
-\r
-ULONG CmpHashTableSize = 2048;\r
-PCM_KEY_HASH_TABLE_ENTRY CmpCacheTable;\r
-PCM_NAME_HASH_TABLE_ENTRY CmpNameCacheTable;\r
-\r
-BOOLEAN CmpHoldLazyFlush;\r
-\r
-/* FUNCTIONS *****************************************************************/\r
-\r
-VOID\r
-NTAPI\r
-CmpInitializeCache(VOID)\r
-{\r
- ULONG Length, i;\r
- \r
- /* Calculate length for the table */\r
- Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY);\r
- \r
- /* Allocate it */\r
- CmpCacheTable = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);\r
- if (!CmpCacheTable)\r
- {\r
- /* Take the system down */\r
- KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0);\r
- }\r
- \r
- /* Zero out the table */\r
- RtlZeroMemory(CmpCacheTable, Length);\r
- \r
- /* Initialize the locks */\r
- for (i = 0;i < CmpHashTableSize; i++)\r
- {\r
- /* Setup the pushlock */\r
- ExInitializePushLock((PULONG_PTR)&CmpCacheTable[i].Lock);\r
- }\r
- \r
- /* Calculate length for the name cache */\r
- Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY);\r
- \r
- /* Now allocate the name cache table */\r
- CmpNameCacheTable = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);\r
- if (!CmpNameCacheTable)\r
- {\r
- /* Take the system down */\r
- KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0);\r
- }\r
- \r
- /* Zero out the table */\r
- RtlZeroMemory(CmpNameCacheTable, Length);\r
-\r
- /* Initialize the locks */\r
- for (i = 0;i < CmpHashTableSize; i++)\r
- {\r
- /* Setup the pushlock */\r
- ExInitializePushLock((PULONG_PTR)&CmpNameCacheTable[i].Lock);\r
- }\r
- \r
- /* Setup the delayed close table */\r
- CmpInitializeDelayedCloseTable();\r
-}\r
-\r
-VOID\r
-NTAPI\r
-CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)\r
-{\r
- PCM_KEY_HASH *Prev;\r
- PCM_KEY_HASH Current;\r
-\r
- /* Lookup all the keys in this index entry */\r
- Prev = &GET_HASH_ENTRY(CmpCacheTable, KeyHash->ConvKey).Entry;\r
- while (TRUE)\r
- {\r
- /* Save the current one and make sure it's valid */\r
- Current = *Prev;\r
- ASSERT(Current != NULL);\r
-\r
- /* Check if it matches */\r
- if (Current == KeyHash)\r
- {\r
- /* Then write the previous one */\r
- *Prev = Current->NextHash;\r
- break;\r
- }\r
-\r
- /* Otherwise, keep going */\r
- Prev = &Current->NextHash;\r
- }\r
-}\r
-\r
-PCM_KEY_CONTROL_BLOCK\r
-NTAPI\r
-CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,\r
- IN BOOLEAN IsFake)\r
-{\r
- ULONG i;\r
- PCM_KEY_HASH Entry;\r
-\r
- /* Get the hash index */\r
- i = GET_HASH_INDEX(KeyHash->ConvKey);\r
-\r
- /* If this is a fake key, increase the key cell to use the parent data */\r
- if (IsFake) KeyHash->KeyCell++;\r
-\r
- /* Loop the hash table */\r
- Entry = CmpCacheTable[i].Entry;\r
- while (Entry)\r
- {\r
- /* Check if this matches */\r
- if ((KeyHash->ConvKey == Entry->ConvKey) &&\r
- (KeyHash->KeyCell == Entry->KeyCell) &&\r
- (KeyHash->KeyHive == Entry->KeyHive))\r
- {\r
- /* Return it */\r
- return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);\r
- }\r
-\r
- /* Keep looping */\r
- Entry = Entry->NextHash;\r
- }\r
-\r
- /* No entry found, add this one and return NULL since none existed */\r
- KeyHash->NextHash = CmpCacheTable[i].Entry;\r
- CmpCacheTable[i].Entry = KeyHash;\r
- return NULL;\r
-}\r
-\r
-PCM_NAME_CONTROL_BLOCK\r
-NTAPI\r
-CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)\r
-{\r
- PCM_NAME_CONTROL_BLOCK Ncb = NULL;\r
- ULONG ConvKey = 0;\r
- PWCHAR p, pp;\r
- ULONG i;\r
- BOOLEAN IsCompressed = TRUE, Found = FALSE;\r
- PCM_NAME_HASH HashEntry;\r
- ULONG Length, NcbSize;\r
-\r
- /* Loop the name */\r
- p = NodeName->Buffer;\r
- for (i = 0; i < NodeName->Length; i += sizeof(WCHAR))\r
- {\r
- /* Make sure it's not a slash */\r
- if (*p != OBJ_NAME_PATH_SEPARATOR)\r
- {\r
- /* Add it to the hash */\r
- ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);\r
- }\r
-\r
- /* Next character */\r
- p++;\r
- }\r
-\r
- /* Set assumed lengh and loop to check */\r
- Length = NodeName->Length / sizeof(WCHAR);\r
- for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++)\r
- {\r
- /* Check if this is a valid character */\r
- if (*NodeName->Buffer > (UCHAR)-1)\r
- {\r
- /* This is the actual size, and we know we're not compressed */\r
- Length = NodeName->Length;\r
- IsCompressed = FALSE;\r
- }\r
- }\r
- \r
- /* Lock the NCB entry */\r
- CmpAcquireNcbLockExclusiveByKey(ConvKey);\r
-\r
- /* Get the hash entry */\r
- HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;\r
- while (HashEntry)\r
- {\r
- /* Get the current NCB */\r
- Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash);\r
-\r
- /* Check if the hash matches */\r
- if ((ConvKey = HashEntry->ConvKey) && (Length = Ncb->NameLength))\r
- {\r
- /* Assume success */\r
- Found = TRUE;\r
-\r
- /* If the NCB is compressed, do a compressed name compare */\r
- if (Ncb->Compressed)\r
- {\r
- /* Compare names */\r
- if (CmpCompareCompressedName(NodeName, Ncb->Name, Length))\r
- {\r
- /* We failed */\r
- Found = FALSE;\r
- }\r
- }\r
- else\r
- {\r
- /* Do a manual compare */\r
- p = NodeName->Buffer;\r
- pp = Ncb->Name;\r
- for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR))\r
- {\r
- /* Compare the character */\r
- if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp))\r
- {\r
- /* Failed */\r
- Found = FALSE;\r
- break;\r
- }\r
-\r
- /* Next chars */\r
- p++;\r
- pp++;\r
- }\r
- }\r
-\r
- /* Check if we found a name */\r
- if (Found)\r
- {\r
- /* Reference it */\r
- Ncb->RefCount++;\r
- }\r
- }\r
-\r
- /* Go to the next hash */\r
- HashEntry = HashEntry->NextHash;\r
- }\r
- \r
- /* Check if we didn't find it */\r
- if (!Found)\r
- {\r
- /* Allocate one */\r
- NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;\r
- Ncb = ExAllocatePoolWithTag(PagedPool, NcbSize, TAG_CM);\r
- if (!Ncb)\r
- {\r
- /* Release the lock and fail */\r
- CmpReleaseNcbLockByKey(ConvKey);\r
- return NULL;\r
- }\r
- \r
- /* Clear it out */\r
- RtlZeroMemory(Ncb, NcbSize);\r
- \r
- /* Check if the name was compressed */\r
- if (IsCompressed)\r
- {\r
- /* Copy the compressed name */\r
- Ncb->Compressed = TRUE;\r
- for (i = 0; i < Length; i++)\r
- {\r
- /* Copy Unicode to ANSI */\r
- ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);\r
- }\r
- }\r
- else\r
- {\r
- /* Copy the name directly */\r
- Ncb->Compressed = FALSE;\r
- for (i = 0; i < Length; i++)\r
- {\r
- /* Copy each unicode character */\r
- Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]);\r
- }\r
- }\r
- \r
- /* Setup the rest of the NCB */\r
- Ncb->ConvKey = ConvKey;\r
- Ncb->RefCount++;\r
- Ncb->NameLength = Length;\r
- \r
- /* Insert the name in the hash table */\r
- HashEntry = &Ncb->NameHash;\r
- HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;\r
- GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry = HashEntry;\r
- }\r
- \r
- /* Release NCB lock */\r
- CmpReleaseNcbLockByKey(ConvKey);\r
-\r
- /* Return the NCB found */\r
- return Ncb;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)\r
-{\r
- /* Make sure that the registry and KCB are utterly locked */\r
- ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||\r
- (CmpTestRegistryLockExclusive() == TRUE));\r
-\r
- /* Remove the key hash */\r
- CmpRemoveKeyHash(&Kcb->KeyHash);\r
-}\r
-\r
-VOID\r
-NTAPI\r
-CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)\r
-{\r
- PCM_NAME_HASH Current, *Next;\r
-\r
- /* Lock the NCB */\r
- CmpAcquireNcbLockExclusive(Ncb);\r
-\r
- /* Decrease the reference count */\r
- if (!(--Ncb->RefCount))\r
- {\r
- /* Find the NCB in the table */\r
- Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey).Entry;\r
- while (TRUE)\r
- {\r
- /* Check the current entry */\r
- Current = *Next;\r
- ASSERT(Current != NULL);\r
- if (Current == &Ncb->NameHash)\r
- {\r
- /* Unlink it */\r
- *Next = Current->NextHash;\r
- break;\r
- }\r
-\r
- /* Get to the next one */\r
- Next = &Current->NextHash;\r
- }\r
-\r
- /* Found it, now free it */\r
- ExFreePool(Ncb);\r
- }\r
-\r
- /* Release the lock */\r
- CmpReleaseNcbLock(Ncb);\r
-}\r
-\r
-BOOLEAN\r
-NTAPI\r
-CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)\r
-{\r
- /* Check if this is the KCB's first reference */\r
- if (Kcb->RefCount == 0)\r
- {\r
- /* Check if the KCB is locked in shared mode */\r
- if (!CmpIsKcbLockedExclusive(Kcb))\r
- {\r
- /* Convert it to exclusive */\r
- if (!CmpTryToConvertKcbSharedToExclusive(Kcb))\r
- {\r
- /* Set the delayed close index so that we can be ignored */\r
- Kcb->DelayedCloseIndex = 1;\r
-\r
- /* Increase the reference count while we release the lock */\r
- InterlockedIncrement((PLONG)&Kcb->RefCount);\r
- \r
- /* Go from shared to exclusive */\r
- CmpConvertKcbSharedToExclusive(Kcb);\r
-\r
- /* Decrement the reference count; the lock is now held again */\r
- InterlockedDecrement((PLONG)&Kcb->RefCount);\r
- \r
- /* Check if we still control the index */\r
- if (Kcb->DelayedCloseIndex == 1)\r
- {\r
- /* Reset it */\r
- Kcb->DelayedCloseIndex = 0;\r
- }\r
- else\r
- {\r
- /* Sanity check */\r
- ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||\r
- (Kcb->DelayedCloseIndex == 0));\r
- }\r
- }\r
- }\r
- }\r
-\r
- /* Increase the reference count */\r
- if (InterlockedIncrement((PLONG)&Kcb->RefCount) == 0)\r
- {\r
- /* We've overflown to 64K references, bail out */\r
- InterlockedDecrement((PLONG)&Kcb->RefCount);\r
- return FALSE;\r
- }\r
-\r
- /* Check if this was the last close index */\r
- if (!Kcb->DelayedCloseIndex)\r
- {\r
- /* Check if the KCB is locked in shared mode */\r
- if (!CmpIsKcbLockedExclusive(Kcb))\r
- {\r
- /* Convert it to exclusive */\r
- if (!CmpTryToConvertKcbSharedToExclusive(Kcb))\r
- {\r
- /* Go from shared to exclusive */\r
- CmpConvertKcbSharedToExclusive(Kcb);\r
- }\r
- }\r
-\r
- /* If we're still the last entry, remove us */\r
- if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);\r
- }\r
-\r
- /* Return success */\r
- return TRUE;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)\r
-{\r
- PULONG_PTR CachedList;\r
- ULONG i;\r
-\r
- /* Sanity check */\r
- ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||\r
- (CmpTestRegistryLockExclusive() == TRUE));\r
-\r
- /* Check if the value list is cached */\r
- if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))\r
- {\r
- /* Get the cache list */\r
- CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList);\r
- for (i = 0; i < Kcb->ValueCache.Count; i++)\r
- {\r
- /* Check if this cell is cached */\r
- if (CMP_IS_CELL_CACHED(CachedList[i]))\r
- {\r
- /* Free it */\r
- ExFreePool((PVOID)CMP_GET_CACHED_CELL(CachedList[i]));\r
- }\r
- }\r
-\r
- /* Now free the list */\r
- ExFreePool((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList));\r
- Kcb->ValueCache.ValueList = HCELL_NIL;\r
- }\r
- else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)\r
- {\r
- /* This is a sym link, check if there's only one reference left */\r
- if ((((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->RefCount == 1) &&\r
- !(((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->Delete))\r
- {\r
- /* Disable delay close for the KCB */\r
- ((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;\r
- }\r
-\r
- /* Dereference the KCB */\r
- CmpDelayDerefKeyControlBlock((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb);\r
- Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;\r
- }\r
-}\r
-\r
-VOID\r
-NTAPI\r
-CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,\r
- IN BOOLEAN LockHeldExclusively)\r
-{\r
- PCM_KEY_CONTROL_BLOCK Parent;\r
- PAGED_CODE();\r
-\r
- /* Sanity checks */\r
- ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||\r
- (CmpTestRegistryLockExclusive() == TRUE));\r
- ASSERT(Kcb->RefCount == 0);\r
-\r
- /* Cleanup the value cache */\r
- CmpCleanUpKcbValueCache(Kcb);\r
-\r
- /* Reference the NCB */\r
- CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);\r
-\r
- /* Check if we have an index hint block and free it */\r
- if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) ExFreePool(Kcb->IndexHint);\r
-\r
- /* Check if we were already deleted */\r
- Parent = Kcb->ParentKcb;\r
- if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);\r
- \r
- /* Free the KCB as well */\r
- CmpFreeKeyControlBlock(Kcb);\r
-\r
- /* Check if we have a parent */\r
- if (Parent)\r
- {\r
- /* Dereference the parent */\r
- LockHeldExclusively ?\r
- CmpDereferenceKeyControlBlockWithLock(Kcb,LockHeldExclusively) :\r
- CmpDelayDerefKeyControlBlock(Kcb);\r
- }\r
-}\r
-\r
-VOID\r
-NTAPI\r
-CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,\r
- IN BOOLEAN LockHeldExclusively)\r
-{\r
- /* Sanity check */\r
- ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||\r
- (CmpTestRegistryLockExclusive() == TRUE));\r
-\r
- /* Check if this is the last reference */\r
- if (InterlockedDecrement((PLONG)&Kcb->RefCount) == 0)\r
- {\r
- /* Check if we should do a direct delete */\r
- if (((CmpHoldLazyFlush) &&\r
- !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) &&\r
- !(Kcb->Flags & KEY_SYM_LINK)) ||\r
- (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ||\r
- (Kcb->Delete))\r
- {\r
- /* Clean up the KCB*/\r
- CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);\r
- }\r
- else\r
- {\r
- /* Otherwise, use delayed close */\r
- CmpAddToDelayedClose(Kcb, LockHeldExclusively);\r
- }\r
- }\r
-}\r
-\r
-VOID\r
-NTAPI\r
-InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)\r
-{\r
- /* Initialize the list */\r
- InitializeListHead(&Kcb->KeyBodyListHead);\r
-\r
- /* Clear the bodies */\r
- Kcb->KeyBodyArray[0] =\r
- Kcb->KeyBodyArray[1] =\r
- Kcb->KeyBodyArray[2] =\r
- Kcb->KeyBodyArray[3] = NULL;\r
-}\r
-\r
-PCM_KEY_CONTROL_BLOCK\r
-NTAPI\r
-CmpCreateKeyControlBlock(IN PHHIVE Hive,\r
- IN HCELL_INDEX Index,\r
- IN PCM_KEY_NODE Node,\r
- IN PCM_KEY_CONTROL_BLOCK Parent,\r
- IN ULONG Flags,\r
- IN PUNICODE_STRING KeyName)\r
-{\r
- PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL;\r
- UNICODE_STRING NodeName;\r
- ULONG ConvKey = 0, i;\r
- BOOLEAN IsFake, HashLock;\r
- PWCHAR p;\r
-\r
- /* Make sure we own this hive in case it's being unloaded */\r
- if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&\r
- (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))\r
- {\r
- /* Fail */\r
- return NULL;\r
- }\r
-\r
- /* Check if this is a fake KCB */\r
- IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE;\r
-\r
- /* If we have a parent, use its ConvKey */\r
- if (Parent) ConvKey = Parent->ConvKey;\r
-\r
- /* Make a copy of the name */\r
- NodeName = *KeyName;\r
-\r
- /* Remove leading slash */\r
- while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR))\r
- {\r
- /* Move the buffer by one */\r
- NodeName.Buffer++;\r
- NodeName.Length -= sizeof(WCHAR);\r
- }\r
-\r
- /* Make sure we didn't get just a slash or something */\r
- ASSERT(NodeName.Length > 0);\r
-\r
- /* Now setup the hash */\r
- p = NodeName.Buffer;\r
- for (i = 0; i < NodeName.Length; i += sizeof(WCHAR))\r
- {\r
- /* Make sure it's a valid character */\r
- if (*p != OBJ_NAME_PATH_SEPARATOR)\r
- {\r
- /* Add this key to the hash */\r
- ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);\r
- }\r
-\r
- /* Move on */\r
- p++;\r
- }\r
-\r
- /* Allocate the KCB */\r
- Kcb = CmpAllocateKeyControlBlock();\r
- if (!Kcb) return NULL;\r
-\r
- /* Initailize the key list */\r
- InitializeKCBKeyBodyList(Kcb);\r
-\r
- /* Set it up */\r
- Kcb->Delete = FALSE;\r
- Kcb->RefCount = 1;\r
- Kcb->KeyHive = Hive;\r
- Kcb->KeyCell = Index;\r
- Kcb->ConvKey = ConvKey;\r
- Kcb->DelayedCloseIndex = CmpDelayedCloseSize;\r
- Kcb->InDelayClose = 0;\r
-\r
- /* Check if we have two hash entires */\r
- HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE;\r
- if (HashLock)\r
- {\r
- /* Not yet implemented */\r
- KeBugCheck(0);\r
- }\r
-\r
- /* Check if we already have a KCB */\r
- FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);\r
- if (FoundKcb)\r
- {\r
- /* Sanity check */\r
- ASSERT(!FoundKcb->Delete);\r
-\r
- /* Free the one we allocated and reference this one */\r
- CmpFreeKeyControlBlock(Kcb);\r
- Kcb = FoundKcb;\r
- if (!CmpReferenceKeyControlBlock(Kcb))\r
- {\r
- /* We got too many handles */\r
- ASSERT(Kcb->RefCount + 1 != 0);\r
- Kcb = NULL;\r
- }\r
- else\r
- {\r
- /* Check if we're not creating a fake one, but it used to be fake */\r
- if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake))\r
- {\r
- /* Set the hive and cell */\r
- Kcb->KeyHive = Hive;\r
- Kcb->KeyCell = Index;\r
-\r
- /* This means that our current information is invalid */\r
- Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO;\r
- }\r
-\r
- /* Check if we didn't have any valid data */\r
- if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY |\r
- CM_KCB_SUBKEY_ONE |\r
- CM_KCB_SUBKEY_HINT)))\r
- {\r
- /* Calculate the index hint */\r
- Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +\r
- Node->SubKeyCounts[Volatile];\r
-\r
- /* Cached information is now valid */\r
- Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;\r
- }\r
-\r
- /* Setup the other data */\r
- Kcb->KcbLastWriteTime = Node->LastWriteTime;\r
- Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;\r
- Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;\r
- Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen;\r
- }\r
- }\r
- else\r
- {\r
- /* No KCB, do we have a parent? */\r
- if (Parent)\r
- {\r
- /* Reference the parent */\r
- if (((Parent->TotalLevels + 1) < 512) &&\r
- (CmpReferenceKeyControlBlock(Parent)))\r
- {\r
- /* Link it */\r
- Kcb->ParentKcb = Parent;\r
- Kcb->TotalLevels = Parent->TotalLevels + 1;\r
- }\r
- else\r
- {\r
- /* Remove the KCB and free it */\r
- CmpRemoveKeyControlBlock(Kcb);\r
- CmpFreeKeyControlBlock(Kcb);\r
- Kcb = NULL;\r
- }\r
- }\r
- else\r
- {\r
- /* No parent, this is the root node */\r
- Kcb->ParentKcb = NULL;\r
- Kcb->TotalLevels = 1;\r
- }\r
-\r
- /* Check if we have a KCB */\r
- if (Kcb)\r
- {\r
- /* Get the NCB */\r
- Kcb->NameBlock = CmpGetNameControlBlock(&NodeName);\r
- if (Kcb->NameBlock)\r
- {\r
- /* Fill it out */\r
- Kcb->ValueCache.Count = Node->ValueList.Count;\r
- Kcb->ValueCache.ValueList = Node->ValueList.List;\r
- Kcb->Flags = Node->Flags;\r
- Kcb->ExtFlags = 0;\r
- Kcb->DelayedCloseIndex = CmpDelayedCloseSize;\r
-\r
- /* Remember if this is a fake key */\r
- if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST;\r
-\r
- /* Setup the other data */\r
- Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +\r
- Node->SubKeyCounts[Volatile];\r
- Kcb->KcbLastWriteTime = Node->LastWriteTime;\r
- Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;\r
- Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;\r
- Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;\r
- }\r
- else\r
- {\r
- /* Dereference the KCB */\r
- CmpDereferenceKeyControlBlockWithLock(Parent, FALSE);\r
-\r
- /* Remove the KCB and free it */\r
- CmpRemoveKeyControlBlock(Kcb);\r
- CmpFreeKeyControlBlock(Kcb);\r
- Kcb = NULL;\r
- }\r
- }\r
- }\r
- \r
- /* Sanity check */\r
- ASSERT((!Kcb) || (Kcb->Delete == FALSE));\r
-\r
- /* Check if we had locked the hashes */\r
- if (HashLock)\r
- {\r
- /* Not yet implemented: Unlock hashes */\r
- KeBugCheck(0);\r
- }\r
-\r
- /* Return the KCB */\r
- return Kcb;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,\r
- IN ULONG Flags)\r
-{\r
- ASSERT(FALSE);\r
-}\r
-\r
+/*
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: ntoskrnl/config/cmkcbncb.c
+ * PURPOSE: Routines for handling KCBs, NCBs, as well as key hashes.
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS *******************************************************************/
+
+ULONG CmpHashTableSize = 2048;
+PCM_KEY_HASH_TABLE_ENTRY CmpCacheTable;
+PCM_NAME_HASH_TABLE_ENTRY CmpNameCacheTable;
+
+BOOLEAN CmpHoldLazyFlush;
+
+/* FUNCTIONS *****************************************************************/
+
+VOID
+NTAPI
+CmpInitializeCache(VOID)
+{
+ ULONG Length, i;
+
+ /* Calculate length for the table */
+ Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY);
+
+ /* Allocate it */
+ CmpCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
+ if (!CmpCacheTable)
+ {
+ /* Take the system down */
+ KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0);
+ }
+
+ /* Zero out the table */
+ RtlZeroMemory(CmpCacheTable, Length);
+
+ /* Initialize the locks */
+ for (i = 0;i < CmpHashTableSize; i++)
+ {
+ /* Setup the pushlock */
+ ExInitializePushLock((PULONG_PTR)&CmpCacheTable[i].Lock);
+ }
+
+ /* Calculate length for the name cache */
+ Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY);
+
+ /* Now allocate the name cache table */
+ CmpNameCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
+ if (!CmpNameCacheTable)
+ {
+ /* Take the system down */
+ KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0);
+ }
+
+ /* Zero out the table */
+ RtlZeroMemory(CmpNameCacheTable, Length);
+
+ /* Initialize the locks */
+ for (i = 0;i < CmpHashTableSize; i++)
+ {
+ /* Setup the pushlock */
+ ExInitializePushLock((PULONG_PTR)&CmpNameCacheTable[i].Lock);
+ }
+
+ /* Setup the delayed close table */
+ CmpInitializeDelayedCloseTable();
+}
+
+VOID
+NTAPI
+CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)
+{
+ PCM_KEY_HASH *Prev;
+ PCM_KEY_HASH Current;
+ ASSERT_VALID_HASH(KeyHash);
+
+ /* Lookup all the keys in this index entry */
+ Prev = &GET_HASH_ENTRY(CmpCacheTable, KeyHash->ConvKey).Entry;
+ while (TRUE)
+ {
+ /* Save the current one and make sure it's valid */
+ Current = *Prev;
+ ASSERT(Current != NULL);
+ ASSERT_VALID_HASH(Current);
+
+ /* Check if it matches */
+ if (Current == KeyHash)
+ {
+ /* Then write the previous one */
+ *Prev = Current->NextHash;
+ if (*Prev) ASSERT_VALID_HASH(*Prev);
+ break;
+ }
+
+ /* Otherwise, keep going */
+ Prev = &Current->NextHash;
+ }
+}
+
+PCM_KEY_CONTROL_BLOCK
+NTAPI
+CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,
+ IN BOOLEAN IsFake)
+{
+ ULONG i;
+ PCM_KEY_HASH Entry;
+ ASSERT_VALID_HASH(KeyHash);
+
+ /* Get the hash index */
+ i = GET_HASH_INDEX(KeyHash->ConvKey);
+
+ /* If this is a fake key, increase the key cell to use the parent data */
+ if (IsFake) KeyHash->KeyCell++;
+
+ /* Loop the hash table */
+ Entry = CmpCacheTable[i].Entry;
+ while (Entry)
+ {
+ /* Check if this matches */
+ ASSERT_VALID_HASH(Entry);
+ if ((KeyHash->ConvKey == Entry->ConvKey) &&
+ (KeyHash->KeyCell == Entry->KeyCell) &&
+ (KeyHash->KeyHive == Entry->KeyHive))
+ {
+ /* Return it */
+ return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
+ }
+
+ /* Keep looping */
+ Entry = Entry->NextHash;
+ }
+
+ /* No entry found, add this one and return NULL since none existed */
+ KeyHash->NextHash = CmpCacheTable[i].Entry;
+ CmpCacheTable[i].Entry = KeyHash;
+ return NULL;
+}
+
+PCM_NAME_CONTROL_BLOCK
+NTAPI
+CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)
+{
+ PCM_NAME_CONTROL_BLOCK Ncb = NULL;
+ ULONG ConvKey = 0;
+ PWCHAR p, pp;
+ ULONG i;
+ BOOLEAN IsCompressed = TRUE, Found = FALSE;
+ PCM_NAME_HASH HashEntry;
+ ULONG Length, NcbSize;
+
+ /* Loop the name */
+ p = NodeName->Buffer;
+ for (i = 0; i < NodeName->Length; i += sizeof(WCHAR))
+ {
+ /* Make sure it's not a slash */
+ if (*p != OBJ_NAME_PATH_SEPARATOR)
+ {
+ /* Add it to the hash */
+ ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
+ }
+
+ /* Next character */
+ p++;
+ }
+
+ /* Set assumed lengh and loop to check */
+ Length = NodeName->Length / sizeof(WCHAR);
+ for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++)
+ {
+ /* Check if this is a 16-bit character */
+ if (NodeName->Buffer[i] > (UCHAR)-1)
+ {
+ /* This is the actual size, and we know we're not compressed */
+ Length = NodeName->Length;
+ IsCompressed = FALSE;
+ break;
+ }
+ }
+
+ /* Lock the NCB entry */
+ CmpAcquireNcbLockExclusiveByKey(ConvKey);
+
+ /* Get the hash entry */
+ HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
+ while (HashEntry)
+ {
+ /* Get the current NCB */
+ Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash);
+
+ /* Check if the hash matches */
+ if ((ConvKey == HashEntry->ConvKey) && (Length == Ncb->NameLength))
+ {
+ /* Assume success */
+ Found = TRUE;
+
+ /* If the NCB is compressed, do a compressed name compare */
+ if (Ncb->Compressed)
+ {
+ /* Compare names */
+ if (CmpCompareCompressedName(NodeName, Ncb->Name, Length))
+ {
+ /* We failed */
+ Found = FALSE;
+ }
+ }
+ else
+ {
+ /* Do a manual compare */
+ p = NodeName->Buffer;
+ pp = Ncb->Name;
+ for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR))
+ {
+ /* Compare the character */
+ if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp))
+ {
+ /* Failed */
+ Found = FALSE;
+ break;
+ }
+
+ /* Next chars */
+ p++;
+ pp++;
+ }
+ }
+
+ /* Check if we found a name */
+ if (Found)
+ {
+ /* Reference it */
+ Ncb->RefCount++;
+ }
+ }
+
+ /* Go to the next hash */
+ HashEntry = HashEntry->NextHash;
+ }
+
+ /* Check if we didn't find it */
+ if (!Found)
+ {
+ /* Allocate one */
+ NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;
+ Ncb = CmpAllocate(NcbSize, TRUE, TAG_CM);
+ if (!Ncb)
+ {
+ /* Release the lock and fail */
+ CmpReleaseNcbLockByKey(ConvKey);
+ return NULL;
+ }
+
+ /* Clear it out */
+ RtlZeroMemory(Ncb, NcbSize);
+
+ /* Check if the name was compressed */
+ if (IsCompressed)
+ {
+ /* Copy the compressed name */
+ for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
+ {
+ /* Copy Unicode to ANSI */
+ ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
+ }
+ }
+ else
+ {
+ /* Copy the name directly */
+ for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
+ {
+ /* Copy each unicode character */
+ Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
+ }
+ }
+
+ /* Setup the rest of the NCB */
+ Ncb->Compressed = IsCompressed;
+ Ncb->ConvKey = ConvKey;
+ Ncb->RefCount++;
+ Ncb->NameLength = Length;
+
+ /* Insert the name in the hash table */
+ HashEntry = &Ncb->NameHash;
+ HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry;
+ GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry = HashEntry;
+ }
+
+ /* Release NCB lock */
+ CmpReleaseNcbLockByKey(ConvKey);
+
+ /* Return the NCB found */
+ return Ncb;
+}
+
+VOID
+NTAPI
+CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ /* Make sure that the registry and KCB are utterly locked */
+ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* Remove the key hash */
+ CmpRemoveKeyHash(&Kcb->KeyHash);
+}
+
+VOID
+NTAPI
+CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)
+{
+ PCM_NAME_HASH Current, *Next;
+ ULONG ConvKey = Ncb->ConvKey;
+
+ /* Lock the NCB */
+ CmpAcquireNcbLockExclusiveByKey(ConvKey);
+
+ /* Decrease the reference count */
+ if (!(--Ncb->RefCount))
+ {
+ /* Find the NCB in the table */
+ Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey).Entry;
+ while (TRUE)
+ {
+ /* Check the current entry */
+ Current = *Next;
+ ASSERT(Current != NULL);
+ if (Current == &Ncb->NameHash)
+ {
+ /* Unlink it */
+ *Next = Current->NextHash;
+ break;
+ }
+
+ /* Get to the next one */
+ Next = &Current->NextHash;
+ }
+
+ /* Found it, now free it */
+ CmpFree(Ncb, 0);
+ }
+
+ /* Release the lock */
+ CmpReleaseNcbLockByKey(ConvKey);
+}
+
+BOOLEAN
+NTAPI
+CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ CMTRACE(CM_REFERENCE_DEBUG,
+ "%s - Referencing KCB: %p\n", __FUNCTION__, Kcb);
+
+ /* Check if this is the KCB's first reference */
+ if (Kcb->RefCount == 0)
+ {
+ /* Check if the KCB is locked in shared mode */
+ if (!CmpIsKcbLockedExclusive(Kcb))
+ {
+ /* Convert it to exclusive */
+ if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
+ {
+ /* Set the delayed close index so that we can be ignored */
+ Kcb->DelayedCloseIndex = 1;
+
+ /* Increase the reference count while we release the lock */
+ InterlockedIncrement((PLONG)&Kcb->RefCount);
+
+ /* Go from shared to exclusive */
+ CmpConvertKcbSharedToExclusive(Kcb);
+
+ /* Decrement the reference count; the lock is now held again */
+ InterlockedDecrement((PLONG)&Kcb->RefCount);
+
+ /* Check if we still control the index */
+ if (Kcb->DelayedCloseIndex == 1)
+ {
+ /* Reset it */
+ Kcb->DelayedCloseIndex = 0;
+ }
+ else
+ {
+ /* Sanity check */
+ ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||
+ (Kcb->DelayedCloseIndex == 0));
+ }
+ }
+ }
+ }
+
+ /* Increase the reference count */
+ if ((InterlockedIncrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
+ {
+ /* We've overflown to 64K references, bail out */
+ InterlockedDecrement((PLONG)&Kcb->RefCount);
+ return FALSE;
+ }
+
+ /* Check if this was the last close index */
+ if (!Kcb->DelayedCloseIndex)
+ {
+ /* Check if the KCB is locked in shared mode */
+ if (!CmpIsKcbLockedExclusive(Kcb))
+ {
+ /* Convert it to exclusive */
+ if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
+ {
+ /* Go from shared to exclusive */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ }
+ }
+
+ /* If we're still the last entry, remove us */
+ if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);
+ }
+
+ /* Return success */
+ return TRUE;
+}
+
+VOID
+NTAPI
+CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ PULONG_PTR CachedList;
+ ULONG i;
+
+ /* Sanity check */
+ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* Check if the value list is cached */
+ if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))
+ {
+ /* Get the cache list */
+ CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList);
+ for (i = 0; i < Kcb->ValueCache.Count; i++)
+ {
+ /* Check if this cell is cached */
+ if (CMP_IS_CELL_CACHED(CachedList[i]))
+ {
+ /* Free it */
+ CmpFree((PVOID)CMP_GET_CACHED_CELL(CachedList[i]), 0);
+ }
+ }
+
+ /* Now free the list */
+ CmpFree((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList), 0);
+ Kcb->ValueCache.ValueList = HCELL_NIL;
+ }
+ else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
+ {
+ /* This is a sym link, check if there's only one reference left */
+ if ((Kcb->ValueCache.RealKcb->RefCount == 1) &&
+ !(Kcb->ValueCache.RealKcb->Delete))
+ {
+ /* Disable delay close for the KCB */
+ Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
+ }
+
+ /* Dereference the KCB */
+ CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb);
+ Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;
+ }
+}
+
+VOID
+NTAPI
+CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN BOOLEAN LockHeldExclusively)
+{
+ PCM_KEY_CONTROL_BLOCK Parent;
+ PAGED_CODE();
+
+ /* Sanity checks */
+ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+ ASSERT(Kcb->RefCount == 0);
+
+ /* Cleanup the value cache */
+ CmpCleanUpKcbValueCache(Kcb);
+
+ /* Dereference the NCB */
+ CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);
+
+ /* Check if we have an index hint block and free it */
+ if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) CmpFree(Kcb->IndexHint, 0);
+
+ /* Check if we were already deleted */
+ Parent = Kcb->ParentKcb;
+ if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);
+
+ /* Set invalid KCB signature */
+ Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
+
+ /* Free the KCB as well */
+ CmpFreeKeyControlBlock(Kcb);
+
+ /* Check if we have a parent */
+ if (Parent)
+ {
+ /* Dereference the parent */
+ LockHeldExclusively ?
+ CmpDereferenceKeyControlBlockWithLock(Parent,LockHeldExclusively) :
+ CmpDelayDerefKeyControlBlock(Parent);
+ }
+}
+
+VOID
+NTAPI
+CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ PCM_KEY_NODE KeyNode;
+
+ /* Sanity check */
+ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* Check if there's any cached subkey */
+ if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT))
+ {
+ /* Check if there's a hint */
+ if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT))
+ {
+ /* Kill it */
+ CmpFree(Kcb->IndexHint, 0);
+ }
+
+ /* Remove subkey flags */
+ Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT);
+ }
+
+ /* Check if there's no linked cell */
+ if (Kcb->KeyCell == HCELL_NIL)
+ {
+ /* Make sure it's a delete */
+ ASSERT(Kcb->Delete);
+ KeyNode = NULL;
+ }
+ else
+ {
+ /* Get the key node */
+ KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
+ }
+
+ /* Check if we got the node */
+ if (!KeyNode)
+ {
+ /* We didn't, mark the cached data invalid */
+ Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO;
+ }
+ else
+ {
+ /* We have a keynode, update subkey counts */
+ Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
+ Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] +
+ KeyNode->SubKeyCounts[Volatile];
+
+ /* Release the cell */
+ HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
+ }
+}
+
+VOID
+NTAPI
+CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ LONG OldRefCount, NewRefCount;
+ ULONG ConvKey;
+ CMTRACE(CM_REFERENCE_DEBUG,
+ "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
+
+ /* Get the ref count and update it */
+ OldRefCount = *(PLONG)&Kcb->RefCount;
+ NewRefCount = OldRefCount - 1;
+
+ /* Check if we still have references */
+ if( (NewRefCount & 0xFFFF) > 0)
+ {
+ /* Do the dereference */
+ if (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
+ NewRefCount,
+ OldRefCount) == OldRefCount)
+ {
+ /* We'de done */
+ return;
+ }
+ }
+
+ /* Save the key */
+ ConvKey = Kcb->ConvKey;
+
+ /* Do the dereference inside the lock */
+ CmpAcquireKcbLockExclusive(Kcb);
+ CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
+ CmpReleaseKcbLockByKey(ConvKey);
+}
+
+VOID
+NTAPI
+CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN BOOLEAN LockHeldExclusively)
+{
+ CMTRACE(CM_REFERENCE_DEBUG,
+ "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
+
+ /* Sanity check */
+ ASSERT_KCB_VALID(Kcb);
+
+ /* Check if this is the last reference */
+ if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
+ {
+ /* Sanity check */
+ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* Check if we should do a direct delete */
+ if (((CmpHoldLazyFlush) &&
+ !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) &&
+ !(Kcb->Flags & KEY_SYM_LINK)) ||
+ (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ||
+ (Kcb->Delete))
+ {
+ /* Clean up the KCB*/
+ CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
+ }
+ else
+ {
+ /* Otherwise, use delayed close */
+ CmpAddToDelayedClose(Kcb, LockHeldExclusively);
+ }
+ }
+}
+
+VOID
+NTAPI
+InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ /* Initialize the list */
+ InitializeListHead(&Kcb->KeyBodyListHead);
+
+ /* Clear the bodies */
+ Kcb->KeyBodyArray[0] =
+ Kcb->KeyBodyArray[1] =
+ Kcb->KeyBodyArray[2] =
+ Kcb->KeyBodyArray[3] = NULL;
+}
+
+PCM_KEY_CONTROL_BLOCK
+NTAPI
+CmpCreateKeyControlBlock(IN PHHIVE Hive,
+ IN HCELL_INDEX Index,
+ IN PCM_KEY_NODE Node,
+ IN PCM_KEY_CONTROL_BLOCK Parent,
+ IN ULONG Flags,
+ IN PUNICODE_STRING KeyName)
+{
+ PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL;
+ UNICODE_STRING NodeName;
+ ULONG ConvKey = 0, i;
+ BOOLEAN IsFake, HashLock;
+ PWCHAR p;
+
+ /* Make sure we own this hive in case it's being unloaded */
+ if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
+ (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
+ {
+ /* Fail */
+ return NULL;
+ }
+
+ /* Check if this is a fake KCB */
+ IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE;
+
+ /* If we have a parent, use its ConvKey */
+ if (Parent) ConvKey = Parent->ConvKey;
+
+ /* Make a copy of the name */
+ NodeName = *KeyName;
+
+ /* Remove leading slash */
+ while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR))
+ {
+ /* Move the buffer by one */
+ NodeName.Buffer++;
+ NodeName.Length -= sizeof(WCHAR);
+ }
+
+ /* Make sure we didn't get just a slash or something */
+ ASSERT(NodeName.Length > 0);
+
+ /* Now setup the hash */
+ p = NodeName.Buffer;
+ for (i = 0; i < NodeName.Length; i += sizeof(WCHAR))
+ {
+ /* Make sure it's a valid character */
+ if (*p != OBJ_NAME_PATH_SEPARATOR)
+ {
+ /* Add this key to the hash */
+ ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
+ }
+
+ /* Move on */
+ p++;
+ }
+
+ /* Allocate the KCB */
+ Kcb = CmpAllocateKeyControlBlock();
+ if (!Kcb) return NULL;
+
+ /* Initailize the key list */
+ InitializeKCBKeyBodyList(Kcb);
+
+ /* Set it up */
+ Kcb->Signature = CM_KCB_SIGNATURE;
+ Kcb->Delete = FALSE;
+ Kcb->RefCount = 1;
+ Kcb->KeyHive = Hive;
+ Kcb->KeyCell = Index;
+ Kcb->ConvKey = ConvKey;
+ Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
+ Kcb->InDelayClose = 0;
+ ASSERT_KCB_VALID(Kcb);
+
+ /* Check if we have two hash entires */
+ HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE;
+ if (!HashLock)
+ {
+ /* It's not locked, do we have a parent? */
+ if (Parent)
+ {
+ /* Lock the parent KCB and ourselves */
+ CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey);
+ }
+ else
+ {
+ /* Lock only ourselves */
+ CmpAcquireKcbLockExclusive(Kcb);
+ }
+ }
+
+ /* Check if we already have a KCB */
+ FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);
+ if (FoundKcb)
+ {
+ /* Sanity check */
+ ASSERT(!FoundKcb->Delete);
+ Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
+
+ /* Free the one we allocated and reference this one */
+ CmpFreeKeyControlBlock(Kcb);
+ ASSERT_KCB_VALID(FoundKcb);
+ Kcb = FoundKcb;
+ if (!CmpReferenceKeyControlBlock(Kcb))
+ {
+ /* We got too many handles */
+ ASSERT(Kcb->RefCount + 1 != 0);
+ Kcb = NULL;
+ }
+ else
+ {
+ /* Check if we're not creating a fake one, but it used to be fake */
+ if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake))
+ {
+ /* Set the hive and cell */
+ Kcb->KeyHive = Hive;
+ Kcb->KeyCell = Index;
+
+ /* This means that our current information is invalid */
+ Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO;
+ }
+
+ /* Check if we didn't have any valid data */
+ if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY |
+ CM_KCB_SUBKEY_ONE |
+ CM_KCB_SUBKEY_HINT)))
+ {
+ /* Calculate the index hint */
+ Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
+ Node->SubKeyCounts[Volatile];
+
+ /* Cached information is now valid */
+ Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
+ }
+
+ /* Setup the other data */
+ Kcb->KcbLastWriteTime = Node->LastWriteTime;
+ Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
+ Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
+ Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen;
+ }
+ }
+ else
+ {
+ /* No KCB, do we have a parent? */
+ if (Parent)
+ {
+ /* Reference the parent */
+ if (((Parent->TotalLevels + 1) < 512) &&
+ (CmpReferenceKeyControlBlock(Parent)))
+ {
+ /* Link it */
+ Kcb->ParentKcb = Parent;
+ Kcb->TotalLevels = Parent->TotalLevels + 1;
+ }
+ else
+ {
+ /* Remove the KCB and free it */
+ CmpRemoveKeyControlBlock(Kcb);
+ Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
+ CmpFreeKeyControlBlock(Kcb);
+ Kcb = NULL;
+ }
+ }
+ else
+ {
+ /* No parent, this is the root node */
+ Kcb->ParentKcb = NULL;
+ Kcb->TotalLevels = 1;
+ }
+
+ /* Check if we have a KCB */
+ if (Kcb)
+ {
+ /* Get the NCB */
+ Kcb->NameBlock = CmpGetNameControlBlock(&NodeName);
+ if (Kcb->NameBlock)
+ {
+ /* Fill it out */
+ Kcb->ValueCache.Count = Node->ValueList.Count;
+ Kcb->ValueCache.ValueList = Node->ValueList.List;
+ Kcb->Flags = Node->Flags;
+ Kcb->ExtFlags = 0;
+ Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
+
+ /* Remember if this is a fake key */
+ if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST;
+
+ /* Setup the other data */
+ Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
+ Node->SubKeyCounts[Volatile];
+ Kcb->KcbLastWriteTime = Node->LastWriteTime;
+ Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
+ Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
+ Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;
+ }
+ else
+ {
+ /* Dereference the KCB */
+ CmpDereferenceKeyControlBlockWithLock(Parent, FALSE);
+
+ /* Remove the KCB and free it */
+ CmpRemoveKeyControlBlock(Kcb);
+ Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
+ CmpFreeKeyControlBlock(Kcb);
+ Kcb = NULL;
+ }
+ }
+ }
+
+ /* Check if this is a KCB inside a frozen hive */
+ if ((Kcb) && (((PCMHIVE)Hive)->Frozen) && (!(Kcb->Flags & KEY_SYM_LINK)))
+ {
+ /* Don't add these to the delay close */
+ Kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
+ }
+
+ /* Sanity check */
+ ASSERT((!Kcb) || (Kcb->Delete == FALSE));
+
+ /* Check if we had locked the hashes */
+ if (!HashLock)
+ {
+ /* We locked them manually, do we have a parent? */
+ if (Parent)
+ {
+ /* Unlock the parent KCB and ourselves */
+ CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey);
+ }
+ else
+ {
+ /* Unlock only ourselves */
+ CmpReleaseKcbLockByKey(ConvKey);
+ }
+ }
+
+ /* Return the KCB */
+ return Kcb;
+}
+
+PUNICODE_STRING
+NTAPI
+CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ PUNICODE_STRING KeyName;
+ ULONG NameLength, i;
+ PCM_KEY_CONTROL_BLOCK MyKcb;
+ PCM_KEY_NODE KeyNode;
+ BOOLEAN DeletedKey = FALSE;
+ PWCHAR TargetBuffer, CurrentNameW;
+ PUCHAR CurrentName;
+
+ /* Calculate how much size our key name is going to occupy */
+ NameLength = 0;
+ MyKcb = Kcb;
+
+ while (MyKcb)
+ {
+ /* Add length of the name */
+ if (!MyKcb->NameBlock->Compressed)
+ {
+ NameLength += MyKcb->NameBlock->NameLength;
+ }
+ else
+ {
+ NameLength += CmpCompressedNameSize(MyKcb->NameBlock->Name,
+ MyKcb->NameBlock->NameLength);
+ }
+
+ /* Sum up the separator too */
+ NameLength += sizeof(WCHAR);
+
+ /* Go to the parent KCB */
+ MyKcb = MyKcb->ParentKcb;
+ }
+
+ /* Allocate the unicode string now */
+ KeyName = CmpAllocate(NameLength + sizeof(UNICODE_STRING),
+ TRUE,
+ TAG_CM);
+
+ if (!KeyName) return NULL;
+
+ /* Set it up */
+ KeyName->Buffer = (PWSTR)(KeyName + 1);
+ KeyName->Length = NameLength;
+ KeyName->MaximumLength = NameLength;
+
+ /* Loop the keys again, now adding names */
+ NameLength = 0;
+ MyKcb = Kcb;
+
+ while (MyKcb)
+ {
+ /* Sanity checks for deleted and fake keys */
+ if ((!MyKcb->KeyCell && !MyKcb->Delete) ||
+ !MyKcb->KeyHive ||
+ MyKcb->ExtFlags & CM_KCB_KEY_NON_EXIST)
+ {
+ /* Failure */
+ CmpFree(KeyName, 0);
+ return NULL;
+ }
+
+ /* Try to get the name from the keynode,
+ if the key is not deleted */
+ if (!DeletedKey && !MyKcb->Delete)
+ {
+ KeyNode = HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell);
+
+ if (!KeyNode)
+ {
+ /* Failure */
+ CmpFree(KeyName, 0);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* The key was deleted */
+ KeyNode = NULL;
+ DeletedKey = TRUE;
+ }
+
+ /* Get the pointer to the beginning of the current key name */
+ NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR);
+ TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)];
+
+ /* Add a separator */
+ TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR;
+
+ /* Add the name, but remember to go from the end to the beginning */
+ if (!MyKcb->NameBlock->Compressed)
+ {
+ /* Get the pointer to the name (from the keynode, if possible) */
+ if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
+ !KeyNode)
+ {
+ CurrentNameW = MyKcb->NameBlock->Name;
+ }
+ else
+ {
+ CurrentNameW = KeyNode->Name;
+ }
+
+ /* Copy the name */
+ for (i=0; i < MyKcb->NameBlock->NameLength; i++)
+ {
+ TargetBuffer[i+1] = *CurrentNameW;
+ CurrentNameW++;
+ }
+ }
+ else
+ {
+ /* Get the pointer to the name (from the keynode, if possible) */
+ if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
+ !KeyNode)
+ {
+ CurrentName = (PUCHAR)MyKcb->NameBlock->Name;
+ }
+ else
+ {
+ CurrentName = (PUCHAR)KeyNode->Name;
+ }
+
+ /* Copy the name */
+ for (i=0; i < MyKcb->NameBlock->NameLength; i++)
+ {
+ TargetBuffer[i+1] = (WCHAR)*CurrentName;
+ CurrentName++;
+ }
+ }
+
+ /* Release the cell, if needed */
+ if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell);
+
+ /* Go to the parent KCB */
+ MyKcb = MyKcb->ParentKcb;
+ }
+
+ /* Return resulting buffer (both UNICODE_STRING and
+ its buffer following it) */
+ return KeyName;
+}
+
+VOID
+NTAPI
+EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,
+ IN ULONG Flags)
+{
+ ULONG i;
+
+ /* Sanity check */
+ ASSERT(KeyBody->KeyControlBlock != NULL);
+
+ /* Initialize the list entry */
+ InitializeListHead(&KeyBody->KeyBodyList);
+
+ /* Check if we can use the parent KCB array */
+ for (i = 0; i < 4; i++)
+ {
+ /* Add it into the list */
+ if (!InterlockedCompareExchangePointer(&KeyBody->KeyControlBlock->
+ KeyBodyArray[i],
+ KeyBody,
+ NULL))
+ {
+ /* Added */
+ return;
+ }
+ }
+
+ /* Array full, check if we need to unlock the KCB */
+ if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED)
+ {
+ /* It's shared, so release the KCB shared lock */
+ CmpReleaseKcbLock(KeyBody->KeyControlBlock);
+ ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE));
+ }
+
+ /* Check if we need to lock the KCB */
+ if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))
+ {
+ /* Acquire the lock */
+ CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
+ }
+
+ /* Make sure we have the exclusive lock */
+ ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* do the insert */
+ InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead,
+ &KeyBody->KeyBodyList);
+
+ /* Check if we did a manual lock */
+ if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED |
+ CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)))
+ {
+ /* Release the lock */
+ CmpReleaseKcbLock(KeyBody->KeyControlBlock);
+ }
+}
+
+VOID
+NTAPI
+DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,
+ IN BOOLEAN LockHeld)
+{
+ ULONG i;
+
+ /* Sanity check */
+ ASSERT(KeyBody->KeyControlBlock != NULL);
+
+ /* Check if we can use the parent KCB array */
+ for (i = 0; i < 4; i++)
+ {
+ /* Add it into the list */
+ if (InterlockedCompareExchangePointer(&KeyBody->KeyControlBlock->
+ KeyBodyArray[i],
+ NULL,
+ KeyBody) == KeyBody)
+ {
+ /* Removed */
+ return;
+ }
+ }
+
+ /* Sanity checks */
+ ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE);
+ ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE);
+
+ /* Lock the KCB */
+ if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
+ ASSERT((CmpIsKcbLockedExclusive(KeyBody->KeyControlBlock) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* Remove the entry */
+ RemoveEntryList(&KeyBody->KeyBodyList);
+
+ /* Unlock it it if we did a manual lock */
+ if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
+}