Use memory wrappers instead of ExAllocatePool/ExFreePool directly
[reactos.git] / reactos / ntoskrnl / config / cmkcbncb.c
index e768fc9..1ec66fc 100644 (file)
-/*\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);
+}