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@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; +} +