Author: ion
Date: Tue Aug 15 20:12:19 2006
New Revision: 23586
URL:
http://svn.reactos.org/svn/reactos?rev=23586&view=rev
Log:
- Add CM_VIEW_OF_FILE, CM_DELAYED_CLOSE_ENTRY.
- Fix CM_KEY_CONTROL_BLOCK definition.
- Fix minor parameter definition in CmpInitializeHive
- Add cmkcbncb.c with the current work for NCB/KCB management, as well as cache indexes
and delayed allocation/free. Some of these routines will go into cmutil.c soon. File is
incomplete (have not coded all the routines yet, just the general framework) and full of
bugs.
Added:
branches/alex-cm-branch/reactos/ntoskrnl/cm/cmkcbncb.c
Modified:
branches/alex-cm-branch/reactos/ntoskrnl/cm/cm.h
branches/alex-cm-branch/reactos/ntoskrnl/cm/cmhive.c
Modified: branches/alex-cm-branch/reactos/ntoskrnl/cm/cm.h
URL:
http://svn.reactos.org/svn/reactos/branches/alex-cm-branch/reactos/ntoskrnl…
==============================================================================
--- branches/alex-cm-branch/reactos/ntoskrnl/cm/cm.h (original)
+++ branches/alex-cm-branch/reactos/ntoskrnl/cm/cm.h Tue Aug 15 20:12:19 2006
@@ -114,19 +114,37 @@
#define CMP_HASH_PRIME 1000000007
//
+// CmpCreateKcb Flags
+//
+#define CMP_CREATE_FAKE_KCB 0x1
+#define CMP_LOCK_HASHES_FOR_KCB 0x2
+
+//
+// Number of items that can fit inside an Allocation Page
+//
+#define CM_KCBS_PER_PAGE \
+ PAGE_SIZE / sizeof(CM_KEY_CONTROL_BLOCK)
+#define CM_DELAYS_PER_PAGE \
+ PAGE_SIZE / sizeof(CM_DELAYED_CLOSE_ENTRY)
+
+//
// A Cell Index is just a ULONG
//
typedef ULONG HCELL_INDEX;
//
-// TEMPORARY HACK HACK
-//
-typedef struct _HHIVE
-{
- ULONG I;
- ULONG Love;
- ULONG Filip;
-} HHIVE, *PHHIVE;
+// Mapped View of a Hive
+//
+typedef struct _CM_VIEW_OF_FILE
+{
+ LIST_ENTRY LRUViewList;
+ LIST_ENTRY PinViewList;
+ ULONG FileOffset;
+ ULONG Size;
+ PULONG ViewAddress;
+ PVOID Bcb;
+ ULONG UseCount;
+} CM_VIEW_OF_FILE, *PCM_VIEW_OF_FILE;
//
// Key Hash
@@ -270,7 +288,7 @@
typedef struct _CM_KEY_CONTROL_BLOCK
{
USHORT RefCount;
- ULONG Flags;
+ USHORT Flags;
ULONG ExtFlags:8;
ULONG PrivateAlloc:1;
ULONG Delete:1;
@@ -294,8 +312,11 @@
PCM_INDEX_HINT_BLOCK IndexHint;
ULONG HashKey;
ULONG SubKeyCount;
- LIST_ENTRY KeyBodyListHead;
- LIST_ENTRY FreeListEntry;
+ union
+ {
+ LIST_ENTRY KeyBodyListHead;
+ LIST_ENTRY FreeListEntry;
+ };
PCM_KEY_BODY KeyBodyArray[4];
PVOID DelayCloseEntry;
LARGE_INTEGER KcbLastWriteTime;
@@ -337,6 +358,15 @@
ULONG Reserved;
PVOID AllocPage;
} CM_ALLOC_PAGE, *PCM_ALLOC_PAGE;
+
+//
+// Delayed Close Entry
+//
+typedef struct _CM_DELAYED_CLOSE_ENTRY
+{
+ LIST_ENTRY DelayedLRUList;
+ PCM_KEY_CONTROL_BLOCK KeyControlBlock;
+} CM_DELAYED_CLOSE_ENTRY, *PCM_DELAYED_CLOSE_ENTRY;
//
// Use Count Log and Entry
@@ -501,6 +531,15 @@
);
//
+// Registry Lock
+//
+BOOLEAN
+NTAPI
+CmpTestRegistryLockExclusive(
+ VOID
+);
+
+//
// Global variables accessible from all of Cm
//
Modified: branches/alex-cm-branch/reactos/ntoskrnl/cm/cmhive.c
URL:
http://svn.reactos.org/svn/reactos/branches/alex-cm-branch/reactos/ntoskrnl…
==============================================================================
--- branches/alex-cm-branch/reactos/ntoskrnl/cm/cmhive.c (original)
+++ branches/alex-cm-branch/reactos/ntoskrnl/cm/cmhive.c Tue Aug 15 20:12:19 2006
@@ -127,7 +127,7 @@
IN ULONG Operation,
IN ULONG Flags,
IN ULONG FileType,
- IN PVOID Context,
+ IN PVOID HiveData,
IN HANDLE Primary,
IN HANDLE Alternate,
IN HANDLE Log,
@@ -146,7 +146,7 @@
* An external hive that is also internal.
* An alternate hive or a log hive that's not a primary hive too.
* A volatile hive that's linked to permanent storage.
- * An in-memory initailization without context data.
+ * An in-memory initailization without hive data.
* A log hive or alternative hive that's not linked to a correct file type.
*/
if ((Alternate && Log) ||
@@ -155,7 +155,7 @@
(Log && !Primary) ||
((Flags & HIVE_VOLATILE) &&
(Alternate || Primary || External || Log)) ||
- ((Operation == HINIT_MEMORY) && (!Context)) ||
+ ((Operation == HINIT_MEMORY) && (!HiveData)) ||
(Log && (FileType != HFILE_TYPE_LOG)) ||
(Alternate && (FileType != HFILE_TYPE_ALTERNATE)))
{
@@ -274,7 +274,7 @@
Operation,
Flags,
FileType,
- Context,
+ (ULONG_PTR)HiveData,
CmpAllocate,
CmpFree,
CmpFileRead,
Added: branches/alex-cm-branch/reactos/ntoskrnl/cm/cmkcbncb.c
URL:
http://svn.reactos.org/svn/reactos/branches/alex-cm-branch/reactos/ntoskrnl…
==============================================================================
--- branches/alex-cm-branch/reactos/ntoskrnl/cm/cmkcbncb.c (added)
+++ branches/alex-cm-branch/reactos/ntoskrnl/cm/cmkcbncb.c Tue Aug 15 20:12:19 2006
@@ -1,0 +1,844 @@
+/*
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: ntoskrnl/cm/cmkcbncb.c
+ * PURPOSE: Routines for handling KCBs, NCBs, as well as key hashes.
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu(a)reactos.org)
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS *******************************************************************/
+
+ULONG CmpHashTableSize;
+PCM_KEY_HASH_TABLE_ENTRY *CmpCacheTable;
+PCM_NAME_HASH_TABLE_ENTRY *CmpNameCacheTable;
+BOOLEAN CmpAllocInited;
+KGUARDED_MUTEX CmpAllocBucketLock, CmpDelayAllocBucketLock;
+LIST_ENTRY CmpFreeKCBListHead;
+ULONG CmpDelayedCloseSize;
+ULONG CmpDelayedCloseElements;
+KGUARDED_MUTEX CmpDelayedCloseTableLock;
+BOOLEAN CmpDelayCloseWorkItemActive;
+LIST_ENTRY CmpDelayedLRUListHead;
+LIST_ENTRY CmpFreeDelayItemsListHead;
+ULONG CmpDelayCloseIntervalInSeconds = 5;
+KDPC CmpDelayCloseDpc;
+KTIMER CmpDelayCloseTimer;
+BOOLEAN CmpHoldLazyFlush;
+
+/* FUNCTIONS *****************************************************************/
+
+VOID
+NTAPI
+CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)
+{
+ PCM_KEY_HASH *Prev;
+ PCM_KEY_HASH Current;
+
+ /* 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);
+
+ /* Check if it matches */
+ if (Current == KeyHash)
+ {
+ /* Then write the previous one */
+ *Prev = Current->NextHash;
+ 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;
+
+ /* 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 */
+ 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;
+}
+
+// FIXME: NEEDS COMPLETION
+PCM_NAME_CONTROL_BLOCK
+NTAPI
+CmpGetNcb(IN PUNICODE_STRING NodeName)
+{
+ PCM_NAME_CONTROL_BLOCK Ncb;
+ ULONG ConvKey = 0;
+ PWCHAR p, pp;
+ ULONG i;
+ BOOLEAN IsCompressed = TRUE, Found;
+ PCM_NAME_HASH HashEntry;
+ ULONG Length;
+
+ /* 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 valid character */
+ if (*NodeName->Buffer > (UCHAR)-1)
+ {
+ /* This is the actual size, and we know we're not compressed */
+ Length = NodeName->Length;
+ IsCompressed = FALSE;
+ }
+ }
+
+ /* 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)
+ {
+
+ }
+ }
+
+ /* Go to the next hash */
+ HashEntry = HashEntry->NextHash;
+ }
+}
+
+BOOLEAN
+NTAPI
+CmpTryToConvertKcbSharedToExclusive(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ /* Convert the lock */
+ ASSERT(CmpIsKcbLockedExclusive(Kcb) == FALSE);
+ if (ExConvertPushLockSharedToExclusive(GET_HASH_ENTRY(CmpCacheTable,
+ Kcb->ConvKey).Lock))
+ {
+ /* Set the lock owner */
+ GET_HASH_ENTRY(CmpCacheTable, Kcb->ConvKey).Owner = KeGetCurrentThread();
+ return TRUE;
+ }
+
+ /* We failed */
+ return FALSE;
+}
+
+VOID
+NTAPI
+CmpRemoveKcb(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
+CmpFreeKcb(IN PCM_KEY_CONTROL_BLOCK Kcb)
+{
+ ULONG i;
+ PCM_ALLOC_PAGE AllocPage;
+ PAGED_CODE();
+
+ /* Sanity checks */
+ ASSERT(IsListEmpty(&(Kcb->KeyBodyListHead)) == TRUE);
+ for (i = 0; i < 4; i++) ASSERT(Kcb->KeyBodyArray[i] == NULL);
+
+ /* Check if it wasn't privately allocated */
+ if (!Kcb->PrivateAlloc)
+ {
+ /* Free it from the pool */
+ ExFreePool(Kcb);
+ }
+ else
+ {
+ /* Acquire the private allocation lock */
+ KeAcquireGuardedMutex(&CmpAllocBucketLock);
+
+ /* Sanity check on lock ownership */
+ ASSERT((GET_HASH_ENTRY(CmpCacheTable, Kcb->ConvKey).Owner ==
+ KeGetCurrentThread()) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* Remove us from the list */
+ RemoveEntryList(&CmpFreeKCBListHead);
+
+ /* Get the allocation page */
+ AllocPage = (PCM_ALLOC_PAGE)((ULONG_PTR)Kcb & 0xFFFFF000);
+
+ /* Sanity check */
+ ASSERT(AllocPage->FreeCount != CM_KCBS_PER_PAGE);
+
+ /* Increase free count */
+ if (++AllocPage->FreeCount == CM_KCBS_PER_PAGE)
+ {
+ /* Loop all the entries */
+ for (i = CM_KCBS_PER_PAGE; i; i--)
+ {
+ /* Remove the entry
+ RemoveEntryList(& */
+ }
+
+ /* Free the page */
+ ExFreePool(AllocPage);
+ }
+
+ /* Release the lock */
+ KeReleaseGuardedMutex(&CmpAllocBucketLock);
+ }
+}
+
+BOOLEAN
+NTAPI
+CmpReferenceKcb(IN PCM_KEY_CONTROL_BLOCK 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 delete flag */
+ Kcb->Delete = TRUE;
+
+ /* Increase the reference count while we release the lock */
+ InterlockedIncrement((PLONG)&Kcb->RefCount);
+
+ /* Sanity check, KCB should still be shared */
+ ASSERT(CmpIsKcbLockedExclusive(Kcb) == FALSE);
+
+ /* Release the current lock */
+ CmpReleaseKcbLock(Kcb);
+
+ /* Now acquire the lock in exclusive mode */
+ CmpAcquireKcbLockExclusive(Kcb);
+
+ /* Decrement the reference count; the lock is now held again */
+ InterlockedDecrement((PLONG)&Kcb->RefCount);
+
+ /* Sanity check */
+ ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||
+ (Kcb->DelayedCloseIndex == 0));
+
+ /* Remove the delete flag */
+ Kcb->Delete = FALSE;
+ }
+ }
+ }
+
+ /* Increase the reference count */
+ if ((InterlockedIncrement((PLONG)&Kcb->RefCount) + 1) == 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))
+ {
+ /* Sanity check, KCB should still be shared */
+ ASSERT(CmpIsKcbLockedExclusive(Kcb) == FALSE);
+
+ /* Release the current lock */
+ CmpReleaseKcbLock(Kcb);
+
+ /* Now acquire the lock in exclusive mode */
+ CmpAcquireKcbLockExclusive(Kcb);
+ }
+ }
+
+ /* If we're still the last entry, remove us */
+ if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);
+ }
+
+ /* Return success */
+ return TRUE;
+}
+
+// FIXME: THIS FUNCTION IS PARTIALLY FUCKED
+PCM_DELAYED_CLOSE_ENTRY
+NTAPI
+CmpAllocateDelayItem(VOID)
+{
+ PCM_DELAYED_CLOSE_ENTRY Entry;
+ PCM_ALLOC_PAGE AllocPage;
+ ULONG i;
+ PLIST_ENTRY NextEntry;
+ PAGED_CODE();
+
+ /* Lock the allocation buckets */
+ KeAcquireGuardedMutex(&CmpDelayAllocBucketLock);
+ if (TRUE)
+ {
+ /* Allocate an allocation page */
+ AllocPage = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
+ if (AllocPage)
+ {
+ /* Set default entries */
+ AllocPage->FreeCount = CM_DELAYS_PER_PAGE;
+
+ /* Loop each entry */
+ for (i = 0; i < CM_DELAYS_PER_PAGE; i++)
+ {
+ /* Get this entry and link it */
+ Entry = (PCM_DELAYED_CLOSE_ENTRY)(&AllocPage[i]);
+ InsertHeadList(&Entry->DelayedLRUList,
+ &CmpFreeDelayItemsListHead);
+ }
+ }
+
+ /* Get the entry and the alloc page */
+ Entry = CONTAINING_RECORD(NextEntry,
+ CM_DELAYED_CLOSE_ENTRY,
+ DelayedLRUList);
+ AllocPage = (PCM_ALLOC_PAGE)((ULONG_PTR)Entry & 0xFFFFF000);
+
+ /* Decrease free entries */
+ ASSERT(AllocPage->FreeCount != 0);
+ AllocPage->FreeCount--;
+
+ /* Release the lock */
+ KeReleaseGuardedMutex(&CmpDelayAllocBucketLock);
+ return Entry;
+ }
+
+ /* Release the lock */
+ KeReleaseGuardedMutex(&CmpDelayAllocBucketLock);
+ return Entry;
+}
+
+VOID
+NTAPI
+CmpArmDelayedCloseTimer(VOID)
+{
+ LARGE_INTEGER Timeout;
+ PAGED_CODE();
+
+ /* Setup the interval */
+ Timeout.QuadPart = CmpDelayCloseIntervalInSeconds * -10000000;
+ KeSetTimer(&CmpDelayCloseTimer, Timeout, &CmpDelayCloseDpc);
+}
+
+VOID
+NTAPI
+CmpAddToDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN BOOLEAN LockHeldExclusively)
+{
+ ULONG i;
+ ULONG OldRefCount, NewRefCount;
+ PCM_DELAYED_CLOSE_ENTRY Entry;
+ PAGED_CODE();
+
+ /* Sanity check */
+ ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
+ (CmpTestRegistryLockExclusive() == TRUE));
+
+ /* Make sure it's valid */
+ if (Kcb->DelayedCloseIndex != CmpDelayedCloseSize) ASSERT(FALSE);
+
+ /* Sanity checks */
+ ASSERT(Kcb->RefCount == 0);
+ ASSERT(IsListEmpty(&Kcb->KeyBodyListHead) == TRUE);
+ for (i = 0; i < 4; i++) ASSERT(Kcb->KeyBodyArray[i] == NULL);
+
+ /* Allocate a delay item */
+ Entry = CmpAllocateDelayItem();
+ if (!Entry)
+ {
+ /* Cleanup immediately */
+ CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
+ return;
+ }
+
+ /* Sanity check */
+ if (Kcb->InDelayClose) ASSERT(FALSE);
+
+ /* Get the previous reference count */
+ OldRefCount = Kcb->InDelayClose;
+ ASSERT(OldRefCount == 0);
+
+ /* Write the new one */
+ NewRefCount = InterlockedCompareExchange(&Kcb->InDelayClose,
+ 1,
+ OldRefCount);
+ if (NewRefCount != OldRefCount) ASSERT(FALSE);
+
+ /* Remove the delete flag */
+ Kcb->Delete = FALSE;
+
+ /* Set up the close entry */
+ Kcb->DelayCloseEntry = Entry;
+ Entry->KeyControlBlock = Kcb;
+
+ /* Increase the number of elements */
+ InterlockedIncrement(&CmpDelayedCloseElements);
+
+ /* Acquire the delayed close table lock */
+ KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
+
+ /* Insert the entry into the list */
+ InsertHeadList(&CmpDelayedLRUListHead, &Entry->DelayedLRUList);
+
+ /* Check if we need to enable anything */
+ if ((CmpDelayedCloseElements > CmpDelayedCloseSize) &&
+ !(CmpDelayCloseWorkItemActive))
+ {
+ /* Yes, we have too many elements to close, and no work item */
+ CmpArmDelayedCloseTimer();
+ }
+
+ /* Release the table lock */
+ KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
+}
+
+PCM_KEY_CONTROL_BLOCK
+NTAPI
+CmpAllocateKcb(VOID)
+{
+ PLIST_ENTRY ListHead, NextEntry;
+ PCM_KEY_CONTROL_BLOCK CurrentKcb;
+ PCM_ALLOC_PAGE AllocPage;
+ ULONG i;
+ PAGED_CODE();
+
+ /* Check if private allocations are initialized */
+ if (CmpAllocInited)
+ {
+ /* They are, acquire the bucket lock */
+ KeAcquireGuardedMutex(&CmpAllocBucketLock);
+
+ /* Loop the free KCB list */
+ ListHead = &CmpFreeKCBListHead;
+ NextEntry = ListHead->Flink;
+ while (NextEntry != ListHead)
+ {
+ /* Remove the entry */
+ RemoveEntryList(NextEntry);
+
+ /* Get the KCB */
+ CurrentKcb = CONTAINING_RECORD(NextEntry,
+ CM_KEY_CONTROL_BLOCK,
+ FreeListEntry);
+
+ /* Get the allocation page */
+ AllocPage = (PCM_ALLOC_PAGE)((ULONG_PTR)CurrentKcb & 0xFFFFF000);
+
+ /* Decrease the free count */
+ ASSERT(AllocPage->FreeCount != 0);
+ AllocPage->FreeCount--;
+
+ /* Make sure this KCB is privately allocated */
+ ASSERT(CurrentKcb->PrivateAlloc == 1);
+
+ /* Release the allocation lock */
+ KeReleaseGuardedMutex(&CmpAllocBucketLock);
+
+ /* Return the KCB */
+ return CurrentKcb;
+ }
+
+ /* Allocate an allocation page */
+ AllocPage = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
+ if (AllocPage)
+ {
+ /* Set default entries */
+ AllocPage->FreeCount = CM_KCBS_PER_PAGE;
+
+ /* Loop each entry */
+ for (i = 0; i < CM_KCBS_PER_PAGE; i++)
+ {
+ /* Get this entry */
+ CurrentKcb = (PCM_KEY_CONTROL_BLOCK)(AllocPage + 1);
+
+ /* Set it up */
+ CurrentKcb->PrivateAlloc = TRUE;
+ CurrentKcb->DelayCloseEntry = NULL;
+ InsertHeadList(&CurrentKcb->FreeListEntry,
+ &CmpFreeKCBListHead);
+ }
+
+ /* Get the KCB */
+ CurrentKcb = CONTAINING_RECORD(NextEntry,
+ CM_KEY_CONTROL_BLOCK,
+ FreeListEntry);
+
+ /* Get the allocation page */
+ AllocPage = (PCM_ALLOC_PAGE)((ULONG_PTR)CurrentKcb & 0xFFFFF000);
+
+ /* Decrease the free count */
+ ASSERT(AllocPage->FreeCount != 0);
+ AllocPage->FreeCount--;
+
+ /* Make sure this KCB is privately allocated */
+ ASSERT(CurrentKcb->PrivateAlloc == 1);
+
+ /* Release the allocation lock */
+ KeReleaseGuardedMutex(&CmpAllocBucketLock);
+
+ /* Return the KCB */
+ return CurrentKcb;
+ }
+
+ /* Release the lock */
+ KeReleaseGuardedMutex(&CmpAllocBucketLock);
+ }
+
+ /* Allocate a KCB only */
+ CurrentKcb = ExAllocatePoolWithTag(PagedPool,
+ sizeof(CM_KEY_CONTROL_BLOCK),
+ TAG_CM);
+ if (CurrentKcb)
+ {
+ /* Set it up */
+ CurrentKcb->PrivateAlloc = 0;
+ CurrentKcb->DelayCloseEntry = NULL;
+ }
+
+ /* Return it */
+ return CurrentKcb;
+}
+
+VOID
+NTAPI
+CmpDereferenceKcbWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN BOOLEAN LockHeldExclusively)
+{
+ /* 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->PrivateAlloc))
+ {
+ /* Clean up the KCB*/
+ CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
+ }
+ else
+ {
+ /* Otherwise, use delayed close */
+ CmpAddToDelayedClose(Kcb, LockHeldExclusively);
+ }
+}
+
+VOID
+NTAPI
+CmpInitializeKcbKeyBodyList(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
+CmpCreateKcb(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 */
+ if (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()) return NULL;
+
+ /* Check if this is a fake KCB */
+ IsFake = (BOOLEAN)(Flags & CMP_CREATE_FAKE_KCB);
+
+ /* 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) && (*p))
+ {
+ /* Add this key to the hash */
+ ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
+ }
+
+ /* Move on */
+ p++;
+ }
+
+ /* Allocate the KCB */
+ Kcb = CmpAllocateKcb();
+ if (!Kcb) return NULL;
+
+ /* Initailize the key list */
+ CmpInitializeKcbKeyBodyList(Kcb);
+
+ /* Set it up */
+ Kcb->Delete = FALSE;
+ Kcb->RefCount = 1;
+ Kcb->KeyHive = Hive;
+ Kcb->KeyCell = Index;
+ Kcb->ConvKey = ConvKey;
+ Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
+
+ /* Check if we have two hash entires */
+ HashLock = (BOOLEAN)(Flags & CMP_LOCK_HASHES_FOR_KCB);
+ if (HashLock)
+ {
+ /* Not yet implemented */
+ KeBugCheck(0);
+ }
+
+ /* Check if we already have a KCB */
+ FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);
+ if (FoundKcb)
+ {
+ /* Sanity check */
+ ASSERT(!FoundKcb->Delete);
+
+ /* Free the one we allocated and reference this one */
+ CmpFreeKcb(Kcb);
+ Kcb = FoundKcb;
+ if (!CmpReferenceKcb(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[0] +
+ Node->SubKeyCounts[1];
+
+ /* 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) &&
(CmpReferenceKcb(Parent)))
+ {
+ /* Link it */
+ Kcb->ParentKcb = Parent;
+ Kcb->TotalLevels = Parent->TotalLevels + 1;
+ }
+ else
+ {
+ /* Remove the KCB and free it */
+ CmpRemoveKcb(Kcb);
+ CmpFreeKcb(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 = CmpGetNcb(&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[0] +
+ Node->SubKeyCounts[1];
+ Kcb->KcbLastWriteTime = Node->LastWriteTime;
+ Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
+ Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
+ Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;
+ }
+ else
+ {
+ /* Dereference the KCB */
+ CmpDereferenceKcbWithLock(Parent, FALSE);
+
+ /* Remove the KCB and free it */
+ CmpRemoveKcb(Kcb);
+ CmpFreeKcb(Kcb);
+ Kcb = NULL;
+ }
+ }
+ }
+
+ /* Check if we had locked the hashes */
+ if (HashLock)
+ {
+ /* Not yet implemented: Unlock hashes */
+ KeBugCheck(0);
+ }
+
+ /* Return the KCB */
+ return Kcb;
+}
+