Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2169 lines
61 KiB

/*++
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;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_
} 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; 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,
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; 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();
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; Cnt<NodeName->Length; 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;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 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;i<NameSize;i++) {
((PUCHAR)Ncb->Name)[i] = (UCHAR)CmUpcaseUnicodeChar(NodeName->Buffer[i]);
}
} else {
Ncb->Compressed = FALSE;
for (i=0;i<NameSize/sizeof(WCHAR);i++) {
Ncb->Name[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; 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;
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)CmUpcaseUnicodeChar(*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;
// 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; 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.
--*/
{
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; 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
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif