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
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
|
|
|