PCM_KEY_CONTROL_BLOCK CmpCreateKeyControlBlock( PHHIVE Hive, HCELL_INDEX Cell, PCM_KEY_NODE Node, PCM_KEY_CONTROL_BLOCK ParentKcb, BOOLEAN FakeKey, PUNICODE_STRING KeyName ) /*++ Routine Description: Allocate and initialize a key control block, insert it into the kcb tree. Full path will be BaseName + '\' + KeyName, unless BaseName NULL, in which case the full path is simply KeyName. RefCount of returned KCB WILL have been incremented to reflect callers ref. Arguments: Hive - Supplies Hive that holds the key we are creating a KCB for. Cell - Supplies Cell that contains the key we are creating a KCB for. Node - Supplies pointer to key node. ParentKcb - Parent kcb of the kcb to be created FakeKey - Whether the kcb to be create is a fake one or not KeyName - the subkey name to of the KCB to be created. NOTE: We need the parameter instead of just using the name in the KEY_NODE because there is no name in the root cell of a hive. Return Value: NULL - failure (insufficient memory) else a pointer to the new kcb. --*/ { PCM_KEY_CONTROL_BLOCK kcb; PCM_KEY_CONTROL_BLOCK kcbmatch=NULL; PCMHIVE CmHive; ULONG namelength; PUNICODE_STRING fullname; ULONG Size; ULONG i; UNICODE_STRING NodeName; ULONG ConvKey=0; ULONG Cnt; WCHAR *Cp; // // ParentKCb has the base hash value. // if (ParentKcb) { ConvKey = ParentKcb->ConvKey; } NodeName = *KeyName; while ((NodeName.Length > 0) && (NodeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { // // This must be the \REGISTRY. // Strip off the leading OBJ_NAME_PATH_SEPARATOR // NodeName.Buffer++; NodeName.Length -= sizeof(WCHAR); } // // Manually compute the hash to use. // ASSERT(NodeName.Length > 0); if (NodeName.Length) { Cp = NodeName.Buffer; for (Cnt=0; Cnt<NodeName.Length; Cnt += sizeof(WCHAR)) { if ((*Cp != OBJ_NAME_PATH_SEPARATOR) && (*Cp != UNICODE_NULL)) { ConvKey = 37 * ConvKey + (ULONG)RtlUpcaseUnicodeChar(*Cp); } ++Cp; } } // // Create a new kcb, which we will free if one already exists // for this key. // Now it is a fixed size structure. // kcb = ExAllocatePoolWithTag(PagedPool, sizeof(CM_KEY_CONTROL_BLOCK), CM_KCB_TAG | PROTECTED_POOL); if (kcb == NULL) { return(NULL); } else { SET_KCB_SIGNATURE(kcb, KCB_SIGNATURE); INIT_KCB_KEYBODY_LIST(kcb); kcb->Delete = FALSE; kcb->RefCount = 1; kcb->KeyHive = Hive; kcb->KeyCell = Cell; kcb->KeyNode = Node; kcb->ConvKey = ConvKey; } ASSERT_KCB(kcb); // // Find location to insert kcb in kcb tree. // LOCK_KCB_TREE(); // // Add the KCB to the hash table // kcbmatch = CmpInsertKeyHash(&kcb->KeyHash, FakeKey); if (kcbmatch != NULL) { // // A match was found. // ASSERT(!kcbmatch->Delete); SET_KCB_SIGNATURE(kcb, '1FmC'); ASSERT_KEYBODY_LIST_EMPTY(kcb); ExFreePoolWithTag(kcb, CM_KCB_TAG | PROTECTED_POOL); ASSERT_KCB(kcbmatch); kcb = kcbmatch; if (kcb->RefCount == 0) { // // This kcb is on the delayed close list. Remove it from that // list. // CmpRemoveFromDelayedClose(kcb); } if ((USHORT)(kcb->RefCount + 1) == 0) { // // We have maxed out the ref count on this key. Probably // some bogus app has opened the same key 64K times without // ever closing it. Just fail the open, they've got enough // handles already. // ASSERT(kcb->RefCount + 1 != 0); kcb = NULL; } else { ++kcb->RefCount; } } else { // // No kcb created previously, fill in all the data. // // // Now try to reference the parentkcb // if (ParentKcb) { if (CmpReferenceKeyControlBlock(ParentKcb)) { kcb->ParentKcb = ParentKcb; kcb->TotalLevels = ParentKcb->TotalLevels + 1; } else { // // We have maxed out the ref count on the parent. // Since it has been cached in the cachetable, // remove it first before we free the allocation. // CmpRemoveKeyControlBlock(kcb); SET_KCB_SIGNATURE(kcb, '2FmC'); ASSERT_KEYBODY_LIST_EMPTY(kcb); ExFreePoolWithTag(kcb, CM_KCB_TAG | PROTECTED_POOL); kcb = NULL; } } else { // // It is the \REGISTRY node. // kcb->ParentKcb = NULL; kcb->TotalLevels = 1; } if (kcb) { // // Now try to find the Name Control block that has the name for this node. // kcb->NameBlock = CmpGetNameControlBlock (&NodeName); if (kcb->NameBlock) { // // Now fill in all the data needed for the cache. // kcb->ValueCache.Count = Node->ValueList.Count; kcb->ValueCache.ValueList = (ULONG_PTR) Node->ValueList.List; kcb->Flags = Node->Flags; kcb->ExtFlags = 0; kcb->DelayedCloseIndex = 0; // // Cache the security cells in the kcb // kcb->Security = Node->Security; if (FakeKey) { // // The KCb to be created is a fake one // kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST; } } else { // // We have maxed out the ref count on the Name. // // // First dereference the parent KCB. // CmpDereferenceKeyControlBlockWithLock(ParentKcb); CmpRemoveKeyControlBlock(kcb); SET_KCB_SIGNATURE(kcb, '3FmC'); ASSERT_KEYBODY_LIST_EMPTY(kcb); ExFreePoolWithTag(kcb, CM_KCB_TAG | PROTECTED_POOL); kcb = NULL; } } } UNLOCK_KCB_TREE(); return kcb; }
VOID CmpAddToDelayedClose( IN PCM_KEY_CONTROL_BLOCK kcb, IN BOOLEAN RegLockHeldEx ) /*++ Routine Description: Adds a kcb to the delayed close table Arguments: kcb - the kcb in question Note: kcb lock/resource should be acquired exclusively when this function is called Return Value: NONE. --*/ { PCM_DELAYED_CLOSE_ENTRY DelayedEntry = NULL; CM_PAGED_CODE(); ASSERT( (CmpIsKCBLockedExclusive(kcb) == TRUE) || // this kcb is owned exclusive (CmpTestRegistryLockExclusive() == TRUE) ); // or the entire registry is locked exclusive // Already on delayed close, don't try to put on again if (kcb->DelayedCloseIndex != CmpDelayedCloseSize) { // see if we really need this ASSERT( FALSE ); return; } ASSERT(kcb->RefCount == 0); // // now materialize a new entry for this kcb // ASSERT_KEYBODY_LIST_EMPTY(kcb); DelayedEntry = CmpDelayCloseAllocNewEntry(); if( !DelayedEntry ) { // // this is bad luck; we need to free the kcb in place // CmpCleanUpKcbCacheWithLock(kcb,RegLockHeldEx); return; } #if DBG if( kcb->InDelayClose != 0 ) { ASSERT( FALSE ); } { LONG OldRefCount; LONG NewRefCount; OldRefCount = *(PLONG)&kcb->InDelayClose; //get entire dword ASSERT( OldRefCount == 0 ); NewRefCount = 1; if( InterlockedCompareExchange((PLONG)&kcb->InDelayClose,NewRefCount,OldRefCount) != OldRefCount ) { ASSERT( FALSE ); } } #endif //DBG // // populate the entry and insert it into the LRU list (at the top). // kcb->DelayedCloseIndex = 0; // see if we really need this kcb->DelayCloseEntry = (PVOID)DelayedEntry; // need this for removing it back from here DelayedEntry->KeyControlBlock = kcb; InterlockedIncrement((PLONG)&CmpDelayedCloseElements); LOCK_DELAY_CLOSE(); InsertHeadList( &CmpDelayedLRUListHead, &(DelayedEntry->DelayedLRUList) ); // // check if limit hit and arm timer if not already armed // if( (CmpDelayedCloseElements > CmpDelayedCloseSize) && (!CmpDelayCloseWorkItemActive) ) { CmpArmDelayedCloseTimer(); } UNLOCK_DELAY_CLOSE(); // // we're done here // }
VOID CmpCleanUpKcbCacheWithLock( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: Clean up all cached allocations that are associated to this key. If the parent is still open just because of this one, Remove the parent as well. Arguments: KeyControlBlock - pointer to a key control block. Return Value: NONE. --*/ { PCM_KEY_CONTROL_BLOCK Kcb; PCM_KEY_CONTROL_BLOCK ParentKcb; Kcb = KeyControlBlock; ASSERT(KeyControlBlock->RefCount == 0); while (Kcb->RefCount == 0) { // // First, free allocations for Value/data. // CmpCleanUpKcbValueCache(Kcb); // // Free the kcb and dereference parentkcb and nameblock. // CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock); if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) { // // Now free the HintIndex allocation // ExFreePoolWithTag(Kcb->IndexHint, CM_CACHE_INDEX_TAG | PROTECTED_POOL); } // // Save the ParentKcb before we free the Kcb // ParentKcb = Kcb->ParentKcb; // // We cannot call CmpDereferenceKeyControlBlockWithLock so we can avoid recurrsion. // if (!Kcb->Delete) { CmpRemoveKeyControlBlock(Kcb); } SET_KCB_SIGNATURE(Kcb, '4FmC'); ASSERT_KEYBODY_LIST_EMPTY(Kcb); ExFreePoolWithTag(Kcb, CM_KCB_TAG | PROTECTED_POOL); Kcb = ParentKcb; Kcb->RefCount--; } }
VOID CmpFreeKeyControlBlock( PCM_KEY_CONTROL_BLOCK kcb ) /*++ Routine Description: Frees a kcb; if it's allocated from our own pool put it back in the free list. If it's allocated from general pool, just free it. Arguments: kcb to free Return Value: --*/ { USHORT j; PCM_ALLOC_PAGE AllocPage; PAGED_CODE(); ASSERT_KEYBODY_LIST_EMPTY(kcb); #if defined(_WIN64) // // free cached name if any // if( (kcb->RealKeyName != NULL) && (kcb->RealKeyName != CMP_KCB_REAL_NAME_UPCASE) ) { ExFreePoolWithTag(kcb->RealKeyName, CM_NAME_TAG); } #endif if( !kcb->PrivateAlloc ) { // // just free it and be done with it // ExFreePoolWithTag(kcb, CM_KCB_TAG | PROTECTED_POOL); return; } LOCK_ALLOC_BUCKET(); ASSERT_HASH_ENTRY_LOCKED_EXCLUSIVE(kcb->ConvKey); LogKCBFree(kcb); // // add kcb to freelist // InsertTailList( &CmpFreeKCBListHead, &(kcb->FreeListEntry) ); // // get the page // AllocPage = (PCM_ALLOC_PAGE)KCB_TO_ALLOC_PAGE( kcb ); // // not all are free // ASSERT( AllocPage->FreeCount != CM_KCBS_PER_PAGE); AllocPage->FreeCount++; if( AllocPage->FreeCount == CM_KCBS_PER_PAGE ) { // // entire page is free; let it go // // // first; iterate through the free kcb list and remove all kcbs inside this page // for(j=0;j<CM_KCBS_PER_PAGE;j++) { kcb = (PCM_KEY_CONTROL_BLOCK)((PUCHAR)AllocPage + FIELD_OFFSET(CM_ALLOC_PAGE,AllocPage) + j*CM_KCB_ENTRY_SIZE); RemoveEntryList(&(kcb->FreeListEntry)); } ExFreePoolWithTag(AllocPage, CM_ALLOCATE_TAG|PROTECTED_POOL); } UNLOCK_ALLOC_BUCKET(); }