1723 lines
46 KiB
C
1723 lines
46 KiB
C
/*++
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
cmindex.c
|
|
|
|
Abstract:
|
|
This module contains cm routines that understand the structure of child subkey indicies.
|
|
|
|
Author:
|
|
Bryan M. Willman (bryanwi) 21-Apr-92
|
|
--*/
|
|
|
|
/*
|
|
|
|
The Structure:
|
|
|
|
Use a 1 or 2 level tree. Leaf nodes are arrays of pointers to
|
|
cells, sorted. Binary search to find cell of interest. Directory
|
|
node (can be only one) is an array of pointers to leaf blocks.
|
|
Do compare on last entry of each leaf block.
|
|
|
|
One Level:
|
|
|
|
Key--->+----+
|
|
| |
|
|
| x----------><key whose name is "apple", string in key>
|
|
| |
|
|
+----+
|
|
| |
|
|
| x----------><as above, but key named "banana">
|
|
| |
|
|
+----+
|
|
| |
|
|
| |
|
|
| |
|
|
+----+
|
|
| |
|
|
| |
|
|
| |
|
|
+----+
|
|
| |
|
|
| x----------><as above, but key named "zumwat">
|
|
| |
|
|
+----+
|
|
|
|
|
|
Two Level:
|
|
|
|
Key--->+----+
|
|
| | +-----+
|
|
| x----->| |
|
|
| | | x----------------->"aaa"
|
|
+----+ | |
|
|
| | +-----+
|
|
| | | |
|
|
| | | |
|
|
+----+ | |
|
|
| | +-----+
|
|
| | | |
|
|
| | | x----------------->"abc"
|
|
+----+ | |
|
|
| | +-----+
|
|
| |
|
|
| |
|
|
+----+
|
|
| | +-----+
|
|
| x----->| |
|
|
| | | x----------------->"w"
|
|
+----+ | |
|
|
+-----+
|
|
| |
|
|
| |
|
|
| |
|
|
+-----+
|
|
| |
|
|
| x----------------->"z"
|
|
| |
|
|
+-----+
|
|
|
|
|
|
Never more than two levels.
|
|
|
|
Each block must fix in on HBLOCK_SIZE Cell. Allows about 1000
|
|
entries. Max of 1 million total, best case. Worst case something
|
|
like 1/4 of that.
|
|
|
|
*/
|
|
|
|
#include "cmp.h"
|
|
|
|
ULONG
|
|
CmpFindSubKeyInRoot(
|
|
PHHIVE Hive,
|
|
PCM_KEY_INDEX Index,
|
|
PUNICODE_STRING SearchName,
|
|
PHCELL_INDEX Child
|
|
);
|
|
|
|
ULONG
|
|
CmpFindSubKeyInLeaf(
|
|
PHHIVE Hive,
|
|
PCM_KEY_INDEX Index,
|
|
ULONG IndexHint,
|
|
PUNICODE_STRING SearchName,
|
|
PHCELL_INDEX Child
|
|
);
|
|
|
|
LONG
|
|
CmpCompareInIndex(
|
|
PHHIVE Hive,
|
|
PUNICODE_STRING SearchName,
|
|
ULONG Count,
|
|
PCM_KEY_INDEX Index,
|
|
PHCELL_INDEX Child
|
|
);
|
|
|
|
LONG
|
|
CmpDoCompareKeyName(
|
|
PHHIVE Hive,
|
|
PUNICODE_STRING SearchName,
|
|
HCELL_INDEX Cell
|
|
);
|
|
|
|
HCELL_INDEX
|
|
CmpDoFindSubKeyByNumber(
|
|
PHHIVE Hive,
|
|
PCM_KEY_INDEX Index,
|
|
ULONG Number
|
|
);
|
|
|
|
HCELL_INDEX
|
|
CmpAddToLeaf(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX LeafCell,
|
|
HCELL_INDEX NewKey,
|
|
PUNICODE_STRING NewName
|
|
);
|
|
|
|
HCELL_INDEX
|
|
CmpSelectLeaf(
|
|
PHHIVE Hive,
|
|
PCM_KEY_NODE ParentKey,
|
|
PUNICODE_STRING NewName,
|
|
HSTORAGE_TYPE Type,
|
|
PHCELL_INDEX *RootPointer
|
|
);
|
|
|
|
HCELL_INDEX
|
|
CmpSplitLeaf(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX RootCell,
|
|
ULONG RootSelect,
|
|
HSTORAGE_TYPE Type
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CmpFindSubKeyByName)
|
|
#pragma alloc_text(PAGE,CmpFindSubKeyInRoot)
|
|
#pragma alloc_text(PAGE,CmpFindSubKeyInLeaf)
|
|
#pragma alloc_text(PAGE,CmpDoCompareKeyName)
|
|
#pragma alloc_text(PAGE,CmpCompareInIndex)
|
|
#pragma alloc_text(PAGE,CmpFindSubKeyByNumber)
|
|
#pragma alloc_text(PAGE,CmpDoFindSubKeyByNumber)
|
|
#pragma alloc_text(PAGE,CmpAddSubKey)
|
|
#pragma alloc_text(PAGE,CmpAddToLeaf)
|
|
#pragma alloc_text(PAGE,CmpSelectLeaf)
|
|
#pragma alloc_text(PAGE,CmpSplitLeaf)
|
|
#pragma alloc_text(PAGE,CmpMarkIndexDirty)
|
|
#pragma alloc_text(PAGE,CmpRemoveSubKey)
|
|
#endif
|
|
|
|
ULONG CmpHintHits = 0;
|
|
ULONG CmpHintMisses = 0;
|
|
|
|
|
|
HCELL_INDEX
|
|
CmpFindSubKeyByName(
|
|
PHHIVE Hive,
|
|
PCM_KEY_NODE Parent,
|
|
PUNICODE_STRING SearchName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Find the child cell (either subkey or value) specified by name.
|
|
Arguments:
|
|
Hive - pointer to hive control structure for hive of interest
|
|
Parent - cell of key body which is parent of child of interest
|
|
SearchName - name of child of interest
|
|
Return Value:
|
|
Cell of matching child key, or HCELL_NIL if none.
|
|
--*/
|
|
{
|
|
PCM_KEY_INDEX IndexRoot;
|
|
HCELL_INDEX Child;
|
|
ULONG i;
|
|
ULONG FoundIndex;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpFindSubKeyByName:\n\t"));
|
|
KdPrint(("Hive=%08lx Parent=%08lx SearchName=%08lx\n", Hive, Parent, SearchName));
|
|
}
|
|
|
|
// Try first the Stable, then the Volatile store. Assumes that
|
|
// all Volatile refs in Stable space are zeroed out at boot.
|
|
|
|
for (i = 0; i < Hive->StorageTypeCount; i++) {
|
|
if (Parent->SubKeyCounts[i] != 0) {
|
|
ASSERT(HvIsCellAllocated(Hive, Parent->SubKeyLists[i]));
|
|
IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, Parent->SubKeyLists[i]);
|
|
|
|
if (IndexRoot->Signature == CM_KEY_INDEX_ROOT) {
|
|
CmpFindSubKeyInRoot(Hive, IndexRoot, SearchName, &Child);
|
|
if (Child == HCELL_NIL) {
|
|
continue;
|
|
}
|
|
IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, Child);
|
|
}
|
|
ASSERT((IndexRoot->Signature == CM_KEY_INDEX_LEAF) || (IndexRoot->Signature == CM_KEY_FAST_LEAF));
|
|
|
|
FoundIndex = CmpFindSubKeyInLeaf(Hive, IndexRoot, Parent->WorkVar, SearchName, &Child);
|
|
if (Child != HCELL_NIL) {
|
|
// WorkVar is used as a hint for the last successful lookup
|
|
// to improve our locality when similar keys are opened repeatedly.
|
|
if (FoundIndex == Parent->WorkVar) {
|
|
CmpHintHits++;
|
|
}
|
|
else {
|
|
CmpHintMisses++;
|
|
}
|
|
Parent->WorkVar = FoundIndex;
|
|
return Child;
|
|
}
|
|
}
|
|
}
|
|
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
|
|
ULONG
|
|
CmpFindSubKeyInRoot(
|
|
PHHIVE Hive,
|
|
PCM_KEY_INDEX Index,
|
|
PUNICODE_STRING SearchName,
|
|
PHCELL_INDEX Child
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Find the leaf index that would contain a key, if there is one.
|
|
Arguments:
|
|
Hive - pointer to hive control structure for hive of interest
|
|
Index - pointer to root index block
|
|
SearchName - pointer to name of key of interest
|
|
Child - pointer to variable to receive hcell_index of found leaf index
|
|
block, HCELL_NIL if none. Non nil does not necessarily mean the key is present, call FindSubKeyInLeaf to decide that.
|
|
Return Value:
|
|
Index in List of last Leaf Cell entry examined. If Child != HCELL_NIL,
|
|
Index is entry that matched, else, index is for last entry we looked
|
|
at. (Target Leaf will be this value plus or minus 1)
|
|
--*/
|
|
{
|
|
ULONG High;
|
|
ULONG Low;
|
|
ULONG CanCount;
|
|
HCELL_INDEX LeafCell;
|
|
PCM_KEY_INDEX Leaf;
|
|
LONG Result;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpFindSubKeyInRoot:\n\t"));
|
|
KdPrint(("Hive=%08lx Index=%08lx SearchName=%08lx\n", Hive, Index, SearchName));
|
|
}
|
|
|
|
ASSERT(Index->Count != 0);
|
|
ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);
|
|
|
|
High = Index->Count - 1;
|
|
Low = 0;
|
|
|
|
while (TRUE) {
|
|
// Compute where to look next, get correct pointer, do compare
|
|
|
|
CanCount = ((High - Low) / 2) + Low;
|
|
LeafCell = Index->List[CanCount];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF) || (Leaf->Signature == CM_KEY_FAST_LEAF));
|
|
ASSERT(Leaf->Count != 0);
|
|
|
|
Result = CmpCompareInIndex(Hive, SearchName, Leaf->Count - 1, Leaf, Child);
|
|
if (Result == 0) {
|
|
// SearchName == KeyName of last key in leaf, so this is our leaf
|
|
*Child = LeafCell;
|
|
return CanCount;
|
|
}
|
|
|
|
if (Result < 0) {
|
|
// SearchName < KeyName, so this may still be our leaf
|
|
Result = CmpCompareInIndex(Hive, SearchName, 0, Leaf, Child);
|
|
if (Result >= 0) {
|
|
// we know from above that SearchName is less than last key in leaf.
|
|
// since it is also >= first key in leaf, it must reside in leaf somewhere, and we are done
|
|
*Child = LeafCell;
|
|
return CanCount;
|
|
}
|
|
|
|
High = CanCount;
|
|
}
|
|
else {
|
|
// SearchName > KeyName
|
|
Low = CanCount;
|
|
}
|
|
|
|
if ((High - Low) <= 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we get here, High - Low = 1 or High == Low
|
|
|
|
ASSERT((High - Low == 1) || (High == Low));
|
|
LeafCell = Index->List[Low];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
Result = CmpCompareInIndex(Hive, SearchName, Leaf->Count - 1, Leaf, Child);
|
|
if (Result == 0) {
|
|
// found it
|
|
*Child = LeafCell;
|
|
return Low;
|
|
}
|
|
|
|
if (Result < 0) {
|
|
// SearchName < KeyName, so this may still be our leaf
|
|
Result = CmpCompareInIndex(Hive, SearchName, 0, Leaf, Child);
|
|
if (Result >= 0) {
|
|
// we know from above that SearchName is less than
|
|
// last key in leaf.
|
|
// since it is also >= first key in leaf, it must
|
|
// reside in leaf somewhere, and we are done
|
|
*Child = LeafCell;
|
|
return Low;
|
|
}
|
|
|
|
// does not exist, but belongs in Low or Leaf below low
|
|
|
|
*Child = HCELL_NIL;
|
|
return Low;
|
|
}
|
|
|
|
// see if High matches
|
|
|
|
LeafCell = Index->List[High];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
Result = CmpCompareInIndex(Hive, SearchName, Leaf->Count - 1, Leaf, Child);
|
|
if (Result == 0) {
|
|
// found it
|
|
*Child = LeafCell;
|
|
return High;
|
|
}
|
|
else if (Result < 0) {
|
|
// Clearly greater than low, or we wouldn't be here.
|
|
// So regardless of whether it's below the start
|
|
// of this leaf, it would be in this leaf if it were
|
|
// where, so report this leaf.
|
|
*Child = LeafCell;
|
|
return High;
|
|
}
|
|
|
|
// Off the high end
|
|
|
|
*Child = HCELL_NIL;
|
|
return High;
|
|
}
|
|
|
|
|
|
ULONG
|
|
CmpFindSubKeyInLeaf(
|
|
PHHIVE Hive,
|
|
PCM_KEY_INDEX Index,
|
|
ULONG HintIndex,
|
|
PUNICODE_STRING SearchName,
|
|
PHCELL_INDEX Child
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Find a named key in a leaf index, if it exists. The supplied index may be either a fast index or a slow one.
|
|
Arguments:
|
|
Hive - pointer to hive control structure for hive of interest
|
|
Index - pointer to leaf block
|
|
HintIndex - Supplies hint for first key to check.
|
|
SearchName - pointer to name of key of interest
|
|
Child - pointer to variable to receive hcell_index of found key HCELL_NIL if none found
|
|
Return Value:
|
|
Index in List of last cell. If Child != HCELL_NIL, is offset in
|
|
list at which Child was found. Else, is offset of last place
|
|
we looked.
|
|
--*/
|
|
{
|
|
ULONG High;
|
|
ULONG Low;
|
|
ULONG CanCount;
|
|
LONG Result;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpFindSubKeyInLeaf:\n\t"));
|
|
KdPrint(("Hive=%08lx Index=%08lx SearchName=%08lx\n", Hive, Index, SearchName));
|
|
}
|
|
|
|
ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) || (Index->Signature == CM_KEY_FAST_LEAF));
|
|
|
|
High = Index->Count - 1;
|
|
Low = 0;
|
|
if (HintIndex < High) {
|
|
CanCount = HintIndex;
|
|
}
|
|
else {
|
|
CanCount = High / 2;
|
|
}
|
|
|
|
if (Index->Count == 0) {
|
|
*Child = HCELL_NIL;
|
|
return 0;
|
|
}
|
|
|
|
while (TRUE) {
|
|
// Compute where to look next, get correct pointer, do compare
|
|
Result = CmpCompareInIndex(Hive, SearchName, CanCount, Index, Child);
|
|
if (Result == 0) {
|
|
// SearchName == KeyName
|
|
return CanCount;
|
|
}
|
|
|
|
if (Result < 0) {
|
|
// SearchName < KeyName
|
|
High = CanCount;
|
|
}
|
|
else {
|
|
// SearchName > KeyName
|
|
Low = CanCount;
|
|
}
|
|
|
|
if ((High - Low) <= 1) {
|
|
break;
|
|
}
|
|
CanCount = ((High - Low) / 2) + Low;
|
|
}
|
|
|
|
// If we get here, High - Low = 1 or High == Low
|
|
// Simply look first at Low, then at High
|
|
Result = CmpCompareInIndex(Hive, SearchName, Low, Index, Child);
|
|
if (Result == 0) {
|
|
// found it
|
|
return Low;
|
|
}
|
|
|
|
if (Result < 0) {
|
|
// does not exist, under
|
|
return Low;
|
|
}
|
|
|
|
// see if High matches, we will return High as the
|
|
// closest key regardless.
|
|
Result = CmpCompareInIndex(Hive, SearchName, High, Index, Child);
|
|
return High;
|
|
}
|
|
|
|
|
|
LONG
|
|
CmpCompareInIndex(
|
|
PHHIVE Hive,
|
|
PUNICODE_STRING SearchName,
|
|
ULONG Count,
|
|
PCM_KEY_INDEX Index,
|
|
PHCELL_INDEX Child
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Do a compare of a name in an index. This routine handles both fast leafs and slow ones.
|
|
Arguments:
|
|
Hive - pointer to hive control structure for hive of interest
|
|
SearchName - pointer to name of key we are searching for
|
|
Count - supplies index that we are searching at.
|
|
Index - Supplies pointer to either a CM_KEY_INDEX or
|
|
a CM_KEY_FAST_INDEX. This routine will determine which
|
|
type of index it is passed.
|
|
Child - pointer to variable to receive hcell_index of found key
|
|
HCELL_NIL if result != 0
|
|
Return Value:
|
|
0 = SearchName == KeyName (of Cell)
|
|
< 0 = SearchName < KeyName
|
|
> 0 = SearchName > KeyName
|
|
--*/
|
|
{
|
|
PCM_KEY_FAST_INDEX FastIndex;
|
|
LONG Result;
|
|
ULONG i;
|
|
WCHAR c1;
|
|
WCHAR c2;
|
|
ULONG HintLength;
|
|
ULONG ValidChars;
|
|
ULONG NameLength;
|
|
PCM_INDEX Hint;
|
|
|
|
*Child = HCELL_NIL;
|
|
if (Index->Signature == CM_KEY_FAST_LEAF) {
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Index;
|
|
Hint = &FastIndex->List[Count];
|
|
|
|
// Compute the number of valid characters in the hint to compare.
|
|
|
|
HintLength = 4;
|
|
for (i = 0; i < 4; i++) {
|
|
if (Hint->NameHint[i] == 0) {
|
|
HintLength = i;
|
|
break;
|
|
}
|
|
}
|
|
NameLength = SearchName->Length / sizeof(WCHAR);
|
|
if (NameLength < HintLength) {
|
|
ValidChars = NameLength;
|
|
}
|
|
else {
|
|
ValidChars = HintLength;
|
|
}
|
|
|
|
for (i = 0; i < ValidChars; i++) {
|
|
c1 = SearchName->Buffer[i];
|
|
c2 = FastIndex->List[Count].NameHint[i];
|
|
Result = (LONG)RtlUpcaseUnicodeChar(c1) - (LONG)RtlUpcaseUnicodeChar(c2);
|
|
if (Result != 0) {
|
|
// We have found a mismatched character in the hint,
|
|
// we can now tell which direction to go.
|
|
return(Result);
|
|
}
|
|
}
|
|
|
|
// We have compared all the available characters without a
|
|
// discrepancy. Go ahead and do the actual comparison now.
|
|
|
|
Result = CmpDoCompareKeyName(Hive, SearchName, FastIndex->List[Count].Cell);
|
|
if (Result == 0) {
|
|
*Child = Hint->Cell;
|
|
}
|
|
}
|
|
else {
|
|
// This is just a normal old slow index.
|
|
Result = CmpDoCompareKeyName(Hive, SearchName, Index->List[Count]);
|
|
if (Result == 0) {
|
|
*Child = Index->List[Count];
|
|
}
|
|
}
|
|
|
|
return(Result);
|
|
}
|
|
|
|
|
|
LONG
|
|
CmpDoCompareKeyName(
|
|
PHHIVE Hive,
|
|
PUNICODE_STRING SearchName,
|
|
HCELL_INDEX Cell
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Do a compare of a name with a key.
|
|
Arguments:
|
|
Hive - pointer to hive control structure for hive of interest
|
|
SearchName - pointer to name of key we are searching for
|
|
Cell - cell of key we are to compare with
|
|
Return Value:
|
|
0 = SearchName == KeyName (of Cell)
|
|
< 0 = SearchName < KeyName
|
|
> 0 = SearchName > KeyName
|
|
--*/
|
|
{
|
|
PCM_KEY_NODE Pcan;
|
|
UNICODE_STRING KeyName;
|
|
|
|
Pcan = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|
if (Pcan->Flags & KEY_COMP_NAME) {
|
|
return(CmpCompareCompressedName(SearchName, Pcan->Name, Pcan->NameLength));
|
|
}
|
|
else {
|
|
KeyName.Buffer = &(Pcan->Name[0]);
|
|
KeyName.Length = Pcan->NameLength;
|
|
KeyName.MaximumLength = KeyName.Length;
|
|
return (RtlCompareUnicodeString(SearchName, &KeyName, TRUE));
|
|
}
|
|
}
|
|
|
|
|
|
HCELL_INDEX
|
|
CmpFindSubKeyByNumber(
|
|
PHHIVE Hive,
|
|
PCM_KEY_NODE Node,
|
|
ULONG Number
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the Number'th entry in the index, starting from 0.
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
Node - pointer to key body which is parent of child of interest
|
|
|
|
Number - ordinal of child key to return
|
|
|
|
Return Value:
|
|
|
|
Cell of matching child key, or HCELL_NIL if none.
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_INDEX Index;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpFindSubKeyByNumber:\n\t"));
|
|
KdPrint(("Hive=%08lx Node=%08lx Number=%08lx\n", Hive, Node, Number));
|
|
}
|
|
|
|
if (Number < Node->SubKeyCounts[Stable]) {
|
|
|
|
|
|
// It's in the stable set
|
|
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Stable]);
|
|
return (CmpDoFindSubKeyByNumber(Hive, Index, Number));
|
|
|
|
}
|
|
else if (Hive->StorageTypeCount > Volatile) {
|
|
|
|
|
|
// It's in the volatile set
|
|
|
|
Number = Number - Node->SubKeyCounts[Stable];
|
|
if (Number < Node->SubKeyCounts[Volatile]) {
|
|
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Volatile]);
|
|
return (CmpDoFindSubKeyByNumber(Hive, Index, Number));
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// It's nowhere
|
|
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
|
|
HCELL_INDEX
|
|
CmpDoFindSubKeyByNumber(
|
|
PHHIVE Hive,
|
|
PCM_KEY_INDEX Index,
|
|
ULONG Number
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper for CmpFindSubKeyByNumber,
|
|
Find the Number'th entry in the index, starting from 0.
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
Index - root or leaf of the index
|
|
|
|
Number - ordinal of child key to return
|
|
|
|
Return Value:
|
|
|
|
Cell of requested entry.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
HCELL_INDEX LeafCell;
|
|
PCM_KEY_INDEX Leaf;
|
|
PCM_KEY_FAST_INDEX FastIndex;
|
|
|
|
if (Index->Signature == CM_KEY_INDEX_ROOT) {
|
|
|
|
// step through root, till we find the right leaf
|
|
|
|
for (i = 0; i < Index->Count; i++) {
|
|
LeafCell = Index->List[i];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (Number < Leaf->Count) {
|
|
if (Leaf->Signature == CM_KEY_FAST_LEAF) {
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
|
|
return (FastIndex->List[Number].Cell);
|
|
}
|
|
else {
|
|
return (Leaf->List[Number]);
|
|
}
|
|
}
|
|
else {
|
|
Number = Number - Leaf->Count;
|
|
}
|
|
}
|
|
ASSERT(FALSE);
|
|
}
|
|
ASSERT(Number < Index->Count);
|
|
if (Index->Signature == CM_KEY_FAST_LEAF) {
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Index;
|
|
return(FastIndex->List[Number].Cell);
|
|
}
|
|
else {
|
|
return (Index->List[Number]);
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CmpAddSubKey(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX Parent,
|
|
HCELL_INDEX Child
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a new child subkey to the subkey index for a cell. The
|
|
child MUST NOT already be present (bugcheck if so.)
|
|
|
|
NOTE: We expect Parent to already be marked dirty.
|
|
We will mark stuff in Index dirty
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
Parent - cell of key that will be parent of new key
|
|
|
|
Child - new key to put in Paren't sub key list
|
|
|
|
Return Value:
|
|
|
|
TRUE - it worked
|
|
|
|
FALSE - resource problem
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_NODE pcell;
|
|
HCELL_INDEX WorkCell;
|
|
PCM_KEY_INDEX Index;
|
|
PCM_KEY_FAST_INDEX FastIndex;
|
|
UNICODE_STRING NewName;
|
|
HCELL_INDEX LeafCell;
|
|
PHCELL_INDEX RootPointer = NULL;
|
|
ULONG cleanup = 0;
|
|
ULONG Type;
|
|
BOOLEAN IsCompressed;
|
|
ULONG i;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpAddSubKey:\n\t"));
|
|
KdPrint(("Hive=%08lx Parent=%08lx Child=%08lx\n", Hive, Parent, Child));
|
|
}
|
|
|
|
|
|
// build a name string
|
|
|
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, Child);
|
|
if (pcell->Flags & KEY_COMP_NAME) {
|
|
IsCompressed = TRUE;
|
|
NewName.Length = CmpCompressedNameSize(pcell->Name, pcell->NameLength);
|
|
NewName.MaximumLength = NewName.Length;
|
|
NewName.Buffer = (Hive->Allocate)(NewName.Length, FALSE);
|
|
if (NewName.Buffer == NULL) {
|
|
return(FALSE);
|
|
}
|
|
CmpCopyCompressedName(NewName.Buffer,
|
|
NewName.MaximumLength,
|
|
pcell->Name,
|
|
pcell->NameLength);
|
|
}
|
|
else {
|
|
IsCompressed = FALSE;
|
|
NewName.Length = pcell->NameLength;
|
|
NewName.MaximumLength = pcell->NameLength;
|
|
NewName.Buffer = &(pcell->Name[0]);
|
|
}
|
|
|
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, Parent);
|
|
|
|
Type = HvGetCellType(Child);
|
|
|
|
if (pcell->SubKeyCounts[Type] == 0) {
|
|
|
|
ULONG Signature;
|
|
|
|
|
|
// we must allocate a leaf
|
|
|
|
WorkCell = HvAllocateCell(Hive, sizeof(CM_KEY_FAST_INDEX), Type);
|
|
if (WorkCell == HCELL_NIL) {
|
|
goto ErrorExit;
|
|
}
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, WorkCell);
|
|
Index->Signature = UseFastIndex(Hive) ? CM_KEY_FAST_LEAF : CM_KEY_INDEX_LEAF;
|
|
Index->Count = 0;
|
|
pcell->SubKeyLists[Type] = WorkCell;
|
|
cleanup = 1;
|
|
}
|
|
else {
|
|
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, pcell->SubKeyLists[Type]);
|
|
if ((Index->Signature == CM_KEY_FAST_LEAF) &&
|
|
(Index->Count >= (CM_MAX_FAST_INDEX))) {
|
|
|
|
|
|
// We must change fast index to a slow index to accomodate
|
|
// growth.
|
|
|
|
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Index;
|
|
for (i = 0; i < Index->Count; i++) {
|
|
Index->List[i] = FastIndex->List[i].Cell;
|
|
}
|
|
Index->Signature = CM_KEY_INDEX_LEAF;
|
|
|
|
}
|
|
else if ((Index->Signature == CM_KEY_INDEX_LEAF) &&
|
|
(Index->Count >= (CM_MAX_INDEX - 1))) {
|
|
|
|
// We must change flat entry to a root/leaf tree
|
|
|
|
WorkCell = HvAllocateCell(
|
|
Hive,
|
|
sizeof(CM_KEY_INDEX) + sizeof(HCELL_INDEX), // allow for 2
|
|
Type
|
|
);
|
|
if (WorkCell == HCELL_NIL) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, WorkCell);
|
|
Index->Signature = CM_KEY_INDEX_ROOT;
|
|
Index->Count = 1;
|
|
Index->List[0] = pcell->SubKeyLists[Type];
|
|
pcell->SubKeyLists[Type] = WorkCell;
|
|
cleanup = 2;
|
|
|
|
}
|
|
}
|
|
LeafCell = pcell->SubKeyLists[Type];
|
|
|
|
|
|
// LeafCell is target for add, or perhaps root
|
|
// Index is pointer to fast leaf, slow Leaf or Root, whichever applies
|
|
|
|
if (Index->Signature == CM_KEY_INDEX_ROOT) {
|
|
LeafCell = CmpSelectLeaf(Hive, pcell, &NewName, Type, &RootPointer);
|
|
if (LeafCell == HCELL_NIL) {
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
|
|
// Add new cell to Leaf, update pointers
|
|
|
|
LeafCell = CmpAddToLeaf(Hive, LeafCell, Child, &NewName);
|
|
|
|
if (LeafCell == HCELL_NIL) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pcell->SubKeyCounts[Type] += 1;
|
|
|
|
if (RootPointer != NULL) {
|
|
*RootPointer = LeafCell;
|
|
}
|
|
else {
|
|
pcell->SubKeyLists[Type] = LeafCell;
|
|
}
|
|
|
|
if (IsCompressed) {
|
|
(Hive->Free)(NewName.Buffer, NewName.Length);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
ErrorExit:
|
|
if (IsCompressed) {
|
|
(Hive->Free)(NewName.Buffer, NewName.Length);
|
|
}
|
|
|
|
switch (cleanup) {
|
|
case 1:
|
|
HvFreeCell(Hive, pcell->SubKeyLists[Type]);
|
|
pcell->SubKeyLists[Type] = HCELL_NIL;
|
|
break;
|
|
|
|
case 2:
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, pcell->SubKeyLists[Type]);
|
|
pcell->SubKeyLists[Type] = Index->List[0];
|
|
HvFreeCell(Hive, pcell->SubKeyLists[Type]);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
HCELL_INDEX
|
|
CmpAddToLeaf(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX LeafCell,
|
|
HCELL_INDEX NewKey,
|
|
PUNICODE_STRING NewName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Insert a new subkey into a Leaf index. Supports both fast and slow
|
|
leaf indexes and will determine which sort of index the given leaf is.
|
|
|
|
NOTE: We expect Root to already be marked dirty by caller if non NULL.
|
|
We expect Leaf to always be marked dirty by caller.
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
LeafCell - cell of index leaf node we are to add entry too
|
|
|
|
NewKey - cell of KEY_NODE we are to add
|
|
|
|
NewName - pointer to unicode string with name to we are to add
|
|
|
|
Return Value:
|
|
|
|
HCELL_NIL - some resource problem
|
|
|
|
Else - cell of Leaf index when are done, caller is expected to
|
|
set this into Root index or Key body.
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_INDEX Leaf;
|
|
PCM_KEY_FAST_INDEX FastLeaf;
|
|
ULONG Size;
|
|
ULONG OldSize;
|
|
ULONG freecount;
|
|
HCELL_INDEX NewCell;
|
|
HCELL_INDEX Child;
|
|
ULONG Select;
|
|
LONG Result;
|
|
ULONG EntrySize;
|
|
ULONG i;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpAddToLeaf:\n\t"));
|
|
KdPrint(("Hive=%08lx LeafCell=%08lx NewKey=%08lx\n", Hive, LeafCell, NewKey));
|
|
}
|
|
|
|
if (!HvMarkCellDirty(Hive, LeafCell)) {
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
|
|
// compute number free slots left in the leaf
|
|
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (Leaf->Signature == CM_KEY_INDEX_LEAF) {
|
|
FastLeaf = NULL;
|
|
EntrySize = sizeof(HCELL_INDEX);
|
|
}
|
|
else {
|
|
ASSERT(Leaf->Signature == CM_KEY_FAST_LEAF);
|
|
FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
|
|
EntrySize = sizeof(CM_INDEX);
|
|
}
|
|
OldSize = HvGetCellSize(Hive, Leaf);
|
|
Size = OldSize - ((EntrySize * Leaf->Count) +
|
|
FIELD_OFFSET(CM_KEY_INDEX, List));
|
|
freecount = Size / EntrySize;
|
|
|
|
|
|
// grow the leaf if it isn't big enough
|
|
|
|
NewCell = LeafCell;
|
|
if (freecount < 1) {
|
|
Size = OldSize + OldSize / 2;
|
|
if (Size < (OldSize + EntrySize)) {
|
|
Size = OldSize + EntrySize;
|
|
}
|
|
NewCell = HvReallocateCell(Hive, LeafCell, Size);
|
|
if (NewCell == HCELL_NIL) {
|
|
return HCELL_NIL;
|
|
}
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, NewCell);
|
|
if (FastLeaf != NULL) {
|
|
FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
|
|
}
|
|
}
|
|
|
|
|
|
// Find where to put the new entry
|
|
|
|
Select = CmpFindSubKeyInLeaf(Hive, Leaf, (ULONG)-1, NewName, &Child);
|
|
ASSERT(Child == HCELL_NIL);
|
|
|
|
|
|
// Select is the index in List of the entry nearest where the
|
|
// new entry should go.
|
|
// Decide wether the new entry goes before or after Offset entry,
|
|
// and then ripple copy and set.
|
|
// If Select == Count, then the leaf is empty, so simply set our entry
|
|
|
|
if (Select != Leaf->Count) {
|
|
|
|
Result = CmpCompareInIndex(Hive,
|
|
NewName,
|
|
Select,
|
|
Leaf,
|
|
&Child);
|
|
ASSERT(Result != 0);
|
|
|
|
|
|
// Result < 0 - NewName/NewKey less than selected key, insert before
|
|
// > 0 - NewName/NewKey greater than selected key, insert after
|
|
|
|
if (Result > 0) {
|
|
Select++;
|
|
}
|
|
|
|
if (Select != Leaf->Count) {
|
|
|
|
|
|
// ripple copy to make space and insert
|
|
|
|
|
|
if (FastLeaf != NULL) {
|
|
RtlMoveMemory((PVOID)&(FastLeaf->List[Select + 1]),
|
|
(PVOID)&(FastLeaf->List[Select]),
|
|
sizeof(CM_INDEX)*(FastLeaf->Count - Select));
|
|
}
|
|
else {
|
|
RtlMoveMemory((PVOID)&(Leaf->List[Select + 1]),
|
|
(PVOID)&(Leaf->List[Select]),
|
|
sizeof(HCELL_INDEX)*(Leaf->Count - Select));
|
|
}
|
|
}
|
|
}
|
|
if (FastLeaf != NULL) {
|
|
FastLeaf->List[Select].Cell = NewKey;
|
|
FastLeaf->List[Select].NameHint[0] = 0;
|
|
FastLeaf->List[Select].NameHint[1] = 0;
|
|
FastLeaf->List[Select].NameHint[2] = 0;
|
|
FastLeaf->List[Select].NameHint[3] = 0;
|
|
if (NewName->Length / sizeof(WCHAR) < 4) {
|
|
i = NewName->Length / sizeof(WCHAR);
|
|
}
|
|
else {
|
|
i = 4;
|
|
}
|
|
do {
|
|
if ((USHORT)NewName->Buffer[i - 1] > (UCHAR)-1) {
|
|
|
|
// Can't compress this name. Leave NameHint[0]==0
|
|
// to force the name to be looked up in the key.
|
|
|
|
break;
|
|
}
|
|
FastLeaf->List[Select].NameHint[i - 1] = (UCHAR)NewName->Buffer[i - 1];
|
|
i--;
|
|
} while (i > 0);
|
|
}
|
|
else {
|
|
Leaf->List[Select] = NewKey;
|
|
}
|
|
Leaf->Count += 1;
|
|
return NewCell;
|
|
}
|
|
|
|
|
|
HCELL_INDEX
|
|
CmpSelectLeaf(
|
|
PHHIVE Hive,
|
|
PCM_KEY_NODE ParentKey,
|
|
PUNICODE_STRING NewName,
|
|
HSTORAGE_TYPE Type,
|
|
PHCELL_INDEX *RootPointer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is only called if the subkey index for a cell is NOT
|
|
simply a single Leaf index block.
|
|
|
|
It selects the Leaf index block to which a new entry is to be
|
|
added. It may create this block by splitting an existing Leaf
|
|
block.
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
ParentKey - mapped pointer to parent key
|
|
|
|
NewName - pointer to unicode string naming entry to add
|
|
|
|
Type - Stable or Volatile, describes Child's storage
|
|
|
|
RootPointer - pointer to variable to receive address of HCELL_INDEX
|
|
that points to Leaf block returned as function argument.
|
|
Used for updates.
|
|
|
|
Return Value:
|
|
|
|
HCELL_NIL - resource problem
|
|
|
|
Else, cell index of Leaf index block to add entry to
|
|
|
|
--*/
|
|
{
|
|
HCELL_INDEX LeafCell;
|
|
HCELL_INDEX WorkCell;
|
|
PCM_KEY_INDEX Index;
|
|
PCM_KEY_INDEX Leaf;
|
|
ULONG RootSelect;
|
|
LONG Result;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpSelectLeaf:\n\t"));
|
|
KdPrint(("Hive=%08lx ParentKey=%08lx\n", Hive, ParentKey));
|
|
}
|
|
|
|
|
|
|
|
// Force root to always be dirty, since we'll either grow it or edit it,
|
|
// and it needs to be marked dirty for BOTH cases. (Edit may not
|
|
// occur until after we leave
|
|
|
|
if (!HvMarkCellDirty(Hive, ParentKey->SubKeyLists[Type])) {
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
|
|
// must find the proper leaf
|
|
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, ParentKey->SubKeyLists[Type]);
|
|
ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);
|
|
|
|
while (TRUE) {
|
|
|
|
RootSelect = CmpFindSubKeyInRoot(Hive, Index, NewName, &LeafCell);
|
|
|
|
if (LeafCell == HCELL_NIL) {
|
|
|
|
|
|
// Leaf of interest is somewhere near RootSelect
|
|
|
|
// . Always use lowest order leaf we can get away with
|
|
// . Never split a leaf if there's one with space we can use
|
|
// . When we split a leaf, we have to repeat search
|
|
|
|
// If (NewKey is below lowest key in selected)
|
|
// If there's a Leaf below selected with space
|
|
// use the leaf below
|
|
// else
|
|
// use the leaf (split it if not enough space)
|
|
// Else
|
|
// must be above highest key in selected, less than
|
|
// lowest key in Leaf to right of selected
|
|
// if space in selected
|
|
// use selected
|
|
// else if space in leaf above selected
|
|
// use leaf above
|
|
// else
|
|
// split selected
|
|
|
|
LeafCell = Index->List[RootSelect];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
WorkCell = Leaf->List[0];
|
|
Result = CmpDoCompareKeyName(Hive, NewName, WorkCell);
|
|
ASSERT(Result != 0);
|
|
|
|
if (Result < 0) {
|
|
|
|
|
|
// new is off the left end of Selected
|
|
|
|
if (RootSelect > 0) {
|
|
|
|
|
|
// there's a Leaf to the left, try to use it
|
|
|
|
LeafCell = Index->List[RootSelect - 1];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
if (Leaf->Count < (CM_MAX_INDEX - 1)) {
|
|
RootSelect--;
|
|
*RootPointer = &(Index->List[RootSelect]);
|
|
break;
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
// new key is off the left end of the leftmost leaf.
|
|
// Use the leftmost leaf, if there's enough room
|
|
|
|
LeafCell = Index->List[0];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (Leaf->Count < (CM_MAX_INDEX - 1)) {
|
|
*RootPointer = &(Index->List[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// else fall to split case
|
|
|
|
|
|
}
|
|
else {
|
|
|
|
|
|
// since new key is not in a Leaf, and is not off
|
|
// the left end of the ResultSelect Leaf, it must
|
|
// be off the right end.
|
|
|
|
LeafCell = Index->List[RootSelect];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
if (Leaf->Count < (CM_MAX_INDEX - 1)) {
|
|
*RootPointer = &(Index->List[RootSelect]);
|
|
break;
|
|
}
|
|
|
|
|
|
// No space, see if there's a leaf to the rigth
|
|
// and if it has space
|
|
|
|
if (RootSelect < (ULONG)(Index->Count - 1)) {
|
|
|
|
LeafCell = Index->List[RootSelect + 1];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
if (Leaf->Count < (CM_MAX_INDEX - 1)) {
|
|
*RootPointer = &(Index->List[RootSelect + 1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// fall to split case
|
|
|
|
}
|
|
|
|
}
|
|
else { // LeafCell != HCELL_NIL
|
|
|
|
|
|
// Since newkey cannot already be in tree, it must be
|
|
// greater than the bottom of Leaf and less than the top,
|
|
// therefore it must go in Leaf. If no space, split it.
|
|
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (Leaf->Count < (CM_MAX_INDEX - 1)) {
|
|
|
|
*RootPointer = &(Index->List[RootSelect]);
|
|
break;
|
|
}
|
|
|
|
|
|
// fall to split case
|
|
|
|
}
|
|
|
|
|
|
// either no neigbor, or no space in neighbor, so split
|
|
|
|
WorkCell = CmpSplitLeaf(
|
|
Hive,
|
|
ParentKey->SubKeyLists[Type], // root cell
|
|
RootSelect,
|
|
Type
|
|
);
|
|
if (WorkCell == HCELL_NIL) {
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
ParentKey->SubKeyLists[Type] = WorkCell;
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, WorkCell);
|
|
ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);
|
|
|
|
} // while(true)
|
|
return LeafCell;
|
|
}
|
|
|
|
|
|
HCELL_INDEX
|
|
CmpSplitLeaf(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX RootCell,
|
|
ULONG RootSelect,
|
|
HSTORAGE_TYPE Type
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Split the Leaf index block specified by RootSelect, causing both
|
|
of the split out Leaf blocks to appear in the Root index block
|
|
specified by RootCell.
|
|
|
|
Caller is expected to have marked old root cell dirty.
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
RootCell - cell of the Root index block of index being grown
|
|
|
|
RootSelect - indicates which child of Root to split
|
|
|
|
Type - Stable or Volatile
|
|
|
|
Return Value:
|
|
|
|
HCELL_NIL - some resource problem
|
|
|
|
Else - cell of new (e.g. reallocated) Root index block
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_INDEX Root;
|
|
HCELL_INDEX LeafCell;
|
|
PCM_KEY_INDEX Leaf;
|
|
HCELL_INDEX NewLeafCell;
|
|
PCM_KEY_INDEX NewLeaf;
|
|
ULONG Size;
|
|
ULONG freecount;
|
|
USHORT OldCount;
|
|
USHORT KeepCount;
|
|
USHORT NewCount;
|
|
|
|
CMLOG(CML_MAJOR, CMS_INDEX) {
|
|
KdPrint(("CmpSplitLeaf:\n\t"));
|
|
KdPrint(("Hive=%08lx RootCell=%08lx RootSelect\n", Hive, RootCell, RootSelect));
|
|
}
|
|
|
|
|
|
// allocate new Leaf index block
|
|
|
|
Root = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);
|
|
LeafCell = Root->List[RootSelect];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
OldCount = Leaf->Count;
|
|
|
|
KeepCount = (USHORT)(OldCount / 2); // # of entries to keep in org. Leaf
|
|
NewCount = (OldCount - KeepCount); // # of entries to move
|
|
|
|
Size = (sizeof(HCELL_INDEX) * NewCount) +
|
|
FIELD_OFFSET(CM_KEY_INDEX, List) + 1; // +1 to assure room for add
|
|
|
|
if (!HvMarkCellDirty(Hive, LeafCell)) {
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
NewLeafCell = HvAllocateCell(Hive, Size, Type);
|
|
if (NewLeafCell == HCELL_NIL) {
|
|
return HCELL_NIL;
|
|
}
|
|
NewLeaf = (PCM_KEY_INDEX)HvGetCell(Hive, NewLeafCell);
|
|
NewLeaf->Signature = CM_KEY_INDEX_LEAF;
|
|
|
|
|
|
|
|
// compute number of free slots left in the root
|
|
|
|
Size = HvGetCellSize(Hive, Root);
|
|
Size = Size - ((sizeof(HCELL_INDEX) * Root->Count) +
|
|
FIELD_OFFSET(CM_KEY_INDEX, List));
|
|
freecount = Size / sizeof(HCELL_INDEX);
|
|
|
|
|
|
|
|
// grow the root if it isn't big enough
|
|
|
|
if (freecount < 1) {
|
|
Size = HvGetCellSize(Hive, Root) + sizeof(HCELL_INDEX);
|
|
RootCell = HvReallocateCell(Hive, RootCell, Size);
|
|
if (RootCell == HCELL_NIL) {
|
|
HvFreeCell(Hive, NewLeafCell);
|
|
return HCELL_NIL;
|
|
}
|
|
Root = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);
|
|
}
|
|
|
|
|
|
|
|
// copy data from one Leaf to the other
|
|
|
|
RtlMoveMemory(
|
|
(PVOID)&(NewLeaf->List[0]),
|
|
(PVOID)&(Leaf->List[KeepCount]),
|
|
sizeof(HCELL_INDEX) * NewCount
|
|
);
|
|
|
|
ASSERT(KeepCount != 0);
|
|
ASSERT(NewCount != 0);
|
|
|
|
Leaf->Count = KeepCount;
|
|
NewLeaf->Count = NewCount;
|
|
|
|
|
|
|
|
// make an open slot in the root
|
|
|
|
if (RootSelect < (ULONG)(Root->Count - 1)) {
|
|
RtlMoveMemory(
|
|
(PVOID)&(Root->List[RootSelect + 2]),
|
|
(PVOID)&(Root->List[RootSelect + 1]),
|
|
(Root->Count - (RootSelect + 1)) * sizeof(HCELL_INDEX)
|
|
);
|
|
}
|
|
|
|
|
|
// update the root
|
|
|
|
Root->Count += 1;
|
|
Root->List[RootSelect + 1] = NewLeafCell;
|
|
return RootCell;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CmpMarkIndexDirty(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX ParentKey,
|
|
HCELL_INDEX TargetKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Mark as dirty relevent cells of a subkey index. The Leaf that
|
|
points to TargetKey, and the Root index block, if applicable,
|
|
will be marked dirty. This call assumes we are setting up
|
|
for a subkey delete.
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
ParentKey - key from whose subkey list delete is to be performed
|
|
|
|
TargetKey - key being deleted
|
|
|
|
Return Value:
|
|
|
|
TRUE - it worked, FALSE - it didn't, some resource problem
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_NODE pcell;
|
|
ULONG i;
|
|
HCELL_INDEX IndexCell;
|
|
PCM_KEY_INDEX Index;
|
|
HCELL_INDEX Child = HCELL_NIL;
|
|
UNICODE_STRING SearchName;
|
|
BOOLEAN IsCompressed;
|
|
|
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
|
|
if (pcell->Flags & KEY_COMP_NAME) {
|
|
IsCompressed = TRUE;
|
|
SearchName.Length = CmpCompressedNameSize(pcell->Name, pcell->NameLength);
|
|
SearchName.MaximumLength = SearchName.Length;
|
|
SearchName.Buffer = (Hive->Allocate)(SearchName.Length, FALSE);
|
|
if (SearchName.Buffer == NULL) {
|
|
return(FALSE);
|
|
}
|
|
CmpCopyCompressedName(SearchName.Buffer,
|
|
SearchName.MaximumLength,
|
|
pcell->Name,
|
|
pcell->NameLength);
|
|
}
|
|
else {
|
|
IsCompressed = FALSE;
|
|
SearchName.Length = pcell->NameLength;
|
|
SearchName.MaximumLength = pcell->NameLength;
|
|
SearchName.Buffer = &(pcell->Name[0]);
|
|
}
|
|
|
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
|
|
|
|
for (i = 0; i < Hive->StorageTypeCount; i++) {
|
|
if (pcell->SubKeyCounts[i] != 0) {
|
|
ASSERT(HvIsCellAllocated(Hive, pcell->SubKeyLists[i]));
|
|
IndexCell = pcell->SubKeyLists[i];
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
|
|
|
|
if (Index->Signature == CM_KEY_INDEX_ROOT) {
|
|
|
|
|
|
// target even in index?
|
|
|
|
CmpFindSubKeyInRoot(Hive, Index, &SearchName, &Child);
|
|
if (Child == HCELL_NIL) {
|
|
continue;
|
|
}
|
|
|
|
|
|
// mark root dirty
|
|
|
|
if (!HvMarkCellDirty(Hive, IndexCell)) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
IndexCell = Child;
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, Child);
|
|
}
|
|
ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Index->Signature == CM_KEY_FAST_LEAF));
|
|
|
|
CmpFindSubKeyInLeaf(Hive, Index, pcell->WorkVar, &SearchName, &Child);
|
|
if (Child != HCELL_NIL) {
|
|
if (IsCompressed) {
|
|
(Hive->Free)(SearchName.Buffer, SearchName.Length);
|
|
}
|
|
return(HvMarkCellDirty(Hive, IndexCell));
|
|
}
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
if (IsCompressed) {
|
|
(Hive->Free)(SearchName.Buffer, SearchName.Length);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CmpRemoveSubKey(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX ParentKey,
|
|
HCELL_INDEX TargetKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove the subkey TargetKey refers to from ParentKey's list.
|
|
|
|
NOTE: Assumes that caller has marked relevent cells dirty,
|
|
see CmpMarkIndexDirty.
|
|
|
|
Arguments:
|
|
|
|
Hive - pointer to hive control structure for hive of interest
|
|
|
|
ParentKey - key from whose subkey list delete is to be performed
|
|
|
|
TargetKey - key being deleted
|
|
|
|
Return Value:
|
|
|
|
TRUE - it worked, FALSE - it didn't, some resource problem
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_NODE pcell;
|
|
HCELL_INDEX LeafCell;
|
|
PCM_KEY_INDEX Leaf;
|
|
PCM_KEY_FAST_INDEX FastIndex;
|
|
HCELL_INDEX RootCell = HCELL_NIL;
|
|
PCM_KEY_INDEX Root = NULL;
|
|
HCELL_INDEX Child;
|
|
ULONG Type;
|
|
ULONG RootSelect;
|
|
ULONG LeafSelect;
|
|
UNICODE_STRING SearchName;
|
|
BOOLEAN IsCompressed;
|
|
WCHAR CompressedBuffer[50];
|
|
|
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
|
|
if (pcell->Flags & KEY_COMP_NAME) {
|
|
IsCompressed = TRUE;
|
|
SearchName.Length = CmpCompressedNameSize(pcell->Name, pcell->NameLength);
|
|
SearchName.MaximumLength = SearchName.Length;
|
|
if (SearchName.MaximumLength > sizeof(CompressedBuffer)) {
|
|
SearchName.Buffer = (Hive->Allocate)(SearchName.Length, FALSE);
|
|
if (SearchName.Buffer == NULL) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else {
|
|
SearchName.Buffer = CompressedBuffer;
|
|
}
|
|
CmpCopyCompressedName(SearchName.Buffer,
|
|
SearchName.MaximumLength,
|
|
pcell->Name,
|
|
pcell->NameLength);
|
|
}
|
|
else {
|
|
IsCompressed = FALSE;
|
|
SearchName.Length = pcell->NameLength;
|
|
SearchName.MaximumLength = pcell->NameLength;
|
|
SearchName.Buffer = &(pcell->Name[0]);
|
|
}
|
|
|
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
|
|
Type = HvGetCellType(TargetKey);
|
|
|
|
ASSERT(pcell->SubKeyCounts[Type] != 0);
|
|
ASSERT(HvIsCellAllocated(Hive, pcell->SubKeyLists[Type]));
|
|
|
|
LeafCell = pcell->SubKeyLists[Type];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
if (Leaf->Signature == CM_KEY_INDEX_ROOT) {
|
|
RootSelect = CmpFindSubKeyInRoot(Hive, Leaf, &SearchName, &Child);
|
|
|
|
ASSERT(Child != FALSE);
|
|
|
|
Root = Leaf;
|
|
RootCell = LeafCell;
|
|
LeafCell = Child;
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
}
|
|
|
|
ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_FAST_LEAF));
|
|
|
|
LeafSelect = CmpFindSubKeyInLeaf(Hive, Leaf, pcell->WorkVar, &SearchName, &Child);
|
|
|
|
ASSERT(Child != HCELL_NIL);
|
|
|
|
|
|
// Leaf points to Index Leaf block
|
|
// Child is Index Leaf block cell
|
|
// LeafSelect is Index for List[]
|
|
|
|
pcell->SubKeyCounts[Type] -= 1;
|
|
|
|
Leaf->Count -= 1;
|
|
if (Leaf->Count == 0) {
|
|
|
|
|
|
// Empty Leaf, drop it.
|
|
|
|
HvFreeCell(Hive, LeafCell);
|
|
|
|
if (Root != NULL) {
|
|
|
|
Root->Count -= 1;
|
|
if (Root->Count == 0) {
|
|
|
|
|
|
// Root is empty, free it too.
|
|
|
|
HvFreeCell(Hive, RootCell);
|
|
pcell->SubKeyLists[Type] = HCELL_NIL;
|
|
|
|
}
|
|
else if (RootSelect < (ULONG)(Root->Count)) {
|
|
|
|
|
|
// Middle entry, squeeze root
|
|
|
|
RtlMoveMemory(
|
|
(PVOID)&(Root->List[RootSelect]),
|
|
(PVOID)&(Root->List[RootSelect + 1]),
|
|
(Root->Count - RootSelect) * sizeof(HCELL_INDEX)
|
|
);
|
|
}
|
|
|
|
// Else RootSelect == last entry, so decrementing count
|
|
// was all we needed to do
|
|
|
|
|
|
}
|
|
else {
|
|
|
|
pcell->SubKeyLists[Type] = HCELL_NIL;
|
|
|
|
}
|
|
|
|
}
|
|
else if (LeafSelect < (ULONG)(Leaf->Count)) {
|
|
|
|
if (Leaf->Signature == CM_KEY_INDEX_LEAF) {
|
|
RtlMoveMemory((PVOID)&(Leaf->List[LeafSelect]),
|
|
(PVOID)&(Leaf->List[LeafSelect + 1]),
|
|
(Leaf->Count - LeafSelect) * sizeof(HCELL_INDEX));
|
|
}
|
|
else {
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
|
|
RtlMoveMemory((PVOID)&(FastIndex->List[LeafSelect]),
|
|
(PVOID)&(FastIndex->List[LeafSelect + 1]),
|
|
(FastIndex->Count - LeafSelect) * sizeof(CM_INDEX));
|
|
}
|
|
}
|
|
|
|
// Else LeafSelect == last entry, so decrementing count was enough
|
|
|
|
|
|
if ((IsCompressed) &&
|
|
(SearchName.MaximumLength > sizeof(CompressedBuffer))) {
|
|
(Hive->Free)(SearchName.Buffer, SearchName.Length);
|
|
}
|
|
return TRUE;
|
|
}
|