/*++ Copyright (c) 1991 Microsoft Corporation Module Name: cmsubs.c Abstract: This module various support routines for the configuration manager. The routines in this module are not independent enough to be linked into any other program. The routines in cmsubs2.c are. Author: Bryan M. Willman (bryanwi) 12-Sep-1991 Revision History: --*/ #include "cmp.h" FAST_MUTEX CmpPostLock; #ifdef ALLOC_DATA_PRAGMA #pragma data_seg("PAGEDATA") #endif extern ULONG CmpDelayedCloseSize; extern BOOLEAN CmpHoldLazyFlush; PCM_KEY_HASH *CmpCacheTable = NULL; ULONG CmpHashTableSize = 2048; PCM_NAME_HASH *CmpNameCacheTable = NULL; #ifdef CMP_STATS extern struct { ULONG CmpMaxKcbNo; ULONG CmpKcbNo; ULONG CmpStatNo; ULONG CmpNtCreateKeyNo; ULONG CmpNtDeleteKeyNo; ULONG CmpNtDeleteValueKeyNo; ULONG CmpNtEnumerateKeyNo; ULONG CmpNtEnumerateValueKeyNo; ULONG CmpNtFlushKeyNo; ULONG CmpNtNotifyChangeMultipleKeysNo; ULONG CmpNtOpenKeyNo; ULONG CmpNtQueryKeyNo; ULONG CmpNtQueryValueKeyNo; ULONG CmpNtQueryMultipleValueKeyNo; ULONG CmpNtRestoreKeyNo; ULONG CmpNtSaveKeyNo; ULONG CmpNtSaveMergedKeysNo; ULONG CmpNtSetValueKeyNo; ULONG CmpNtLoadKeyNo; ULONG CmpNtUnloadKeyNo; ULONG CmpNtSetInformationKeyNo; ULONG CmpNtReplaceKeyNo; ULONG CmpNtQueryOpenSubKeysNo; } CmpStatsDebug; #endif VOID CmpRemoveKeyHash( IN PCM_KEY_HASH KeyHash ); PCM_KEY_CONTROL_BLOCK CmpInsertKeyHash( IN PCM_KEY_HASH KeyHash, IN BOOLEAN FakeKey ); // // private prototype for recursive worker // VOID CmpDereferenceNameControlBlockWithLock( PCM_NAME_CONTROL_BLOCK Ncb ); VOID CmpDumpKeyBodyList( IN PCM_KEY_CONTROL_BLOCK kcb, IN PULONG Count, IN PVOID Context ); #ifdef NT_RENAME_KEY ULONG CmpComputeKcbConvKey( PCM_KEY_CONTROL_BLOCK KeyControlBlock ); BOOLEAN CmpRehashKcbSubtree( PCM_KEY_CONTROL_BLOCK Start, PCM_KEY_CONTROL_BLOCK End ); #endif //NT_RENAME_KEY VOID CmpRebuildKcbCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,CmpCleanUpKCBCacheTable) #pragma alloc_text(PAGE,CmpSearchForOpenSubKeys) #pragma alloc_text(PAGE,CmpReferenceKeyControlBlock) #pragma alloc_text(PAGE,CmpGetNameControlBlock) #pragma alloc_text(PAGE,CmpDereferenceNameControlBlockWithLock) #pragma alloc_text(PAGE,CmpCleanUpSubKeyInfo) #pragma alloc_text(PAGE,CmpCleanUpKcbValueCache) #pragma alloc_text(PAGE,CmpCleanUpKcbCacheWithLock) #pragma alloc_text(PAGE,CmpConstructName) #pragma alloc_text(PAGE,CmpCreateKeyControlBlock) #pragma alloc_text(PAGE,CmpSearchKeyControlBlockTree) #pragma alloc_text(PAGE,CmpDereferenceKeyControlBlock) #pragma alloc_text(PAGE,CmpDereferenceKeyControlBlockWithLock) #pragma alloc_text(PAGE,CmpRemoveKeyControlBlock) #pragma alloc_text(PAGE,CmpFreeKeyBody) #pragma alloc_text(PAGE,CmpInsertKeyHash) #pragma alloc_text(PAGE,CmpRemoveKeyHash) #pragma alloc_text(PAGE,CmpInitializeCache) #pragma alloc_text(PAGE,CmpDumpKeyBodyList) #pragma alloc_text(PAGE,CmpFlushNotifiesOnKeyBodyList) #pragma alloc_text(PAGE,CmpRebuildKcbCache) #ifdef NT_RENAME_KEY #pragma alloc_text(PAGE,CmpComputeKcbConvKey) #pragma alloc_text(PAGE,CmpRehashKcbSubtree) #endif //NT_RENAME_KEY #ifdef CM_CHECK_FOR_ORPHANED_KCBS #pragma alloc_text(PAGE,CmpCheckForOrphanedKcbs) #endif //CM_CHECK_FOR_ORPHANED_KCBS #endif VOID CmpDumpKeyBodyList( IN PCM_KEY_CONTROL_BLOCK kcb, IN PULONG Count, IN PVOID Context ) { PCM_KEY_BODY KeyBody; PUNICODE_STRING Name; if( IsListEmpty(&(kcb->KeyBodyListHead)) == TRUE ) { // // Nobody has this subkey open, but for sure some subkey must be // open. nicely return. // return; } Name = CmpConstructName(kcb); if( !Name ){ // oops, we're low on resources if( Context != NULL ) { ((PQUERY_OPEN_SUBKEYS_CONTEXT)Context)->StatusCode = STATUS_INSUFFICIENT_RESOURCES; } return; } // // now iterate through the list of KEY_BODYs referencing this kcb // KeyBody = (PCM_KEY_BODY)kcb->KeyBodyListHead.Flink; while( KeyBody != (PCM_KEY_BODY)(&(kcb->KeyBodyListHead)) ) { KeyBody = CONTAINING_RECORD(KeyBody, CM_KEY_BODY, KeyBodyList); // // sanity check: this should be a KEY_BODY // ASSERT_KEY_OBJECT(KeyBody); if( !Context ) { // // NtQueryOpenSubKeys : dump it's name and owning process // #ifndef _CM_LDR_ { PEPROCESS Process; PUCHAR ImageName = NULL; if( NT_SUCCESS(PsLookupProcessByProcessId(KeyBody->ProcessID,&Process))) { ImageName = PsGetProcessImageFileName(Process); } else { Process = NULL; } if( !ImageName ) { ImageName = (PUCHAR)"Unknown"; } DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Process %p (PID = %lx ImageFileName = %s) (KCB = %p) :: Key %wZ \n", Process,KeyBody->ProcessID,ImageName,kcb,Name); if( Process ) { ObDereferenceObject (Process); } #ifdef CM_LEAK_STACK_TRACES if( KeyBody->Callers != 0 ) { ULONG i; DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Callers Stack Trace : \n"); for( i=0;iCallers;i++) { DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\t CallerAddress[%lu] = %p \n",i,KeyBody->CallerAddress[i]); } } #endif //CM_LEAK_STACK_TRACES } #endif //_CM_LDR_ } else { // // NtQueryOpenSubKeysEx: build up the return buffer; make sure we only touch it // inside a try except as it's user-mode buffer // PQUERY_OPEN_SUBKEYS_CONTEXT QueryContext = (PQUERY_OPEN_SUBKEYS_CONTEXT)Context; PKEY_OPEN_SUBKEYS_INFORMATION SubkeysInfo = (PKEY_OPEN_SUBKEYS_INFORMATION)(QueryContext->Buffer); ULONG SizeNeeded; // // we need to ignore the one created by us inside NtQueryOpenSubKeysEx // if( QueryContext->KeyBodyToIgnore != KeyBody ) { // // update RequiredSize; we do this regardless if we have room or not in the buffer // reserve for one entry in the array and the unicode name buffer // SizeNeeded = (sizeof(KEY_PID_ARRAY) + (ULONG)(Name->Length)); QueryContext->RequiredSize += SizeNeeded; // // if we have encountered an error (overflow, or else) at some previous iteration, no point going on // if( NT_SUCCESS(QueryContext->StatusCode) ) { // // see if we have enough room for current entry. // if( (QueryContext->UsedLength + SizeNeeded) > QueryContext->BufferLength ) { // // buffer not big enough; // QueryContext->StatusCode = STATUS_BUFFER_OVERFLOW; } else { // // we have established we have enough room; create/add a new entry to the key array // and build up unicode name buffer. copy key name to it. // array elements are at the beggining of the user buffer, while name buffers start at // the end and continue bacwards, as long as there is enough room. // try { // // protect user mode memory // SubkeysInfo->KeyArray[SubkeysInfo->Count].PID = KeyBody->ProcessID; SubkeysInfo->KeyArray[SubkeysInfo->Count].KeyName.Length = Name->Length; SubkeysInfo->KeyArray[SubkeysInfo->Count].KeyName.MaximumLength = Name->Length; SubkeysInfo->KeyArray[SubkeysInfo->Count].KeyName.Buffer = (PWSTR)((PUCHAR)QueryContext->CurrentNameBuffer - Name->Length); RtlCopyMemory( SubkeysInfo->KeyArray[SubkeysInfo->Count].KeyName.Buffer, Name->Buffer, Name->Length); // // update array count and work vars inside the querycontext // SubkeysInfo->Count++; QueryContext->CurrentNameBuffer = (PUCHAR)QueryContext->CurrentNameBuffer - Name->Length; QueryContext->UsedLength += SizeNeeded; } except (EXCEPTION_EXECUTE_HANDLER) { QueryContext->StatusCode = GetExceptionCode(); } } } } } // count it (*Count)++; KeyBody = (PCM_KEY_BODY)KeyBody->KeyBodyList.Flink; } ExFreePoolWithTag(Name, CM_NAME_TAG | PROTECTED_POOL); } VOID CmpFlushNotifiesOnKeyBodyList( IN PCM_KEY_CONTROL_BLOCK kcb ) { PCM_KEY_BODY KeyBody; ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); Again: if( IsListEmpty(&(kcb->KeyBodyListHead)) == FALSE ) { // // now iterate through the list of KEY_BODYs referencing this kcb // KeyBody = (PCM_KEY_BODY)kcb->KeyBodyListHead.Flink; while( KeyBody != (PCM_KEY_BODY)(&(kcb->KeyBodyListHead)) ) { KeyBody = CONTAINING_RECORD(KeyBody, CM_KEY_BODY, KeyBodyList); // // sanity check: this should be a KEY_BODY // ASSERT_KEY_OBJECT(KeyBody); // // flush any notifies that might be set on it // if( KeyBody->NotifyBlock ) { // // add an extra reference on the key body so it won't go away. // //ObReferenceObject(KeyBody); if(ObReferenceObjectSafe(KeyBody)) { CmpFlushNotify(KeyBody,TRUE); ASSERT( KeyBody->NotifyBlock == NULL ); ObDereferenceObject(KeyBody); goto Again; } } KeyBody = (PCM_KEY_BODY)KeyBody->KeyBodyList.Flink; } } } VOID CmpCleanUpKCBCacheTable() /*++ Routine Description: Kicks out of cache all kcbs with RefCount == 0 Arguments: Return Value: --*/ { ULONG i; PCM_KEY_HASH *Current; PCM_KEY_CONTROL_BLOCK kcb; PAGED_CODE(); ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); for (i=0; iRefCount == 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; } } } PERFINFO_REG_DUMP_CACHE() ULONG CmpSearchForOpenSubKeys( PCM_KEY_CONTROL_BLOCK KeyControlBlock, SUBKEY_SEARCH_TYPE SearchType, PVOID SearchContext ) /*++ 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 Parent; ULONG 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 // CmpCleanUpKCBCacheTable(); 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; // // dump the root first if we were asked to do so. // if(SearchType == SearchAndCount) { CmpDumpKeyBodyList(KeyControlBlock,&Count,SearchContext); } for (i=0; iTotalLevels > KeyControlBlock->TotalLevels) { LevelDiff = kcb->TotalLevels - KeyControlBlock->TotalLevels; Parent = kcb; for (l=0; lParentKcb; } if (Parent == KeyControlBlock) { // // Found a match; // if( SearchType == SearchIfExist ) { Count = 1; break; } else if(SearchType == SearchAndTagNoDelayClose) { kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; } 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(); Count++; // // don't mess with read only kcbs; this prevents a potential hack when // trying to FORCE_RESTORE over WPA keys. // if( !CmIsKcbReadOnly(kcb) ) { // // flush any pending notifies as the kcb won't be around any longer // CmpFlushNotifiesOnKeyBodyList(kcb); CmpCleanUpSubKeyInfo(kcb->ParentKcb); kcb->Delete = TRUE; CmpRemoveKeyControlBlock(kcb); kcb->KeyCell = HCELL_NIL; // // Restart the search // goto StartDeref; } } else if(SearchType == SearchAndCount) { // // here do the dumping and count incrementing stuff // CmpDumpKeyBodyList(kcb,&Count,SearchContext); #ifdef NT_RENAME_KEY } else if( SearchType == SearchAndRehash ) { // // every kcb which has the one passed as a parameter // as an ancestor needs to be moved to the right location // in the kcb hash table. // ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); if( CmpRehashKcbSubtree(KeyControlBlock,kcb) == TRUE ) { // // at least one kcb has been moved, we need to reiterate this bucket // goto StartDeref; } #endif //NT_RENAME_KEY } } } Current = &kcb->NextHash; } } } return Count; } #ifdef NT_RENAME_KEY ULONG CmpComputeKcbConvKey( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: Computes the convkey for this kcb based on the NCB and its parent ConvKey Arguments: KeyControlBlock - Supplies the key control block for the key for which the ConvKey is to be calculated Return Value: The new ConvKey Notes: This is to be used by the rename key api, which needs to rehash kcbs --*/ { ULONG ConvKey = 0; ULONG Cnt; WCHAR Cp; PUCHAR u; PWCHAR w; PAGED_CODE(); if( KeyControlBlock->ParentKcb != NULL ) { ConvKey = KeyControlBlock->ParentKcb->ConvKey; } // // Manually compute the hash to use. // ASSERT(KeyControlBlock->NameBlock->NameLength > 0); u = (PUCHAR)(&(KeyControlBlock->NameBlock->Name[0])); w = (PWCHAR)u; for( Cnt = 0; Cnt < KeyControlBlock->NameBlock->NameLength;) { if( KeyControlBlock->NameBlock->Compressed ) { Cp = (WCHAR)(*u); u++; Cnt += sizeof(UCHAR); } else { Cp = *w; w++; Cnt += sizeof(WCHAR); } ASSERT( Cp != OBJ_NAME_PATH_SEPARATOR ); ConvKey = 37 * ConvKey + (ULONG)CmUpcaseUnicodeChar(Cp); } return ConvKey; } BOOLEAN CmpRehashKcbSubtree( PCM_KEY_CONTROL_BLOCK Start, PCM_KEY_CONTROL_BLOCK End ) /*++ Routine Description: Walks the path between End and Start and rehashed all kcbs that need rehashing; Assumptions: It is apriori taken that Start is an ancestor of End; Works in two steps: 1. walks the path backwards from End to Start, reverting the back-link (we use the ParentKcb member in the kcb structure for that). I.e. we build a forward path from Start to End 2.Walks the forward path built at 1, rehashes kcbs whos need rehashing and restores the parent relationship. Arguments: KeyControlBlock - where we start kcb - where we stop Return Value: TRUE if at least one kcb has been rehashed --*/ { PCM_KEY_CONTROL_BLOCK Parent; PCM_KEY_CONTROL_BLOCK Current; PCM_KEY_CONTROL_BLOCK TmpKcb; ULONG ConvKey; BOOLEAN Result; PAGED_CODE(); #if DBG // // make sure Start is an ancestor of End; // { ULONG LevelDiff = End->TotalLevels - Start->TotalLevels; ASSERT( (LONG)LevelDiff >= 0 ); TmpKcb = End; for(;LevelDiff; LevelDiff--) { TmpKcb = TmpKcb->ParentKcb; } ASSERT( TmpKcb == Start ); } #endif // // Step 1: walk the path backwards (using the parentkcb link) and // revert it, until we reach Start. It is assumed that Start is an // ancestor of End (the caller must not call this function otherwise !!!) // Current = NULL; Parent = End; while( Current != Start ) { // // revert the link // TmpKcb = Parent->ParentKcb; Parent->ParentKcb = Current; Current = Parent; Parent = TmpKcb; ASSERT( Current->TotalLevels >= Start->TotalLevels ); } ASSERT( Current == Start ); // // Step 2: Walk the forward path built at 1 and rehash the kcbs that need // caching; At the same time, restore the links (parent relationships) // Result = FALSE; while( Current != NULL ) { // // see if we need to rehash this kcb; // // // restore the parent relationship; need to do this first so // CmpComputeKcbConvKey works OK // TmpKcb = Current->ParentKcb; Current->ParentKcb = Parent; ConvKey = CmpComputeKcbConvKey(Current); if( ConvKey != Current->ConvKey ) { // // rehash the kcb by removing it from hash, and then inserting it // again with th new ConvKey // CmpRemoveKeyHash(&(Current->KeyHash)); Current->ConvKey = ConvKey; CmpInsertKeyHash(&(Current->KeyHash),FALSE); Result = TRUE; } // // advance forward // Parent = Current; Current = TmpKcb; } ASSERT( Parent == End ); return Result; } #endif //NT_RENAME_KEY BOOLEAN CmpReferenceKeyControlBlock( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) { // Note: this is called only under KCB lock LONG RefCount; RefCount = (InterlockedIncrement( (PLONG)&KeyControlBlock->RefCount )) & 0xffff; if (RefCount == 1) { // // need to get the lock exclusive as we are changing the cache table // if (CmpKcbOwner != KeGetCurrentThread()) { CmpUnlockKCBTree(); CmpLockKCBTreeExclusive(); } CmpRemoveFromDelayedClose(KeyControlBlock); } else if (RefCount == 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 // InterlockedDecrement( (PLONG)&KeyControlBlock->RefCount); return FALSE; } return TRUE; } PCM_NAME_CONTROL_BLOCK CmpGetNameControlBlock( PUNICODE_STRING NodeName ) { PCM_NAME_CONTROL_BLOCK Ncb = NULL; ULONG Cnt; WCHAR *Cp; WCHAR *Cp2; ULONG Index; ULONG i; ULONG Size; PCM_NAME_HASH CurrentName; BOOLEAN NameFound = FALSE; USHORT NameSize; BOOLEAN NameCompressed; ULONG NameConvKey=0; // // Calculate the ConvKey for this NodeName; // Cp = NodeName->Buffer; for (Cnt=0; CntLength; Cnt += sizeof(WCHAR)) { if (*Cp != OBJ_NAME_PATH_SEPARATOR) { NameConvKey = 37 * NameConvKey + (ULONG) CmUpcaseUnicodeChar(*Cp); } ++Cp; } // // Find the Name Size; // NameCompressed = TRUE; NameSize = NodeName->Length / sizeof(WCHAR); for (i=0;iLength/sizeof(WCHAR);i++) { if ((USHORT)NodeName->Buffer[i] > (UCHAR)-1) { NameSize = NodeName->Length; NameCompressed = FALSE; } } Index = GET_HASH_INDEX(NameConvKey); CurrentName = CmpNameCacheTable[Index]; while (CurrentName) { Ncb = CONTAINING_RECORD(CurrentName, CM_NAME_CONTROL_BLOCK, NameHash); if ((NameConvKey == CurrentName->ConvKey) && (NameSize == Ncb->NameLength)) { // // Hash value matches, compare the names. // NameFound = TRUE; if (Ncb->Compressed) { // we already know the name is uppercase if (CmpCompareCompressedName(NodeName, Ncb->Name, NameSize, CMP_DEST_UP)) { NameFound = FALSE; } } else { Cp = (WCHAR *) NodeName->Buffer; Cp2 = (WCHAR *) Ncb->Name; for (i=0 ;iNameLength; i+= sizeof(WCHAR)) { // // Cp2 is always uppercase; see below // if (CmUpcaseUnicodeChar(*Cp) != (*Cp2) ) { NameFound = FALSE; break; } ++Cp; ++Cp2; } } if (NameFound) { // // Found it, increase the refcount. // if ((USHORT) (Ncb->RefCount + 1) == 0) { // // We have maxed out the ref count. // fail the call. // Ncb = NULL; } else { ++Ncb->RefCount; } break; } } CurrentName = CurrentName->NextHash; } if (NameFound == FALSE) { // // Now need to create one Name block for this string. // Size = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + NameSize; Ncb = ExAllocatePoolWithTag(PagedPool, Size, CM_NAME_TAG | PROTECTED_POOL); if (Ncb == NULL) { return(NULL); } RtlZeroMemory(Ncb, Size); // // Update all the info for this newly created Name block. // Starting with whistler, the name is always upercase in kcb name block // if (NameCompressed) { Ncb->Compressed = TRUE; for (i=0;iName)[i] = (UCHAR)CmUpcaseUnicodeChar(NodeName->Buffer[i]); } } else { Ncb->Compressed = FALSE; for (i=0;iName[i] = CmUpcaseUnicodeChar(NodeName->Buffer[i]); } } Ncb->ConvKey = NameConvKey; Ncb->RefCount = 1; Ncb->NameLength = NameSize; CurrentName = &(Ncb->NameHash); // // Insert into Name Hash table. // CurrentName->NextHash = CmpNameCacheTable[Index]; CmpNameCacheTable[Index] = CurrentName; } return(Ncb); } VOID CmpDereferenceNameControlBlockWithLock( PCM_NAME_CONTROL_BLOCK Ncb ) { PCM_NAME_HASH *Prev; PCM_NAME_HASH Current; if (--Ncb->RefCount == 0) { // // Remove it from the the Hash Table // Prev = &(GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey)); while (TRUE) { Current = *Prev; ASSERT(Current != NULL); if (Current == &(Ncb->NameHash)) { *Prev = Current->NextHash; break; } Prev = &Current->NextHash; } // // Free storage // ExFreePoolWithTag(Ncb, CM_NAME_TAG | PROTECTED_POOL); } return; } VOID CmpRebuildKcbCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: rebuilds all the kcb cache values from knode; this routine is intended to be called after a tree sync/copy Arguments: KeyControlBlock - pointer to a key control block. Return Value: NONE. --*/ { PCM_KEY_NODE Node; ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); ASSERT( !(KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) ); Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell); if( Node == NULL ) { // // this shouldn't happen as we should have the knode arround // ASSERT( FALSE ); return; } HvReleaseCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell); // subkey info; CmpCleanUpSubKeyInfo(KeyControlBlock); // value cache CmpCleanUpKcbValueCache(KeyControlBlock); CmpSetUpKcbValueCache(KeyControlBlock,Node->ValueList.Count,Node->ValueList.List); // the rest of the cache KeyControlBlock->KcbLastWriteTime = Node->LastWriteTime; KeyControlBlock->KcbMaxNameLen = (USHORT)Node->MaxNameLen; KeyControlBlock->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen; KeyControlBlock->KcbMaxValueDataLen = Node->MaxValueDataLen; } VOID CmpCleanUpSubKeyInfo( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: Clean up the subkey information cache due to create or delete keys. Registry is locked exclusively and no need to lock the KCB. Arguments: KeyControlBlock - pointer to a key control block. Return Value: NONE. --*/ { PCM_KEY_NODE Node; ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); if (KeyControlBlock->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT)) { if (KeyControlBlock->ExtFlags & (CM_KCB_SUBKEY_HINT)) { ExFreePoolWithTag(KeyControlBlock->IndexHint, CM_CACHE_INDEX_TAG | PROTECTED_POOL); } KeyControlBlock->ExtFlags &= ~((CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT)); } // // Update the cached SubKeyCount in stored the kcb // if( KeyControlBlock->KeyCell == HCELL_NIL ) { // // prior call of ZwRestoreKey(REG_FORCE_RESTORE) invalidated this kcb // ASSERT( KeyControlBlock->Delete ); Node = NULL; } else { Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell); } if( Node == NULL ) { // // insufficient resources; mark subkeycount as invalid // KeyControlBlock->ExtFlags |= CM_KCB_INVALID_CACHED_INFO; } else { KeyControlBlock->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO; KeyControlBlock->SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; HvReleaseCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell); } } VOID CmpCleanUpKcbValueCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: Clean up cached value/data that are associated to this key. Arguments: KeyControlBlock - pointer to a key control block. Return Value: NONE. --*/ { ULONG i; PULONG_PTR CachedList; if (CMP_IS_CELL_CACHED(KeyControlBlock->ValueCache.ValueList)) { CachedList = (PULONG_PTR) CMP_GET_CACHED_CELLDATA(KeyControlBlock->ValueCache.ValueList); for (i = 0; i < KeyControlBlock->ValueCache.Count; i++) { if (CMP_IS_CELL_CACHED(CachedList[i])) { // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadWrite( CMP_GET_CACHED_ADDRESS(CachedList[i]) ); ExFreePool((PVOID) CMP_GET_CACHED_ADDRESS(CachedList[i])); } } // Trying to catch the BAD guy who writes over our pool. CmpMakeSpecialPoolReadWrite( CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList) ); ExFreePool((PVOID) CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList)); // Mark the ValueList as NULL KeyControlBlock->ValueCache.ValueList = HCELL_NIL; } else if (KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) { // // This is a symbolic link key with symbolic name resolved. // Dereference to its real kcb and clear the bit. // if ((KeyControlBlock->ValueCache.RealKcb->RefCount == 1) && !(KeyControlBlock->ValueCache.RealKcb->Delete)) { KeyControlBlock->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; } CmpDereferenceKeyControlBlockWithLock(KeyControlBlock->ValueCache.RealKcb); KeyControlBlock->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND; } } 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 && 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'); #ifdef CMP_STATS CmpStatsDebug.CmpKcbNo--; ASSERT( CmpStatsDebug.CmpKcbNo >= 0 ); #endif CmpFreeKeyControlBlock( Kcb ); Kcb = ParentKcb; if (Kcb) { InterlockedDecrement( (PLONG)&Kcb->RefCount ); } } } PUNICODE_STRING CmpConstructName( PCM_KEY_CONTROL_BLOCK kcb ) /*++ Routine Description: Construct the name given a kcb. Arguments: kcb - Kcb for the key Return Value: Pointer to the unicode string constructed. Caller is responsible to free this storage space. --*/ { PUNICODE_STRING FullName; PCM_KEY_CONTROL_BLOCK TmpKcb; PCM_KEY_NODE KeyNode; SIZE_T Length; SIZE_T size; USHORT i; SIZE_T BeginPosition; WCHAR *w1, *w2; UCHAR *u2; // // Calculate the total string length. // Length = 0; TmpKcb = kcb; while (TmpKcb) { if (TmpKcb->NameBlock->Compressed) { Length += TmpKcb->NameBlock->NameLength * sizeof(WCHAR); } else { Length += TmpKcb->NameBlock->NameLength; } // // Add the space for OBJ_NAME_PATH_SEPARATOR; // Length += sizeof(WCHAR); TmpKcb = TmpKcb->ParentKcb; } if (Length > MAXUSHORT) { return NULL; } // // Allocate the pool for the unicode string // size = sizeof(UNICODE_STRING) + Length; FullName = (PUNICODE_STRING) ExAllocatePoolWithTag(PagedPool, size, CM_NAME_TAG | PROTECTED_POOL); if (FullName) { FullName->Buffer = (USHORT *) ((ULONG_PTR) FullName + sizeof(UNICODE_STRING)); FullName->Length = (USHORT) Length; FullName->MaximumLength = (USHORT) Length; // // Now fill the name into the buffer. // TmpKcb = kcb; BeginPosition = Length; while (TmpKcb) { if( (TmpKcb->KeyHive == NULL) || (TmpKcb->KeyCell == HCELL_NIL) || (TmpKcb->ExtFlags & CM_KCB_KEY_NON_EXIST) ) { ExFreePoolWithTag(FullName, CM_NAME_TAG | PROTECTED_POOL); FullName = NULL; break; } KeyNode = (PCM_KEY_NODE)HvGetCell(TmpKcb->KeyHive,TmpKcb->KeyCell); if( KeyNode == NULL ) { // // could not allocate view // ExFreePoolWithTag(FullName, CM_NAME_TAG | PROTECTED_POOL); FullName = NULL; break; } // // sanity // #if DBG if( ! (TmpKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ) { ASSERT( KeyNode->NameLength == TmpKcb->NameBlock->NameLength ); ASSERT( ((KeyNode->Flags&KEY_COMP_NAME) && (TmpKcb->NameBlock->Compressed)) || ((!(KeyNode->Flags&KEY_COMP_NAME)) && (!(TmpKcb->NameBlock->Compressed))) ); } #endif //DBG // // Calculate the begin position of each subkey. Then fill in the char. // // if (TmpKcb->NameBlock->Compressed) { BeginPosition -= (TmpKcb->NameBlock->NameLength + 1) * sizeof(WCHAR); w1 = &(FullName->Buffer[BeginPosition/sizeof(WCHAR)]); *w1 = OBJ_NAME_PATH_SEPARATOR; w1++; if( ! (TmpKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ) { // // Get the name from the knode; to preserve case // u2 = (UCHAR *) &(KeyNode->Name[0]); } else { // // get it from the kcb, as in the keynode we don't hold the right name (see PROTO.HIV nodes) // u2 = (UCHAR *) &(TmpKcb->NameBlock->Name[0]); } for (i=0; iNameBlock->NameLength; i++) { *w1 = (WCHAR)(*u2); w1++; u2++; } } else { BeginPosition -= (TmpKcb->NameBlock->NameLength + sizeof(WCHAR)); w1 = &(FullName->Buffer[BeginPosition/sizeof(WCHAR)]); *w1 = OBJ_NAME_PATH_SEPARATOR; w1++; if( ! (TmpKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ) { // // Get the name from the knode; to preserve case // w2 = KeyNode->Name; } else { // // get it from the kcb, as in the keynode we don't hold the right name (see PROTO.HIV nodes) // w2 = TmpKcb->NameBlock->Name; } for (i=0; iNameBlock->NameLength; i=i+sizeof(WCHAR)) { *w1 = *w2; w1++; w2++; } } HvReleaseCell(TmpKcb->KeyHive,TmpKcb->KeyCell); TmpKcb = TmpKcb->ParentKcb; } } return (FullName); } 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; 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; CntDelete = FALSE; kcb->RefCount = 1; kcb->KeyHive = Hive; kcb->KeyCell = Cell; kcb->ConvKey = ConvKey; // Initialize as not on delayed close (0=1st delayed close slot) kcb->DelayedCloseIndex = CmpDelayedCloseSize; #ifdef CMP_STATS // colect stats CmpStatsDebug.CmpKcbNo++; if( CmpStatsDebug.CmpKcbNo > CmpStatsDebug.CmpMaxKcbNo ) { CmpStatsDebug.CmpMaxKcbNo = CmpStatsDebug.CmpKcbNo; } #endif } ASSERT_KCB(kcb); // // Find location to insert kcb in kcb tree. // BEGIN_KCB_LOCK_GUARD; CmpLockKCBTreeExclusive(); // // 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'); #ifdef CMP_STATS CmpStatsDebug.CmpKcbNo--; ASSERT( CmpStatsDebug.CmpKcbNo >= 0 ); #endif CmpFreeKeyControlBlock(kcb); ASSERT_KCB(kcbmatch); kcb = kcbmatch; if( !CmpReferenceKeyControlBlock(kcb) ) { // // 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 { // // update the keycell and hive, in case this is a fake kcb // if( (kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && (!FakeKey) ) { kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO; kcb->KeyHive = Hive; kcb->KeyCell = Cell; } // // Update the cached information stored in the kcb, since we have the key_node handy // if (!(kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT)) ) { // SubKeyCount kcb->SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; // clean up the invalid flag (if any) kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO; } kcb->KcbLastWriteTime = Node->LastWriteTime; kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen; kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen; kcb->KcbMaxValueDataLen = Node->MaxValueDataLen; } } else { // // No kcb created previously, fill in all the data. // // // Now try to reference the parentkcb // if (ParentKcb) { if ( ((ParentKcb->TotalLevels + 1) < CMP_MAX_REGISTRY_DEPTH) && (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'); #ifdef CMP_STATS CmpStatsDebug.CmpKcbNo--; ASSERT( CmpStatsDebug.CmpKcbNo >= 0 ); #endif CmpFreeKeyControlBlock(kcb); kcb = NULL; } } else { // // It is the \REGISTRY node. // kcb->ParentKcb = NULL; kcb->TotalLevels = 1; } if (kcb) { // // Cache the security cells in the kcb // CmpAssignSecurityToKcb(kcb,Node->Security); // // 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 = CmpDelayedCloseSize; if (FakeKey) { // // The KCb to be created is a fake one; // kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST; } CmpTraceKcbCreate(kcb); PERFINFO_REG_KCB_CREATE(kcb); // // Update the cached information stored in the kcb, since we have the key_node handy // // SubKeyCount kcb->SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; kcb->KcbLastWriteTime = Node->LastWriteTime; kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen; kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen; kcb->KcbMaxValueDataLen = Node->MaxValueDataLen; } 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'); #ifdef CMP_STATS CmpStatsDebug.CmpKcbNo--; ASSERT( CmpStatsDebug.CmpKcbNo >= 0 ); #endif CmpFreeKeyControlBlock(kcb); kcb = NULL; } } } #ifdef NT_UNLOAD_KEY_EX if( kcb && IsHiveFrozen(Hive) && (!(kcb->Flags & KEY_SYM_LINK)) ) { // // kcbs created inside a frozen hive should not be added to delayclose table. // kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; } #endif //NT_UNLOAD_KEY_EX CmpUnlockKCBTree(); END_KCB_LOCK_GUARD; return kcb; } BOOLEAN 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. If it returns KCB_WORKER_ERROR we bail out and signal the error to the caller. 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_HASH *Prev; ULONG WorkerResult; ULONG i; // // Walk the hash table // for (i=0; iDelete); 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 TRUE; } else if (WorkerResult == KCB_WORKER_ERROR) { return FALSE; } else if (WorkerResult == KCB_WORKER_DELETE) { ASSERT(Current->Delete); *Prev = Current->NextHash; continue; } else { ASSERT(WorkerResult == KCB_WORKER_CONTINUE); Prev = &Current->NextHash; } } } return TRUE; } VOID CmpDereferenceKeyControlBlock( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: Decrements the reference count on a key control block, and frees it if it becomes zero. It is expected that no notify control blocks remain if the reference count becomes zero. Arguments: KeyControlBlock - pointer to a key control block. Return Value: NONE. --*/ { LONG OldRefCount; LONG NewRefCount; OldRefCount = *(PLONG)&KeyControlBlock->RefCount; //get entire dword NewRefCount = OldRefCount - 1; if( (NewRefCount & 0xffff) > 0 && InterlockedCompareExchange((PLONG)&KeyControlBlock->RefCount,NewRefCount,OldRefCount) == OldRefCount ) { return; } BEGIN_KCB_LOCK_GUARD; CmpLockKCBTreeExclusive(); CmpDereferenceKeyControlBlockWithLock(KeyControlBlock) ; CmpUnlockKCBTree(); END_KCB_LOCK_GUARD; return; } VOID CmpDereferenceKeyControlBlockWithLock( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) { ASSERT_KCB(KeyControlBlock); ASSERT_KCB_LOCK_OWNED_EXCLUSIVE(); if( (InterlockedDecrement( (PLONG)&KeyControlBlock->RefCount ) & 0xffff) == 0) { // // Remove kcb from the tree // // delay close disabled during boot; up to the point CCS is saved. // for symbolic links, we still need to keep the symbolic link kcb around. // if((CmpHoldLazyFlush && (!(KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND)) && (!(KeyControlBlock->Flags & KEY_SYM_LINK))) || (KeyControlBlock->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ) { // // Free storage directly so we can clean up junk quickly. // // // Need to free all cached Index List, Index Leaf, Value, etc. // CmpCleanUpKcbCacheWithLock(KeyControlBlock); } else if (!KeyControlBlock->Delete) { // // Put this kcb on our delayed close list. // CmpAddToDelayedClose(KeyControlBlock); } else { // // Free storage directly as there is no point in putting this on // our delayed close list. // // // Need to free all cached Index List, Index Leaf, Value, etc. // CmpCleanUpKcbCacheWithLock(KeyControlBlock); } } return; } VOID CmpRemoveKeyControlBlock( PCM_KEY_CONTROL_BLOCK KeyControlBlock ) /*++ Routine Description: Remove a key control block from the KCB tree. It is expected that no notify control blocks remain. The kcb will NOT be freed, call DereferenceKeyControlBlock for that. This call assumes the KCB tree is already locked or registry is locked exclusively. Arguments: KeyControlBlock - pointer to a key control block. Return Value: NONE. --*/ { ASSERT_KCB(KeyControlBlock); // // Remove the KCB from the hash table // CmpRemoveKeyHash(&KeyControlBlock->KeyHash); return; } BOOLEAN CmpFreeKeyBody( PHHIVE Hive, HCELL_INDEX Cell ) /*++ Routine Description: Free storage for the key entry Hive.Cell refers to, including its class and security data. Will NOT free child list or value list. Arguments: Hive - supplies a pointer to the hive control structure for the hive Cell - supplies index of key to free Return Value: TRUE - success FALSE - error; couldn't map cell --*/ { PCELL_DATA key; // // map in the cell // key = HvGetCell(Hive, Cell); if( key == NULL ) { // // we couldn't map the bin containing this cell // Sorry, we cannot free the keybody // this shouldn't happen as the cell must've been // marked dirty (i.e. pinned in memory) by now // ASSERT( FALSE ); return FALSE; } if (!(key->u.KeyNode.Flags & KEY_HIVE_EXIT)) { if (key->u.KeyNode.Security != HCELL_NIL) { HvFreeCell(Hive, key->u.KeyNode.Security); } if (key->u.KeyNode.ClassLength > 0) { HvFreeCell(Hive, key->u.KeyNode.Class); } } HvReleaseCell(Hive,Cell); // // unmap the cell itself and free it // HvFreeCell(Hive, Cell); return TRUE; } PCM_KEY_CONTROL_BLOCK CmpInsertKeyHash( IN PCM_KEY_HASH KeyHash, IN BOOLEAN FakeKey ) /*++ Routine Description: Adds a key hash structure to the hash table. The hash table will be checked to see if a duplicate entry already exists. If a duplicate is found, its kcb will be returned. If a duplicate is not found, NULL will be returned. Arguments: KeyHash - Supplies the key hash structure to be added. Return Value: NULL - if the supplied key has was added PCM_KEY_HASH - The duplicate hash entry, if one was found --*/ { ULONG Index; PCM_KEY_HASH Current; ASSERT_KEY_HASH(KeyHash); Index = GET_HASH_INDEX(KeyHash->ConvKey); // // If this is a fake key, we will use the cell and hive from its // parent for uniqeness. To deal with the case when the fake // has the same ConvKey as its parent (in which case we cannot distingish // between the two), we set the lowest bit of the fake key's cell. // // It's possible (unlikely) that we cannot distingish two fake keys // (when their Convkey's are the same) under the same key. It is not breaking // anything, we just cannot find the other one in cache lookup. // // if (FakeKey) { KeyHash->KeyCell++; } // // First look for duplicates. // Current = CmpCacheTable[Index]; while (Current) { ASSERT_KEY_HASH(Current); // // We must check ConvKey since we can create a fake kcb // for keys that does not exist. // We will use the Hive and Cell from the parent. // if ((KeyHash->ConvKey == Current->ConvKey) && (KeyHash->KeyCell == Current->KeyCell) && (KeyHash->KeyHive == Current->KeyHive)) { // // Found a match // return(CONTAINING_RECORD(Current, CM_KEY_CONTROL_BLOCK, KeyHash)); } Current = Current->NextHash; } #if DBG // // Make sure this key is not somehow cached in the wrong spot. // { ULONG DbgIndex; PCM_KEY_CONTROL_BLOCK kcb; for (DbgIndex = 0; DbgIndex < CmpHashTableSize; DbgIndex++) { Current = CmpCacheTable[DbgIndex]; while (Current) { kcb = CONTAINING_RECORD(Current, CM_KEY_CONTROL_BLOCK, KeyHash); ASSERT_KEY_HASH(Current); ASSERT((KeyHash->KeyHive != Current->KeyHive) || FakeKey || (kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) || (KeyHash->KeyCell != Current->KeyCell)); Current = Current->NextHash; } } } #endif // // No duplicate was found, add this entry at the head of the list // KeyHash->NextHash = CmpCacheTable[Index]; CmpCacheTable[Index] = KeyHash; return(NULL); } VOID CmpRemoveKeyHash( IN PCM_KEY_HASH KeyHash ) /*++ Routine Description: Removes a key hash structure from the hash table. Arguments: KeyHash - Supplies the key hash structure to be deleted. Return Value: None --*/ { ULONG Index; PCM_KEY_HASH *Prev; PCM_KEY_HASH Current; ASSERT_KEY_HASH(KeyHash); Index = GET_HASH_INDEX(KeyHash->ConvKey); // // Find this entry. // Prev = &CmpCacheTable[Index]; while (TRUE) { Current = *Prev; ASSERT(Current != NULL); ASSERT_KEY_HASH(Current); if (Current == KeyHash) { *Prev = Current->NextHash; #if DBG if (*Prev) { ASSERT_KEY_HASH(*Prev); } #endif break; } Prev = &Current->NextHash; } } VOID CmpInitializeCache() { ULONG TotalCmCacheSize; TotalCmCacheSize = CmpHashTableSize * sizeof(PCM_KEY_HASH); CmpCacheTable = ExAllocatePoolWithTag(PagedPool, TotalCmCacheSize, 'aCMC'); if (CmpCacheTable == NULL) { CM_BUGCHECK(CONFIG_INITIALIZATION_FAILED,INIT_CACHE_TABLE,1,0,0); return; } RtlZeroMemory(CmpCacheTable, TotalCmCacheSize); TotalCmCacheSize = CmpHashTableSize * sizeof(PCM_NAME_HASH); CmpNameCacheTable = ExAllocatePoolWithTag(PagedPool, TotalCmCacheSize, 'aCMC'); if (CmpNameCacheTable == NULL) { CM_BUGCHECK(CONFIG_INITIALIZATION_FAILED,INIT_CACHE_TABLE,1,0,0); return; } RtlZeroMemory(CmpNameCacheTable, TotalCmCacheSize); CmpInitializeDelayedCloseTable(); } #ifdef CM_CHECK_FOR_ORPHANED_KCBS VOID CmpCheckForOrphanedKcbs( PHHIVE Hive ) /*++ Routine Description: Parses the entire kcb cache in search of kcbs that still reffer to the specified hive breakpoint when a match is found. Arguments: Hive - Supplies Hive. Return Value: none --*/ { PCM_KEY_CONTROL_BLOCK KeyControlBlock; PCM_KEY_HASH Current; ULONG i; // // Walk the hash table // for (i=0; iKeyHive == Hive ) { // // found it ! Break to investigate !!! // DbgPrint("\n Orphaned KCB (%p) found for hive (%p)\n\n",KeyControlBlock,Hive); DbgBreakPoint(); } Current = Current->NextHash; } } } #endif //CM_CHECK_FOR_ORPHANED_KCBS #ifdef ALLOC_DATA_PRAGMA #pragma data_seg() #endif