BOOLEAN CmpReferenceKeyControlBlock( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) { if (KeyControlBlock->RefCount == 0) { CmpRemoveFromDelayedClose(KeyControlBlock); } if ((USHORT)(KeyControlBlock->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 call // return (FALSE); } else { ++(KeyControlBlock->RefCount); return (TRUE); } }
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 CmpSearchKeyControlBlockTree( PKCB_WORKER_ROUTINE WorkerRoutine, PVOID Context1, PVOID Context2 ) /*++ Routine Description: Traverse the kcb tree. We will visit all nodes unless WorkerRoutine tells us to stop part way through. For each node, call WorkerRoutine(..., Context1, Contex2). If it returns KCB_WORKER_DONE, we are done, simply return. If it returns KCB_WORKER_CONTINUE, just continue the search. If it returns KCB_WORKER_DELETE, the specified KCB is marked as deleted. This routine has the side-effect of removing all delayed-close KCBs. Arguments: WorkerRoutine - applied to nodes witch Match. Context1 - data we pass through Context2 - data we pass through Return Value: NONE. --*/ { PCM_KEY_CONTROL_BLOCK Current; PCM_KEY_CONTROL_BLOCK Next; PCM_KEY_HASH *Prev; ULONG WorkerResult; ULONG i; // // Walk the hash table // for (i=0; i<CmpHashTableSize; i++) { Prev = &CmpCacheTable[i]; while (*Prev) { Current = CONTAINING_RECORD(*Prev, CM_KEY_CONTROL_BLOCK, KeyHash); ASSERT_KCB(Current); ASSERT(!Current->Delete); if (Current->RefCount == 0) { // // This kcb is in DelayClose case, remove it. // CmpRemoveFromDelayedClose(Current); CmpCleanUpKcbCacheWithLock(Current); // // The HashTable is changed, start over in this index again. // Prev = &CmpCacheTable[i]; continue; } WorkerResult = (WorkerRoutine)(Current, Context1, Context2); if (WorkerResult == KCB_WORKER_DONE) { return; } else if (WorkerResult == KCB_WORKER_DELETE) { ASSERT(Current->Delete); *Prev = Current->NextHash; continue; } else { ASSERT(WorkerResult == KCB_WORKER_CONTINUE); Prev = &Current->NextHash; } } } }
ULONG CmpSearchForOpenSubKeys( PCM_KEY_CONTROL_BLOCK KeyControlBlock, SUBKEY_SEARCH_TYPE SearchType ) /*++ Routine Description: This routine searches the KCB tree for any open handles to keys that are subkeys of the given key. It is used by CmRestoreKey to verify that the tree being restored to has no open handles. Arguments: KeyControlBlock - Supplies the key control block for the key for which open subkeys are to be found. SearchType - the type of the search SearchIfExist - exits at the first open subkey found ==> returns 1 if any subkey is open SearchAndDeref - Forces the keys underneath the Key referenced KeyControlBlock to be marked as not referenced (see the REG_FORCE_RESTORE flag in CmRestoreKey) returns 1 if at least one deref was made SearchAndCount - Counts all open subkeys - returns the number of them Return Value: TRUE - open handles to subkeys of the given key exist FALSE - open handles to subkeys of the given key do not exist. --*/ { ULONG i; PCM_KEY_HASH *Current; PCM_KEY_CONTROL_BLOCK kcb; PCM_KEY_CONTROL_BLOCK Realkcb; PCM_KEY_CONTROL_BLOCK Parent; USHORT LevelDiff, l; ULONG Count = 0; // // Registry lock should be held exclusively, so no need to KCB lock // ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); // // First, clean up all subkeys in the cache // for (i=0; i<CmpHashTableSize; i++) { Current = &CmpCacheTable[i]; while (*Current) { kcb = CONTAINING_RECORD(*Current, CM_KEY_CONTROL_BLOCK, KeyHash); if (kcb->RefCount == 0) { // // This kcb is in DelayClose case, remove it. // CmpRemoveFromDelayedClose(kcb); CmpCleanUpKcbCacheWithLock(kcb); // // The HashTable is changed, start over in this index again. // Current = &CmpCacheTable[i]; continue; } Current = &kcb->NextHash; } } if (KeyControlBlock->RefCount == 1) { // // There is only one open handle, so there must be no open subkeys. // Count = 0; } else { // // Now search for an open subkey handle. // Count = 0; #ifdef KCB_TO_KEYBODY_LINK // // dump the root first if we were asked to do so. // if(SearchType == SearchAndCount) { CmpDumpKeyBodyList(KeyControlBlock,&Count); } #endif for (i=0; i<CmpHashTableSize; i++) { StartDeref: Current = &CmpCacheTable[i]; while (*Current) { kcb = CONTAINING_RECORD(*Current, CM_KEY_CONTROL_BLOCK, KeyHash); if (kcb->TotalLevels > KeyControlBlock->TotalLevels) { LevelDiff = kcb->TotalLevels - KeyControlBlock->TotalLevels; Parent = kcb; for (l=0; l<LevelDiff; l++) { Parent = Parent->ParentKcb; } if (Parent == KeyControlBlock) { // // Found a match; // if( SearchType == SearchIfExist ) { Count = 1; break; } else if(SearchType == SearchAndDeref) { // // Mark the key as deleted, remove it from cache, but don't add it // to the Delay Close table (we want the key to be visible only to // the one(s) that have open handles on it. // ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); // // flush any pending notifies as the kcb won't be around any longer // #ifdef KCB_TO_KEYBODY_LINK CmpFlushNotifiesOnKeyBodyList(kcb); #endif CmpCleanUpSubKeyInfo(kcb->ParentKcb); kcb->Delete = TRUE; CmpRemoveKeyControlBlock(kcb); kcb->KeyCell = HCELL_NIL; kcb->KeyNode = NULL; Count++; // // Restart the search // goto StartDeref; } else if(SearchType == SearchAndCount) { // // here do the dumping and count incrementing stuff // #ifdef KCB_TO_KEYBODY_LINK CmpDumpKeyBodyList(kcb,&Count); #else Count++; #endif } } } Current = &kcb->NextHash; } } } return Count; }
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; }