2061 lines
54 KiB
C
2061 lines
54 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
);
|
|||
|
|
|||
|
#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
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
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
|
|||
|
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);
|
|||
|
|
|||
|
//
|
|||
|
// dump it's name and owning process
|
|||
|
//
|
|||
|
#ifndef _CM_LDR_
|
|||
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Process %p (KCB = %p) :: Key %wZ \n",KeyBody->Process,kcb,Name);
|
|||
|
#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;i<KeyBody->Callers;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_
|
|||
|
|
|||
|
// 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;
|
|||
|
|
|||
|
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
|
|||
|
//
|
|||
|
CmpFlushNotify(KeyBody);
|
|||
|
|
|||
|
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; 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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
PERFINFO_REG_DUMP_CACHE()
|
|||
|
|
|||
|
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;
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
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 == 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();
|
|||
|
|
|||
|
//
|
|||
|
// 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);
|
|||
|
|
|||
|
#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)RtlUpcaseUnicodeChar(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
|
|||
|
)
|
|||
|
{
|
|||
|
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_NAME_CONTROL_BLOCK
|
|||
|
CmpGetNameControlBlock(
|
|||
|
PUNICODE_STRING NodeName
|
|||
|
)
|
|||
|
{
|
|||
|
PCM_NAME_CONTROL_BLOCK Ncb;
|
|||
|
ULONG Cnt;
|
|||
|
WCHAR *Cp;
|
|||
|
WCHAR *Cp2;
|
|||
|
ULONG Index;
|
|||
|
ULONG i;
|
|||
|
ULONG Size;
|
|||
|
PCM_NAME_HASH CurrentName;
|
|||
|
ULONG rc;
|
|||
|
BOOLEAN NameFound = FALSE;
|
|||
|
USHORT NameSize;
|
|||
|
BOOLEAN NameCompressed;
|
|||
|
ULONG NameConvKey=0;
|
|||
|
LONG Result;
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the ConvKey for this NodeName;
|
|||
|
//
|
|||
|
|
|||
|
Cp = NodeName->Buffer;
|
|||
|
for (Cnt=0; Cnt<NodeName->Length; Cnt += sizeof(WCHAR)) {
|
|||
|
if (*Cp != OBJ_NAME_PATH_SEPARATOR) {
|
|||
|
NameConvKey = 37 * NameConvKey + (ULONG) RtlUpcaseUnicodeChar(*Cp);
|
|||
|
}
|
|||
|
++Cp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find the Name Size;
|
|||
|
//
|
|||
|
NameCompressed = TRUE;
|
|||
|
NameSize = NodeName->Length / sizeof(WCHAR);
|
|||
|
for (i=0;i<NodeName->Length/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 ;i<Ncb->NameLength; i+= sizeof(WCHAR)) {
|
|||
|
//
|
|||
|
// Cp2 is always uppercase; see bellow
|
|||
|
//
|
|||
|
if (RtlUpcaseUnicodeChar(*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;i<NameSize;i++) {
|
|||
|
((PUCHAR)Ncb->Name)[i] = (UCHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
|
|||
|
}
|
|||
|
} else {
|
|||
|
Ncb->Compressed = FALSE;
|
|||
|
for (i=0;i<NameSize/sizeof(WCHAR);i++) {
|
|||
|
Ncb->Name[i] = RtlUpcaseUnicodeChar(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;
|
|||
|
PCELL_DATA pcell;
|
|||
|
ULONG realsize;
|
|||
|
BOOLEAN small;
|
|||
|
|
|||
|
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) {
|
|||
|
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;
|
|||
|
USHORT Length;
|
|||
|
USHORT size;
|
|||
|
USHORT i;
|
|||
|
USHORT 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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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 = Length;
|
|||
|
FullName->MaximumLength = 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; i<TmpKcb->NameBlock->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; i<TmpKcb->NameBlock->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;
|
|||
|
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)) {
|
|||
|
//
|
|||
|
// UNICODE_NULL is a valid char !!!
|
|||
|
//
|
|||
|
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 = CmpAllocateKeyControlBlock( );
|
|||
|
|
|||
|
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->ConvKey = ConvKey;
|
|||
|
|
|||
|
#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 (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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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_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 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.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BEGIN_KCB_LOCK_GUARD;
|
|||
|
CmpLockKCBTreeExclusive();
|
|||
|
CmpDereferenceKeyControlBlockWithLock(KeyControlBlock) ;
|
|||
|
CmpUnlockKCBTree();
|
|||
|
END_KCB_LOCK_GUARD;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CmpDereferenceKeyControlBlockWithLock(
|
|||
|
PCM_KEY_CONTROL_BLOCK KeyControlBlock
|
|||
|
)
|
|||
|
{
|
|||
|
ULONG_PTR FreeIndex;
|
|||
|
PCM_KEY_CONTROL_BLOCK KcbToFree;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
ASSERT_KCB(KeyControlBlock);
|
|||
|
|
|||
|
if (--KeyControlBlock->RefCount == 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
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
HASH_VALUE Hash;
|
|||
|
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;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
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; i<CmpHashTableSize; i++) {
|
|||
|
Current = CmpCacheTable[i];
|
|||
|
while (Current) {
|
|||
|
KeyControlBlock = CONTAINING_RECORD(Current, CM_KEY_CONTROL_BLOCK, KeyHash);
|
|||
|
ASSERT_KCB(KeyControlBlock);
|
|||
|
|
|||
|
if( KeyControlBlock->KeyHive == 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
|
|||
|
|
|||
|
|