windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/common/memdb/bintree.c
2020-09-26 16:20:57 +08:00

3706 lines
86 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
bintree.c
Abstract:
Routines that manage the memdb binary tree structures.
Author:
Jim Schmidt (jimschm) 8-Aug-1996
Revision History:
jimschm 30-Dec-1998 Hacked in AVL balancing
jimschm 23-Sep-1998 Proxy nodes, so MemDbMoveTree can replace end nodes too
jimschm 29-May-1998 Ability to replace center nodes in key strings
jimschm 21-Oct-1997 Split from memdb.c
--*/
#include "pch.h"
#include "memdbp.h"
#ifndef UNICODE
#error UNICODE required
#endif
#define MAX_MEMDB_SIZE 0x08000000 //128 MB
#define KSF_FLAGS_TO_COPY (KSF_USERFLAG_MASK|KSF_ENDPOINT|KSF_BINARY|KSF_PROXY_NODE)
DWORD
pNewKey (
IN PCWSTR KeyStr,
IN PCWSTR KeyStrWithHive,
IN BOOL Endpoint
);
DWORD
pAllocKeyToken (
IN PCWSTR KeyName,
OUT PINT AdjustFactor
);
VOID
pDeallocToken (
IN DWORD Token
);
DWORD
pFindPatternKeyWorker (
IN PCWSTR SubKey,
IN PCWSTR End,
IN DWORD RootOffset,
IN BOOL EndPatternAllowed
);
#ifdef DEBUG
VOID
pDumpTree (
IN DWORD Root,
IN PCSTR Title OPTIONAL
);
VOID
pCheckBalanceFactors (
IN DWORD Root
);
DWORD g_SingleRotations = 0;
DWORD g_DoubleRotations = 0;
DWORD g_Deletions = 0;
DWORD g_Insertions = 0;
#define INCSTAT(x) (x++)
#else
#define pDumpTree(arg1,arg2)
#define INCSTAT(x)
#endif
#define ANTIDIRECTION(x) ((x)^KSF_BALANCE_MASK)
#define FLAGS_TO_INT(x) ((INT) ((x)==KSF_LEFT_HEAVY ? -1 : (x)==KSF_RIGHT_HEAVY ? 1 : 0))
#define INT_TO_FLAGS(x) ((DWORD) ((x)==-1 ? KSF_LEFT_HEAVY : (x)==1 ? KSF_RIGHT_HEAVY : 0))
//
// Implementation
//
DWORD
pRotateOnce (
OUT PDWORD RootPtr,
IN DWORD ParentOffset,
IN DWORD PivotOffset,
IN DWORD Direction
)
{
PKEYSTRUCT GrandParent;
PKEYSTRUCT Parent;
PKEYSTRUCT Pivot;
PKEYSTRUCT TempKey;
DWORD Temp;
INT OldRootBalance;
INT NewRootBalance;
DWORD OldDir, NewDir;
INCSTAT(g_SingleRotations);
MYASSERT (ParentOffset != INVALID_OFFSET);
MYASSERT (PivotOffset != INVALID_OFFSET);
Parent = GetKeyStruct (ParentOffset);
Pivot = GetKeyStruct (PivotOffset);
if (Direction == KSF_LEFT_HEAVY) {
//
// Perform LL rotation
//
Temp = Pivot->Right;
Pivot->Right = ParentOffset;
Pivot->Parent = Parent->Parent;
Parent->Parent = PivotOffset;
Parent->Left = Temp;
} else {
//
// Preform RR rotation
//
Temp = Pivot->Left;
Pivot->Left = ParentOffset;
Pivot->Parent = Parent->Parent;
Parent->Parent = PivotOffset;
Parent->Right = Temp;
}
if (Temp != INVALID_OFFSET) {
TempKey = GetKeyStruct (Temp);
TempKey->Parent = ParentOffset;
}
OldDir = Parent->Flags & KSF_BALANCE_MASK;
NewDir = Pivot->Flags & KSF_BALANCE_MASK;
OldRootBalance = FLAGS_TO_INT (OldDir);
NewRootBalance = FLAGS_TO_INT (NewDir);
if (Direction == KSF_LEFT_HEAVY) {
OldRootBalance = -(++NewRootBalance);
} else {
OldRootBalance = -(--NewRootBalance);
}
Pivot->Flags = (Pivot->Flags & (~KSF_BALANCE_MASK)) | INT_TO_FLAGS(NewRootBalance);
Parent->Flags = (Parent->Flags & (~KSF_BALANCE_MASK)) | INT_TO_FLAGS(OldRootBalance);
//
// Fix grandparent/root to parent linkage
//
if (Pivot->Parent != INVALID_OFFSET) {
GrandParent = GetKeyStruct (Pivot->Parent);
if (GrandParent->Left == ParentOffset) {
GrandParent->Left = PivotOffset;
} else {
GrandParent->Right = PivotOffset;
}
} else {
*RootPtr = PivotOffset;
}
return PivotOffset;
}
DWORD
pRotateTwice (
OUT PDWORD RootPtr,
IN DWORD ParentOffset,
IN DWORD PivotOffset,
IN DWORD Direction
)
{
PKEYSTRUCT GrandParent;
PKEYSTRUCT Parent;
PKEYSTRUCT Pivot;
PKEYSTRUCT Child;
DWORD ChildOffset;
PKEYSTRUCT GrandChildLeft;
PKEYSTRUCT GrandChildRight;
DWORD AntiDirection;
DWORD Flag;
INT ParentDir;
INT PivotDir;
INT ChildDir;
INCSTAT(g_DoubleRotations);
//
// Initialize pointers
//
MYASSERT (ParentOffset != INVALID_OFFSET);
MYASSERT (PivotOffset != INVALID_OFFSET);
Parent = GetKeyStruct (ParentOffset);
Pivot = GetKeyStruct (PivotOffset);
if (Direction == KSF_LEFT_HEAVY) {
AntiDirection = KSF_RIGHT_HEAVY;
ChildOffset = Pivot->Right;
} else {
AntiDirection = KSF_LEFT_HEAVY;
ChildOffset = Pivot->Left;
}
MYASSERT (ChildOffset != INVALID_OFFSET);
Child = GetKeyStruct (ChildOffset);
if (Child->Left != INVALID_OFFSET) {
GrandChildLeft = GetKeyStruct (Child->Left);
} else {
GrandChildLeft = NULL;
}
if (Child->Right != INVALID_OFFSET) {
GrandChildRight = GetKeyStruct (Child->Right);
} else {
GrandChildRight = NULL;
}
//
// Perform the rotation
//
if (Direction == KSF_LEFT_HEAVY) {
//
// Perform LR rotation
//
Child->Parent = Parent->Parent;
Parent->Left = Child->Right;
if (GrandChildRight) {
GrandChildRight->Parent = ParentOffset;
}
Pivot->Right = Child->Left;
if (GrandChildLeft) {
GrandChildLeft->Parent = PivotOffset;
}
Child->Left = PivotOffset;
Pivot->Parent = ChildOffset;
Child->Right = ParentOffset;
Parent->Parent = ChildOffset;
} else {
//
// Preform RL rotation
//
Child->Parent = Parent->Parent;
Parent->Right = Child->Left;
if (GrandChildLeft) {
GrandChildLeft->Parent = ParentOffset;
}
Pivot->Left = Child->Right;
if (GrandChildRight) {
GrandChildRight->Parent = PivotOffset;
}
Child->Right = PivotOffset;
Pivot->Parent = ChildOffset;
Child->Left = ParentOffset;
Parent->Parent = ChildOffset;
}
//
// Fix balance factors
//
Flag = Child->Flags & KSF_BALANCE_MASK;
ChildDir = FLAGS_TO_INT (Flag);
if (Direction == KSF_RIGHT_HEAVY) {
ParentDir = -max (ChildDir, 0);
PivotDir = -min (ChildDir, 0);
} else {
ParentDir = -min (ChildDir, 0);
PivotDir = -max (ChildDir, 0);
}
Parent->Flags = Parent->Flags & (~KSF_BALANCE_MASK) | INT_TO_FLAGS(ParentDir);
Pivot->Flags = Pivot->Flags & (~KSF_BALANCE_MASK) | INT_TO_FLAGS(PivotDir);
Child->Flags = Child->Flags & (~KSF_BALANCE_MASK);
//
// Fix grandparent/root to parent linkage
//
if (Child->Parent != INVALID_OFFSET) {
GrandParent = GetKeyStruct (Child->Parent);
if (GrandParent->Left == ParentOffset) {
GrandParent->Left = ChildOffset;
} else {
GrandParent->Right = ChildOffset;
}
} else {
*RootPtr = ChildOffset;
}
return ChildOffset;
}
VOID
pBalanceInsertion (
OUT PDWORD RootPtr,
IN DWORD ChangedNode,
IN DWORD PivotEnd
)
{
DWORD PrevPivot;
DWORD PivotNode;
PKEYSTRUCT KeyStruct;
PKEYSTRUCT KeyParent;
DWORD BalanceFlags;
PivotNode = ChangedNode;
MYASSERT (PivotNode != INVALID_OFFSET);
//
// Initialize previous pivot to be the changed node,
// and begin balancing at its parent
//
PrevPivot = PivotNode;
KeyStruct = GetKeyStruct (PivotNode);
PivotNode = KeyStruct->Parent;
//
// Balance the tree starting at the changed node and going
// up until PivotEnd is reached. PivotEnd is the offset to
// the deepest node with a balance of non-zero.
//
MYASSERT (PivotNode != INVALID_OFFSET || PivotNode == PivotEnd);
while (PivotNode != INVALID_OFFSET) {
KeyParent = GetKeyStruct (PivotNode);
BalanceFlags = KeyParent->Flags & KSF_BALANCE_MASK;
if (BalanceFlags == KSF_LEFT_HEAVY) {
if (KeyParent->Left == PrevPivot) {
MYASSERT (KeyStruct == GetKeyStruct (PrevPivot));
if (KeyStruct->Flags & KSF_LEFT_HEAVY) {
//
// LL rotation
//
pRotateOnce (RootPtr, PivotNode, PrevPivot, KSF_LEFT_HEAVY);
} else if (KeyStruct->Flags & KSF_RIGHT_HEAVY) {
//
// LR rotation
//
pRotateTwice (RootPtr, PivotNode, PrevPivot, KSF_LEFT_HEAVY);
}
} else {
KeyParent->Flags = KeyParent->Flags & (~KSF_BALANCE_MASK);
}
} else if (BalanceFlags == KSF_RIGHT_HEAVY) {
if (KeyParent->Right == PrevPivot) {
MYASSERT (KeyStruct == GetKeyStruct (PrevPivot));
if (KeyStruct->Flags & KSF_RIGHT_HEAVY) {
//
// RR rotation
//
pRotateOnce (RootPtr, PivotNode, PrevPivot, KSF_RIGHT_HEAVY);
} else if (KeyStruct->Flags & KSF_LEFT_HEAVY) {
//
// RL rotation
//
pRotateTwice (RootPtr, PivotNode, PrevPivot, KSF_RIGHT_HEAVY);
}
} else {
KeyParent->Flags = KeyParent->Flags & (~KSF_BALANCE_MASK);
}
} else {
if (KeyParent->Right == PrevPivot) {
KeyParent->Flags = (KeyParent->Flags & (~KSF_BALANCE_MASK)) | KSF_RIGHT_HEAVY;
} else {
KeyParent->Flags = (KeyParent->Flags & (~KSF_BALANCE_MASK)) | KSF_LEFT_HEAVY;
}
}
if (PivotNode == PivotEnd) {
break;
}
PrevPivot = PivotNode;
PivotNode = KeyParent->Parent;
KeyStruct = KeyParent;
}
}
VOID
pBalanceDeletion (
OUT PDWORD RootPtr,
IN DWORD NodeNeedingAdjustment,
IN DWORD Direction
)
{
PKEYSTRUCT KeyStruct;
PKEYSTRUCT ChildStruct;
DWORD ChildOffset;
DWORD Node;
DWORD AntiDirection;
DWORD OldNode;
DWORD OrgParent;
Node = NodeNeedingAdjustment;
MYASSERT (Node != INVALID_OFFSET);
KeyStruct = GetKeyStruct (Node);
for (;;) {
MYASSERT (KeyStruct == GetKeyStruct (Node));
AntiDirection = ANTIDIRECTION (Direction);
OrgParent = KeyStruct->Parent;
//
// Case 1 - parent was initially balanced (terminates balancing)
//
if (!(KeyStruct->Flags & KSF_BALANCE_MASK)) {
KeyStruct->Flags |= AntiDirection;
break;
}
//
// Case 2 - parent was heavy on side that was deleted
//
if (KeyStruct->Flags & Direction) {
KeyStruct->Flags = KeyStruct->Flags & (~KSF_BALANCE_MASK);
}
//
// Cases 3, 4 and 5 - deletion caused imbalance in parent
//
else {
MYASSERT (KeyStruct->Flags & AntiDirection);
ChildOffset = Direction == KSF_LEFT_HEAVY ?
KeyStruct->Right :
KeyStruct->Left;
MYASSERT (ChildOffset != INVALID_OFFSET);
ChildStruct = GetKeyStruct (ChildOffset);
if (!(ChildStruct->Flags & KSF_BALANCE_MASK)) {
//
// Case 3 - single rotation needed (terminates balancing). We
// don't care that Node changes during rotation.
//
pRotateOnce (RootPtr, Node, ChildOffset, AntiDirection);
break;
} else if (ChildStruct->Flags & Direction) {
//
// Case 4 - double rotation needed, Node is changed during rotation
//
Node = pRotateTwice (RootPtr, Node, ChildOffset, AntiDirection);
} else {
//
// Case 5 - single rotation needed, Node is changed during rotation
//
Node = pRotateOnce (RootPtr, Node, ChildOffset, AntiDirection);
}
}
//
// Continue climbing the tree
//
OldNode = Node;
Node = OrgParent;
if (Node != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (Node);
if (KeyStruct->Left == OldNode) {
Direction = KSF_LEFT_HEAVY;
} else {
Direction = KSF_RIGHT_HEAVY;
}
} else {
break;
}
}
}
#ifdef DEBUG
VOID
DumpBinTreeStats (
VOID
)
{
DEBUGMSG ((
DBG_STATS,
"MemDb Binary Tree Stats:\n\n"
" Insertions: %u\n"
" Deletions: %u\n"
" Single Rotations: %u\n"
" Double Rotations: %u\n",
g_Insertions,
g_Deletions,
g_SingleRotations,
g_DoubleRotations
));
}
INT
pComputeHeight (
IN DWORD Offset
)
{
PKEYSTRUCT KeyStruct;
INT Left, Right;
if (Offset == INVALID_OFFSET) {
return 0;
}
KeyStruct = GetKeyStruct (Offset);
Left = pComputeHeight (KeyStruct->Left);
Right = pComputeHeight (KeyStruct->Right);
return 1 + max (Left, Right);
}
VOID
pMakeNum (
OUT PTSTR Msg,
IN DWORD Offset,
IN TCHAR LeftChar,
IN TCHAR RightChar
)
{
TCHAR Num[32];
INT Len;
PTSTR OrgMsg;
INT i;
_stprintf (Num, TEXT("%u"), Offset);
Len = (6 - TcharCount (Num)) / 2;
OrgMsg = Msg;
for (i = 0 ; i < Len ; i++) {
*Msg++ = LeftChar;
}
for (i = 0 ; Num[i] ; i++) {
*Msg++ = Num[i];
}
OrgMsg += 6;
while (Msg < OrgMsg) {
*Msg++ = RightChar;
}
*Msg = 0;
}
VOID
pDumpTree (
IN DWORD Root,
IN PCSTR Title OPTIONAL
)
{
DWORD Offset;
PKEYSTRUCT KeyStruct;
PKEYSTRUCT KeyParent;
DWORD MaxLevel;
DWORD Spaces;
UINT u;
TCHAR Msg[16384];
UINT Pos;
INT Pass;
GROWBUFFER NodesA = GROWBUF_INIT;
GROWBUFFER NodesB = GROWBUF_INIT;
PGROWBUFFER Nodes;
PGROWBUFFER NextNodes;
PDWORD OffsetPtr;
PDWORD EndOfList;
INT HalfWidth;
TCHAR LeftChar, RightChar;
if (Root == INVALID_OFFSET) {
return;
}
if (Title) {
LOGDIRECTA (DBG_VERBOSE, "\r\n");
LOGDIRECTA (DBG_VERBOSE, Title);
LOGDIRECTA (DBG_VERBOSE, "\r\n\r\n");
}
for (Pass = 0 ; Pass < 2 ; Pass++) {
MaxLevel = (DWORD) pComputeHeight (Root);
MaxLevel = min (MaxLevel, 10);
if (Pass == 0) {
HalfWidth = 3;
Spaces = 6;
} else {
HalfWidth = 1;
Spaces = 2;
}
for (u = 1 ; u < MaxLevel ; u++) {
Spaces *= 2;
}
NodesB.End = 0;
Nodes = &NodesA;
NextNodes = &NodesB;
GrowBufAppendDword (NextNodes, Root);
for (u = 0 ; u < MaxLevel ; u++) {
//
// Swap growbufs
//
if (Nodes == &NodesA) {
Nodes = &NodesB;
NextNodes = &NodesA;
} else {
Nodes = &NodesA;
NextNodes = &NodesB;
}
NextNodes->End = 0;
//
// Process all nodes
//
EndOfList = (PDWORD) (Nodes->Buf + Nodes->End);
for (OffsetPtr = (PDWORD) (Nodes->Buf) ; OffsetPtr < EndOfList ; OffsetPtr++) {
//
// Add all children as next nodes
//
Offset = *OffsetPtr;
if (Offset == INVALID_OFFSET) {
GrowBufAppendDword (NextNodes, INVALID_OFFSET);
GrowBufAppendDword (NextNodes, INVALID_OFFSET);
} else {
KeyStruct = GetKeyStruct (Offset);
GrowBufAppendDword (NextNodes, KeyStruct->Left);
GrowBufAppendDword (NextNodes, KeyStruct->Right);
}
//
// Print current node
//
Pos = 0;
LeftChar = TEXT(' ');
RightChar = TEXT(' ');
if (Offset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (Offset);
if (KeyStruct->Parent != INVALID_OFFSET) {
KeyParent = GetKeyStruct (KeyStruct->Parent);
if (KeyParent->Right == Offset) {
LeftChar = TEXT('\'');
} else if (KeyParent->Left == Offset) {
RightChar = TEXT('\'');
}
}
for ( ; Pos < (Spaces - HalfWidth) ; Pos++) {
Msg[Pos] = LeftChar;
}
if (Pass == 0) {
pMakeNum (Msg + Pos, Offset, LeftChar, RightChar);
} else {
_stprintf (Msg + Pos, TEXT("%2i"), FLAGS_TO_INT (KeyStruct->Flags & KSF_BALANCE_MASK));
}
Pos += TcharCount (Msg + Pos);
}
while (Pos < Spaces * 2) {
Msg[Pos] = RightChar;
Pos++;
}
Msg[Pos] = 0;
LOGDIRECT (DBG_VERBOSE, Msg);
}
LOGDIRECT (DBG_VERBOSE, TEXT("\r\n"));
for (OffsetPtr = (PDWORD) (Nodes->Buf) ; OffsetPtr < EndOfList ; OffsetPtr++) {
Offset = *OffsetPtr;
for (Pos = 0 ; Pos < Spaces ; Pos++) {
Msg[Pos] = TEXT(' ');
}
if (Offset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (*OffsetPtr);
if (KeyStruct->Left != INVALID_OFFSET ||
KeyStruct->Right != INVALID_OFFSET
) {
Msg[Pos] = '|';
Pos++;
}
}
while (Pos < Spaces * 2) {
Msg[Pos] = TEXT(' ');
Pos++;
}
Msg[Pos] = 0;
LOGDIRECT (DBG_VERBOSE, Msg);
}
Spaces /= 2;
LOGDIRECT (DBG_VERBOSE, TEXT("\r\n"));
Spaces = max (Spaces, 1);
}
LOGDIRECT (DBG_VERBOSE, TEXT("\r\n"));
}
FreeGrowBuffer (&NodesA);
FreeGrowBuffer (&NodesB);
}
BOOL
pCheckTreeBalance (
IN DWORD Root,
IN BOOL Force
)
{
DWORD NextOffset;
DWORD PrevOffset;
DWORD Offset;
PKEYSTRUCT KeyStruct;
DWORD MinLevel = 0xFFFFFFFF;
DWORD MaxLevel = 0;
DWORD Level = 0;
DWORD Nodes = 0;
static DWORD SpotCheck = 0;
//
// Don't perform this check every single time
//
if (!Force) {
SpotCheck++;
if (SpotCheck == 10000) {
SpotCheck = 0;
} else {
return FALSE;
}
}
if (Root == INVALID_OFFSET) {
return FALSE;
}
pCheckBalanceFactors (Root);
NextOffset = Root;
//
// Get leftmost node
//
do {
Offset = NextOffset;
Level++;
KeyStruct = GetKeyStruct (Offset);
NextOffset = KeyStruct->Left;
} while (NextOffset != INVALID_OFFSET);
//
// Recurse through entire tree
//
PrevOffset = INVALID_OFFSET;
do {
//
// Visit node at Offset
//
Nodes++;
KeyStruct = GetKeyStruct (Offset);
if (KeyStruct->Left == INVALID_OFFSET ||
KeyStruct->Right == INVALID_OFFSET
) {
MinLevel = min (MinLevel, Level);
MaxLevel = max (MaxLevel, Level);
}
//
// Go to the next node
//
if (KeyStruct->Right != INVALID_OFFSET) {
//
// Go to left-most node of right
//
KeyStruct = GetKeyStruct (Offset);
NextOffset = KeyStruct->Right;
while (NextOffset != INVALID_OFFSET) {
Offset = NextOffset;
Level++;
KeyStruct = GetKeyStruct (Offset);
NextOffset = KeyStruct->Left;
}
}
else {
//
// Go to parent, looping if its right child is the
// previous node.
//
do {
PrevOffset = Offset;
Offset = KeyStruct->Parent;
Level--;
if (Offset == INVALID_OFFSET) {
break;
}
KeyStruct = GetKeyStruct (Offset);
} while (KeyStruct->Right == PrevOffset);
}
} while (Offset != INVALID_OFFSET);
DEBUGMSG_IF ((
(MaxLevel - MinLevel) > 3,
DBG_NAUSEA,
"Binary tree imbalance detected: MinLevel=%u, MaxLevel=%u, Nodes=%u",
MinLevel,
MaxLevel,
Nodes
));
return TRUE;
}
INT
pComputeBalanceFactor (
IN DWORD Offset
)
{
PKEYSTRUCT KeyStruct;
KeyStruct = GetKeyStruct (Offset);
return pComputeHeight (KeyStruct->Right) - pComputeHeight (KeyStruct->Left);
}
VOID
pCheckBalanceFactors (
IN DWORD Root
)
{
DWORD Offset;
INT Factor;
PKEYSTRUCT KeyStruct;
Offset = GetFirstOffset (Root);
while (Offset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (Offset);
Factor = pComputeBalanceFactor (Offset);
if ((Factor == -1 && !(KeyStruct->Flags & KSF_LEFT_HEAVY)) ||
(Factor == 1 && !(KeyStruct->Flags & KSF_RIGHT_HEAVY)) ||
(!Factor && (KeyStruct->Flags & KSF_BALANCE_MASK))
) {
pDumpTree (Root, "Tree Balance Factor Error");
DEBUGMSG ((DBG_WHOOPS, "Tree balance factors are wrong!"));
break;
}
if (Factor < -1 || Factor > 1) {
pDumpTree (Root, "Balance Factor Out of Bounds.");
DEBUGMSG ((DBG_WHOOPS, "Balance factors out of bounds!"));
break;
}
Offset = GetNextOffset (Offset);
}
}
#endif
PBYTE
pAllocMemoryFromDb (
IN UINT RequestSize,
OUT PDWORD Offset,
OUT PINT AdjustFactor
)
{
PBYTE result;
PBYTE newBuf;
//
// Grow heap if necessary
//
*AdjustFactor = 0;
if (RequestSize + g_db->End > g_db->AllocSize) {
if (g_db->AllocSize < 0x100000) {
g_db->AllocSize += BLOCK_SIZE;
} else {
g_db->AllocSize *= 2;
}
if (g_db->AllocSize >= MAX_MEMDB_SIZE) {
OutOfMemory_Terminate ();
}
if (g_db->Buf) {
newBuf = (PBYTE) MemReAlloc (g_hHeap, 0, g_db->Buf, g_db->AllocSize);
} else {
newBuf = (PBYTE) MemAlloc (g_hHeap, 0, g_db->AllocSize);
}
if (!newBuf) {
// g_db->AllocSize must be bigger than 2G
OutOfMemory_Terminate();
}
//
// provide relocation difference to caller
//
if (g_db->Buf) {
*AdjustFactor = (INT) ((PBYTE) newBuf - (PBYTE) g_db->Buf);
}
g_db->Buf = newBuf;
}
result = g_db->Buf + g_db->End;
*Offset = g_db->End;
g_db->End += RequestSize;
return result;
}
PKEYSTRUCT
pAllocKeyStructBlock (
OUT PDWORD Offset,
OUT PINT AdjustFactor
)
{
DWORD delOffset;
DWORD prevDel;
PKEYSTRUCT keyStruct = NULL;
//
// Look for free block
//
*AdjustFactor = 0;
prevDel = INVALID_OFFSET;
delOffset = g_db->FirstDeleted;
while (delOffset != INVALID_OFFSET) {
keyStruct = GetKeyStruct (delOffset);
prevDel = delOffset;
delOffset = keyStruct->NextDeleted;
}
//
// Alloc new block if no free space
//
if (delOffset == INVALID_OFFSET) {
//
// Calc position in block
//
keyStruct = (PKEYSTRUCT) pAllocMemoryFromDb (sizeof (KEYSTRUCT), Offset, AdjustFactor);
} else {
//
// Delink free block if recovering free space
//
*Offset = delOffset;
if (prevDel != INVALID_OFFSET) {
GetKeyStruct (prevDel)->NextDeleted = keyStruct->NextDeleted;
} else {
g_db->FirstDeleted = keyStruct->NextDeleted;
}
}
return keyStruct;
}
DWORD
pAllocKeyStruct (
IN OUT PDWORD ParentOffsetPtr,
IN PCWSTR KeyName,
IN DWORD PrevLevelNode
)
/*++
Routine Description:
pAllocKeyStruct allocates a block of memory in the single
heap, expanding it if necessary.
The KeyName must not already be in the tree, and
ParentOffsetPtr must point to a valid DWORD offset
variable. ParentOffsetPtr, or one of the children
of ParentOffsetPtr, will be linked to the new struct.
Arguments:
ParentOffsetPtr - Address of a DWORD that holds the offset to
the root. Within the function, the variable
will change to point to the parent of the
new struct.
KeyName - The string identifying the key. It cannot
contain backslashes. The new struct will
be initialized and this name will be copied
into the struct.
PrevLevelNode - Specifies the previous level root offset
Return Value:
An offset to the new structure.
--*/
{
PKEYSTRUCT KeyStruct;
PKEYSTRUCT KeyParent;
DWORD Offset;
DWORD NodeOffsetParent;
INT cmp;
DWORD PivotEnd;
PDWORD RootPtr;
INT adjustFactor;
DWORD newToken;
INCSTAT(g_Insertions);
#ifdef DEBUG
pCheckTreeBalance (*ParentOffsetPtr, FALSE);
#endif
KeyStruct = pAllocKeyStructBlock (
&Offset,
&adjustFactor
);
//
// Database might have moved. Relocate any pointers within the database now.
//
if (ParentOffsetPtr != &g_db->FirstLevelRoot) {
ParentOffsetPtr = (PDWORD) ((PBYTE) ParentOffsetPtr + adjustFactor);
}
//
// Init new block
//
KeyStruct->NextLevelRoot = INVALID_OFFSET;
KeyStruct->PrevLevelNode = PrevLevelNode;
KeyStruct->Left = INVALID_OFFSET;
KeyStruct->Right = INVALID_OFFSET;
KeyStruct->dwValue = 0;
KeyStruct->Flags = 0;
#ifdef DEBUG
KeyStruct->Signature = SIGNATURE;
#endif
newToken = pAllocKeyToken (KeyName, &adjustFactor);
//
// Again the database might have moved
//
KeyStruct = (PKEYSTRUCT) ((PBYTE) KeyStruct + adjustFactor);
if (ParentOffsetPtr != &g_db->FirstLevelRoot) {
ParentOffsetPtr = (PDWORD) ((PBYTE) ParentOffsetPtr + adjustFactor);
}
//
// finish updating keystruct
//
KeyStruct->KeyToken = newToken;
//
// Put it in the tree
//
NodeOffsetParent = INVALID_OFFSET;
PivotEnd = INVALID_OFFSET;
RootPtr = ParentOffsetPtr;
while (*ParentOffsetPtr != INVALID_OFFSET) {
NodeOffsetParent = *ParentOffsetPtr;
KeyParent = GetKeyStruct (*ParentOffsetPtr);
if (KeyParent->Flags & KSF_BALANCE_MASK) {
PivotEnd = *ParentOffsetPtr;
}
cmp = StringICompareW (KeyName, GetKeyToken (KeyParent->KeyToken));
if (cmp < 0) {
ParentOffsetPtr = &KeyParent->Left;
} else if (cmp > 0) {
ParentOffsetPtr = &KeyParent->Right;
} else {
MYASSERT (FALSE);
}
}
KeyStruct->Parent = NodeOffsetParent;
*ParentOffsetPtr = Offset;
#ifdef DEBUG
// If using retail structs, delete Signature from BlockPtr
if (!g_UseDebugStructs) {
MoveMemory (
KeyStruct,
(PCBYTE) KeyStruct + (sizeof (KEYSTRUCT_DEBUG) - sizeof (KEYSTRUCT_RETAIL)),
sizeof (KEYSTRUCT_RETAIL)
);
}
#endif
//
// Balance the tree
//
pBalanceInsertion (RootPtr, Offset, PivotEnd);
#ifdef DEBUG
pCheckTreeBalance (*RootPtr, FALSE);
#endif
return Offset;
}
VOID
pDeallocKeyStruct (
IN DWORD Offset,
IN OUT PDWORD RootPtr,
IN BOOL DelinkFlag,
IN BOOL ClearFlag
)
/*++
Routine Description:
pDeallocKeyStruct first deletes all structures pointed to by
NextLevelRoot. After all items are deleted from the next
level, pDeallocKeyStruct optionally delinks the struct from
the binary tree. Before exiting, the struct is given to the
deleted block chain.
Arguments:
Offset - An offset to the item as provided by pAllocKeyStruct
or any of the Find functions.
RootPtr - A pointer to the level tree root variable. This value
will be updated if delinking is involved.
DelinkFlag - A flag indicating TRUE to delink the struct from
the binary tree it is in, or FALSE if the struct is
only to be added to the deleted block chain.
ClearFlag - Specifies FALSE if the key struct's children are to
be deleted, or TRUE if the current key struct should
simply be cleaned up but left allocated.
Return Value:
none
--*/
{
PKEYSTRUCT KeyStruct;
PKEYSTRUCT KeyParent;
PKEYSTRUCT KeyChild;
PKEYSTRUCT KeyLeftmost;
PKEYSTRUCT KeyLeftChild;
PKEYSTRUCT KeyRightChild;
DWORD Leftmost;
DWORD NodeOffset;
PDWORD ParentOffsetPtr;
WCHAR TempStr[MEMDB_MAX];
DWORD Direction = 0;
DWORD RebalanceOffset;
KeyStruct = GetKeyStruct (Offset);
//
// Remove endpoints from hash table
//
if (KeyStruct->Flags & KSF_ENDPOINT) {
PrivateBuildKeyFromOffset (0, Offset, TempStr, NULL, NULL, NULL);
RemoveHashTableEntry (TempStr);
//
// Free binary value on key
//
FreeKeyStructBinaryBlock (KeyStruct);
KeyStruct->Flags &= ~KSF_ENDPOINT;
}
//
// Call recursively if there are sublevels to this key
//
if (!ClearFlag) {
if (KeyStruct->NextLevelRoot != INVALID_OFFSET) {
NodeOffset = GetFirstOffset (KeyStruct->NextLevelRoot);
while (NodeOffset != INVALID_OFFSET) {
pDeallocKeyStruct (NodeOffset, &KeyStruct->NextLevelRoot, FALSE, FALSE);
NodeOffset = GetNextOffset (NodeOffset);
}
}
//
// Remove the item from its binary tree
//
if (DelinkFlag) {
//
// Find parent-to-child pointer
//
if (KeyStruct->Parent != INVALID_OFFSET) {
KeyParent = GetKeyStruct (KeyStruct->Parent);
if (KeyParent->Left == Offset) {
ParentOffsetPtr = &KeyParent->Left;
Direction = KSF_LEFT_HEAVY;
} else {
ParentOffsetPtr = &KeyParent->Right;
Direction = KSF_RIGHT_HEAVY;
}
} else {
ParentOffsetPtr = RootPtr;
}
if (KeyStruct->Left == INVALID_OFFSET &&
KeyStruct->Right == INVALID_OFFSET
) {
//
// No children; reset parent, then rebalance tree
//
*ParentOffsetPtr = INVALID_OFFSET;
RebalanceOffset = KeyStruct->Parent;
} else if (KeyStruct->Left == INVALID_OFFSET) {
//
// Only a right child; bring it up a level and rebalance
//
*ParentOffsetPtr = KeyStruct->Right;
KeyChild = GetKeyStruct (*ParentOffsetPtr);
KeyChild->Parent = KeyStruct->Parent;
//
// The moved node's balance factor must be set the same as the
// node we are deleting. The rebalancing will correct it.
//
KeyChild->Flags = (KeyChild->Flags & (~KSF_BALANCE_MASK)) |
(KeyStruct->Flags & KSF_BALANCE_MASK);
Direction = KSF_RIGHT_HEAVY;
RebalanceOffset = KeyStruct->Right;
} else if (KeyStruct->Right == INVALID_OFFSET) {
//
// Only a left child; bring it up a level and rebalance
//
*ParentOffsetPtr = KeyStruct->Left;
KeyChild = GetKeyStruct (*ParentOffsetPtr);
KeyChild->Parent = KeyStruct->Parent;
//
// The moved node's balance factor must be set the same as the
// node we are deleting. The rebalancing will correct it.
//
KeyChild->Flags = (KeyChild->Flags & (~KSF_BALANCE_MASK)) |
(KeyStruct->Flags & KSF_BALANCE_MASK);
Direction = KSF_LEFT_HEAVY;
RebalanceOffset = KeyStruct->Left;
} else {
//
// Two children - find min val of right subtree (the leftmost node
// of the right child).
//
Leftmost = KeyStruct->Right;
KeyLeftmost = GetKeyStruct (Leftmost);
while (KeyLeftmost->Left != INVALID_OFFSET) {
Leftmost = KeyLeftmost->Left;
KeyLeftmost = GetKeyStruct (Leftmost);
}
//
// If Leftmost has right children, and it is not the
// right child of the node we are deleting, then
// hook right subtree to parent.
//
// If Leftmost does not have right children, then
// remove its parent's linkage
//
if (Leftmost != KeyStruct->Right) {
KeyParent = GetKeyStruct (KeyLeftmost->Parent);
if (KeyLeftmost->Right != INVALID_OFFSET) {
//
// Because of the balance properties, we know that
// we have a single leaf node to the right. Its
// balance factor is zero, and we move it to a
// position where it remains zero.
//
KeyRightChild = GetKeyStruct (KeyLeftmost->Right);
MYASSERT (KeyRightChild->Left == INVALID_OFFSET);
MYASSERT (KeyRightChild->Right == INVALID_OFFSET);
KeyParent->Left = KeyLeftmost->Right;
KeyRightChild->Parent = KeyLeftmost->Parent;
} else {
KeyParent->Left = INVALID_OFFSET;
}
//
// We are affecting the balance factor of the
// parent. Rebalancing must start at the leftmost
// node's parent.
//
RebalanceOffset = KeyLeftmost->Parent;
Direction = KSF_LEFT_HEAVY; // we deleted from the left side
} else {
//
// In this case there is no leftmost node of the right child.
// Therefore, we reduced the height of the right side.
//
RebalanceOffset = Leftmost;
Direction = KSF_RIGHT_HEAVY;
}
//
// Now leftmost is available to replace the deleted
// node
//
KeyLeftmost->Parent = KeyStruct->Parent;
*ParentOffsetPtr = Leftmost;
KeyLeftmost->Left = KeyStruct->Left;
KeyLeftChild = GetKeyStruct (KeyStruct->Left);
KeyLeftChild->Parent = Leftmost;
if (Leftmost != KeyStruct->Right) {
KeyLeftmost->Right = KeyStruct->Right;
MYASSERT (KeyStruct->Right != INVALID_OFFSET);
KeyRightChild = GetKeyStruct (KeyStruct->Right);
KeyRightChild->Parent = Leftmost;
}
//
// We need to copy the balance factor of what we are deleting to the
// replacement node.
//
KeyLeftmost->Flags = (KeyLeftmost->Flags & (~KSF_BALANCE_MASK)) |
(KeyStruct->Flags & KSF_BALANCE_MASK);
}
//
// Rebalance the tree
//
if (RebalanceOffset != INVALID_OFFSET) {
MYASSERT (Direction);
if (Direction) {
//pDumpTree (*RootPtr, "Before rebalance");
pBalanceDeletion (RootPtr, RebalanceOffset, Direction);
//pDumpTree (*RootPtr, "Final tree");
}
}
#ifdef DEBUG
pCheckTreeBalance (*RootPtr, FALSE);
#endif
}
//
// Donate block to free space unless caller does not
// want child structs freed.
//
pDeallocToken (KeyStruct->KeyToken);
KeyStruct->NextDeleted = g_db->FirstDeleted;
g_db->FirstDeleted = Offset;
}
}
VOID
pRemoveHashEntriesForNode (
IN PCWSTR Root,
IN DWORD Offset
)
/*++
Routine Description:
pRemoveHashEntriesFromNode removes all hash table entries from all children
of the specified node. This function is called recursively.
Arguments:
Root - Specifies the root string that corresponds with Offset. This must
also contain the temporary hive root.
Offset - Specifies the offset of the node to process. The node and all of
its children will be removed from the hash table.
Return Value:
None.
--*/
{
DWORD ChildOffset;
PKEYSTRUCT KeyStruct;
WCHAR ChildRoot[MEMDB_MAX];
PWSTR End;
//
// Remove hash entry if this root is an endpoint
//
KeyStruct = GetKeyStruct (Offset);
if (KeyStruct->Flags & KSF_ENDPOINT) {
RemoveHashTableEntry (Root);
#ifdef DEBUG
{
DWORD HashOffset;
HashOffset = FindStringInHashTable (Root, NULL);
if (HashOffset != INVALID_OFFSET) {
DEBUGMSG ((DBG_WARNING, "Memdb move duplicate: %s", Root));
}
}
#endif
}
//
// Recurse for all children, removing hash entries for all endpoints found
//
StringCopyW (ChildRoot, Root);
End = GetEndOfStringW (ChildRoot);
*End = L'\\';
End++;
*End = 0;
ChildOffset = GetFirstOffset (KeyStruct->NextLevelRoot);
while (ChildOffset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (ChildOffset);
StringCopyW (End, GetKeyToken (KeyStruct->KeyToken));
pRemoveHashEntriesForNode (ChildRoot, ChildOffset);
ChildOffset = GetNextOffset (ChildOffset);
}
}
VOID
pAddHashEntriesForNode (
IN PCWSTR Root,
IN DWORD Offset,
IN BOOL AddRoot
)
/*++
Routine Description:
pAddHashEntriesForNode adds hash table entries for the specified root and
all of its children.
Arguments:
Root - Specifies the root string that corresponds to Offset. This string
must also include the temporary hive root.
Offset - Specifies the node offset to begin processing. The node and all
of its children are added to the hash table.
AddRoot - Specifies TRUE if the root should be added to the hash table,
FALSE otherwise.
Return Value:
None.
--*/
{
DWORD ChildOffset;
PKEYSTRUCT KeyStruct;
WCHAR ChildRoot[MEMDB_MAX];
PWSTR End;
DWORD HashOffset;
//
// Add hash entry if this root is an endpoint
//
KeyStruct = GetKeyStruct (Offset);
if (AddRoot && KeyStruct->Flags & KSF_ENDPOINT) {
HashOffset = FindStringInHashTable (Root, NULL);
if (HashOffset != Offset) {
#ifdef DEBUG
if (HashOffset != INVALID_OFFSET) {
DEBUGMSG ((DBG_WARNING, "Memdb duplicate: %s", Root));
}
#endif
AddHashTableEntry (Root, Offset);
}
}
//
// Recurse for all children, adding hash entries for all endpoints found
//
StringCopyW (ChildRoot, Root);
End = GetEndOfStringW (ChildRoot);
*End = L'\\';
End++;
*End = 0;
ChildOffset = GetFirstOffset (KeyStruct->NextLevelRoot);
while (ChildOffset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (ChildOffset);
StringCopyW (End, GetKeyToken (KeyStruct->KeyToken));
pAddHashEntriesForNode (ChildRoot, ChildOffset, TRUE);
ChildOffset = GetNextOffset (ChildOffset);
}
}
BOOL
pFindPlaceForNewNode (
IN PKEYSTRUCT InsertNode,
IN PDWORD TreeRootPtr,
OUT PDWORD ParentOffsetPtr,
OUT PDWORD *ParentToChildOffsetPtr,
OUT PDWORD PivotEnd
)
/*++
Routine Description:
pFindPlaceForNewNode searches a level for the position within the tree.
This is used to insert new unique keys in the tree.
Arguments:
InsertNode - Specifies the allocated but unlinked node. Its
Key member must be valid.
TreeRootPtr - Specifies a pointer to the memory that holds the
root offset. This is used to walk the tree. It
can be INVALID_OFFSET.
ParentOffsetPtr - Receives the offset to the parent node
ParentToChildOffsetPtr - Recieves the address of the left or right child
pointer within the parent struct
PivotEnd - Receives the offset of the tree node that should
stop balancing
Return Value:
TRUE if a spot was found in the tree for InsertNode, or FALSE if a spot was
not found (because the key name is already in the tree).
--*/
{
PDWORD ParentPtr;
PKEYSTRUCT Parent;
INT cmp;
ParentPtr = TreeRootPtr;
*ParentOffsetPtr = INVALID_OFFSET;
*PivotEnd = INVALID_OFFSET;
while (*ParentPtr != INVALID_OFFSET) {
*ParentOffsetPtr = *ParentPtr;
Parent = GetKeyStruct (*ParentPtr);
if (Parent->Flags & KSF_BALANCE_MASK) {
*PivotEnd = *ParentPtr;
}
cmp = StringICompareW (GetKeyToken (InsertNode->KeyToken), GetKeyToken (Parent->KeyToken));
if (cmp < 0) {
ParentPtr = &Parent->Left;
} else if (cmp > 0) {
ParentPtr = &Parent->Right;
} else {
return FALSE;
}
}
*ParentToChildOffsetPtr = ParentPtr;
return TRUE;
}
VOID
pMergeFamilies (
IN PDWORD DestTreeRootPtr,
IN DWORD MergeSrcOffset,
IN DWORD MergeDestPrevLevelOffset
)
/*++
Routine Description:
pMergeFamilies takes two tree families and merges them together. When
duplicates are found, their linkage is abandoned, but they are not
deallocated. This allows MemDbBuildKeyFromOffset to continue to work.
Arguments:
DestTreeRootPtr - Specifies the address of the destination level's
root variable. This is potentially altered with
insertion and balancing.
MergeSrcOffset - Specifies the offset to the source tree family
(STF). The STF is merged into the destination
tree indicated by DestTreeRootPtr.
MergeDestPrevLevelOffset - Specifies the offset to the previous level node.
This value cannot be INVALID_OFFSET.
Return Value:
None.
--*/
{
PKEYSTRUCT MergeSrc;
PKEYSTRUCT MergeDest;
PDWORD ParentToChildOffsetPtr;
DWORD ParentOffset;
DWORD DestCollisionOffset;
DWORD PivotEnd;
GROWBUFFER NextLevelMerge = GROWBUF_INIT;
DWORD NodeOffset;
PDWORD NextLevelOffsetPtr;
UINT Pos;
BOOL FoundPlaceForNode;
//
// Look for a place within the tree indicated by the offset
// stored in DestTreeRootPtr. If one is found, we can simply
// relink the node at MergeSrcOffset. Otherwise, we have to
// recursively merge the next level of MergeSrcOffset, and
// we have to abandon MergeSrcOffset.
//
MergeSrc = GetKeyStruct (MergeSrcOffset);
MYASSERT (MergeSrc);
FoundPlaceForNode = pFindPlaceForNewNode (
MergeSrc,
DestTreeRootPtr,
&ParentOffset,
&ParentToChildOffsetPtr,
&PivotEnd
);
if (FoundPlaceForNode) {
//
// Since we found a place to put the src family, it is
// easy to hook it and its next level into the dest
// family.
//
MergeSrc->Parent = ParentOffset;
*ParentToChildOffsetPtr = MergeSrcOffset;
MergeSrc->Flags = MergeSrc->Flags & (~KSF_BALANCE_MASK);
MergeSrc->Left = INVALID_OFFSET;
MergeSrc->Right = INVALID_OFFSET;
MergeSrc->PrevLevelNode = MergeDestPrevLevelOffset;
pBalanceInsertion (DestTreeRootPtr, MergeSrcOffset, PivotEnd);
#ifdef DEBUG
pCheckTreeBalance (*DestTreeRootPtr, FALSE);
#endif
} else {
//
// We found a collision, then we have to abandon MergeSrc,
// removing linkage to the parent and children -- but preserving
// the linkage to the previous level. Finally, we have to call
// this function recursively to hook up all the next level nodes.
//
DestCollisionOffset = ParentOffset; // renamed to be more accurate
MergeDest = GetKeyStruct (DestCollisionOffset);
MYASSERT (MergeDest);
MergeSrc->Parent = INVALID_OFFSET;
MergeSrc->Left = INVALID_OFFSET;
MergeSrc->Right = INVALID_OFFSET;
MergeSrc->PrevLevelNode = MergeDestPrevLevelOffset;
//
// If this is an end point, then try to preserve value and flags
//
if (MergeSrc->Flags & KSF_ENDPOINT) {
if (MergeDest->Flags & KSF_ENDPOINT) {
DEBUGMSG ((
DBG_WARNING,
"MemDb: Loss of value and flags in %s",
GetKeyToken (MergeSrc->KeyToken)
));
} else {
MergeDest->Flags = MergeDest->Flags & ~KSF_FLAGS_TO_COPY;
MergeDest->Flags |= MergeSrc->Flags & KSF_FLAGS_TO_COPY;
MergeDest->dwValue = MergeSrc->dwValue;
}
}
//
// Save away all entries in the next src level into a grow buffer,
// then call pMergeFamilies recursively.
//
NodeOffset = GetFirstOffset (MergeSrc->NextLevelRoot);
while (NodeOffset != INVALID_OFFSET) {
NextLevelOffsetPtr = (PDWORD) GrowBuffer (&NextLevelMerge, sizeof (DWORD));
MYASSERT (NextLevelOffsetPtr);
*NextLevelOffsetPtr = NodeOffset;
NodeOffset = GetNextOffset (NodeOffset);
}
NextLevelOffsetPtr = (PDWORD) NextLevelMerge.Buf;
for (Pos = 0 ; Pos < NextLevelMerge.End ; Pos += sizeof (DWORD)) {
pMergeFamilies (
&MergeDest->NextLevelRoot,
*NextLevelOffsetPtr,
DestCollisionOffset
);
NextLevelOffsetPtr++;
}
FreeGrowBuffer (&NextLevelMerge);
}
}
DWORD
pMoveKey (
IN DWORD OriginalKey,
IN PCWSTR NewKeyRoot,
IN PCWSTR NewKeyRootWithHive
)
/*++
Routine Description:
pMoveKey moves a key (and all of its children) to a new root. If the
caller specifies a source key that has no children, a proxy node is created
to maintain offsets. The proxy node is not listed in the hash table.
Arguments:
OriginalKey - Specifies the offset to the original key that needs to
be moved. It does not need to be an endpoint, and may
have children.
NewKeyRoot - Specifies the new root for OriginalKey. It may have
multiple levels (separated by backslashes).
NewKeyRootWithHive - Different from NewKeyRoot only when the node is in a
temporary hive. This is used for the hash table only.
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
DWORD ReplacementKey;
PKEYSTRUCT SrcKey, DestKey, ChildKey;
PKEYSTRUCT KeyParent;
DWORD NodeOffset;
GROWBUFFER Children = GROWBUF_INIT;
PDWORD ChildOffsetPtr;
DWORD Pos;
WCHAR OriginalRoot[MEMDB_MAX];
BOOL Endpoint;
//
// Check requirements
//
SrcKey = GetKeyStruct (OriginalKey);
if (!SrcKey) {
DEBUGMSG ((DBG_WHOOPS, "MemDb: pMoveKey can't find original key %s", OriginalKey));
return INVALID_OFFSET;
}
if (SrcKey->Flags & KSF_PROXY_NODE) {
DEBUGMSG ((DBG_WHOOPS, "MemDb: pMoveKey can't move proxy node %s", OriginalKey));
return INVALID_OFFSET;
}
Endpoint = SrcKey->Flags & KSF_ENDPOINT;
if (!PrivateBuildKeyFromOffset (0, OriginalKey, OriginalRoot, NULL, NULL, NULL)) {
return INVALID_OFFSET;
}
//
// Allocate new key
//
ReplacementKey = pNewKey (NewKeyRoot, NewKeyRootWithHive, FALSE);
if (ReplacementKey == INVALID_OFFSET) {
return INVALID_OFFSET;
}
SrcKey = GetKeyStruct (OriginalKey);
DestKey = GetKeyStruct (ReplacementKey);
if (!SrcKey || !DestKey) {
return INVALID_OFFSET;
}
DEBUGMSG ((DBG_NAUSEA, "Moving %s to %s", OriginalRoot, NewKeyRootWithHive));
//
// Remove all hash entries for all children
//
pRemoveHashEntriesForNode (OriginalRoot, OriginalKey);
//
// Record all children in an array
//
NodeOffset = GetFirstOffset (SrcKey->NextLevelRoot);
while (NodeOffset != INVALID_OFFSET) {
ChildOffsetPtr = (PDWORD) GrowBuffer (&Children, sizeof (DWORD));
if (!ChildOffsetPtr) {
return INVALID_OFFSET;
}
*ChildOffsetPtr = NodeOffset;
NodeOffset = GetNextOffset (NodeOffset);
}
//
// Move next level pointer to new node. There are two cases
// to handle:
//
// 1. Destination exists and has children. Here the source
// needs to be merged into the destination.
//
// 2. Destination is brand new and has no children. Here we
// simply move the source children to the destination.
//
// During this process, the hash table is updated accordingly.
//
if (DestKey->NextLevelRoot != INVALID_OFFSET) {
//
// Hard case, merge children to new parent's family
//
ChildOffsetPtr = (PDWORD) Children.Buf;
for (Pos = 0 ; Pos < Children.End ; Pos += sizeof (DWORD)) {
pMergeFamilies (
&DestKey->NextLevelRoot,
*ChildOffsetPtr,
ReplacementKey
);
ChildOffsetPtr++;
}
} else {
//
// Easy case, link children to new parent
//
DestKey->NextLevelRoot = SrcKey->NextLevelRoot;
SrcKey->NextLevelRoot = INVALID_OFFSET;
if (DestKey->Flags & KSF_ENDPOINT) {
DEBUGMSG ((
DBG_WARNING,
"MemDb: Loss of value and flags in %s",
GetKeyToken (SrcKey->KeyToken)
));
} else {
DestKey->Flags = DestKey->Flags & ~KSF_FLAGS_TO_COPY;
DestKey->Flags |= SrcKey->Flags & KSF_FLAGS_TO_COPY;
DestKey->dwValue = SrcKey->dwValue;
}
ChildOffsetPtr = (PDWORD) Children.Buf;
for (Pos = 0 ; Pos < Children.End ; Pos += sizeof (DWORD)) {
NodeOffset = *ChildOffsetPtr;
ChildOffsetPtr++;
ChildKey = GetKeyStruct (NodeOffset);
ChildKey->PrevLevelNode = ReplacementKey;
}
}
//
// Add all new entries to hash table
//
pAddHashEntriesForNode (NewKeyRootWithHive, ReplacementKey, FALSE);
//
// Free the original key node, or if an endpoint, make the
// node a proxy to the new node (to maintain offsets).
//
if (!Endpoint) {
SrcKey->NextLevelRoot = INVALID_OFFSET;
KeyParent = GetKeyStruct (SrcKey->PrevLevelNode);
pDeallocKeyStruct (OriginalKey, &KeyParent->NextLevelRoot, TRUE, FALSE);
} else {
DestKey->Flags = (DestKey->Flags & KSF_BALANCE_MASK) | (SrcKey->Flags & (~KSF_BALANCE_MASK));
DestKey->dwValue = SrcKey->dwValue;
SrcKey->Flags = KSF_PROXY_NODE | (SrcKey->Flags & KSF_BALANCE_MASK);
SrcKey->dwValue = ReplacementKey;
SrcKey->NextLevelRoot = INVALID_OFFSET;
}
FreeGrowBuffer (&Children);
return ReplacementKey;
}
BOOL
MemDbMoveTreeA (
IN PCSTR RootNode,
IN PCSTR NewRoot
)
/*++
Routine Description:
MemDbMoveTree is the external interface to pMoveKey. See description in
pMoveKey for details.
Arguments:
RootNode - Specifies the node to move.
NewRoot - Specifies the new root for RootNode.
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
PCWSTR UnicodeRootNode;
PCWSTR UnicodeNewRoot;
BOOL b = FALSE;
UnicodeRootNode = ConvertAtoW (RootNode);
UnicodeNewRoot = ConvertAtoW (NewRoot);
if (UnicodeRootNode && UnicodeNewRoot) {
b = MemDbMoveTreeW (UnicodeRootNode, UnicodeNewRoot);
}
FreeConvertedStr (UnicodeRootNode);
FreeConvertedStr (UnicodeNewRoot);
return b;
}
BOOL
MemDbMoveTreeW (
IN PCWSTR RootNode,
IN PCWSTR NewRoot
)
{
DWORD Offset;
WCHAR Temp[MEMDB_MAX];
WCHAR NewRootWithHive[MEMDB_MAX];
PWSTR p, q;
PCWSTR SubKey;
BOOL b = FALSE;
INT HiveLen;
if (StringIMatch (RootNode, NewRoot)) {
DEBUGMSG ((DBG_WHOOPS, "Cannot move tree because source and dest are the same"));
return FALSE;
}
EnterCriticalSection (&g_MemDbCs);
__try {
SubKey = SelectHive (RootNode);
//
// Copy key to temp buffer
//
StringCopyW (Temp, SubKey);
if (*Temp == 0) {
DEBUGMSG ((DBG_WHOOPS, "MemDbMoveTree requires a root"));
__leave;
}
//
// Compute the new root with the original hive
//
if (StringIMatchW (Temp, RootNode)) {
// no hive case
StringCopyW (NewRootWithHive, NewRoot);
} else {
HiveLen = wcslen (RootNode) - wcslen (SubKey);
StringCopyTcharCountW (NewRootWithHive, RootNode, HiveLen);
StringCopyW (AppendWackW (NewRootWithHive), NewRoot);
}
//
// Find the last offset of the root key
//
q = Temp;
Offset = INVALID_OFFSET;
do {
if (Offset == INVALID_OFFSET) {
Offset = g_db->FirstLevelRoot;
} else {
Offset = GetKeyStruct (Offset)->NextLevelRoot;
}
if (Offset == INVALID_OFFSET) {
DEBUGMSGW ((DBG_VERBOSE, "MemDbMoveTree root %s not found", RootNode));
__leave;
}
p = wcschr (q, L'\\');
if (p) {
*p = 0;
}
Offset = FindKeyStruct (Offset, q);
if (Offset == INVALID_OFFSET) {
DEBUGMSGW ((DBG_VERBOSE, "MemDbMoveTree root %s not found", RootNode));
__leave;
}
q = p + 1;
} while (p);
//
// Now move the key
//
Offset = pMoveKey (Offset, NewRoot, NewRootWithHive);
if (Offset != INVALID_OFFSET) {
b = TRUE;
} else {
DEBUGMSGW ((DBG_WHOOPS, "Can't move %s to %s", RootNode, NewRoot));
}
}
__finally {
LeaveCriticalSection (&g_MemDbCs);
}
return b;
}
PKEYSTRUCT
pGetKeyStructWithProxy (
IN DWORD Offset
)
/*++
Routine Description:
pGetKeyStructWithProxy returns a pointer given an offset. It also implements proxy
nodes, transparent to the rest of memdb. The debug version checks the
signature and validity of each offset. It is assumed that Offset is always
valid.
Arguments:
Offset - Specifies the offset to the node
Return Value:
The pointer to the node.
--*/
{
PKEYSTRUCT KeyStruct;
#ifdef DEBUG
if (Offset == INVALID_OFFSET) {
DEBUGMSG ((DBG_ERROR, "Invalid root accessed in pGetKeyStructWithProxy at offset %u", Offset));
return NULL;
}
if (!g_db->Buf) {
DEBUGMSG ((DBG_ERROR, "Attempt to access non-existent buffer at %u", Offset));
return NULL;
}
if (Offset > g_db->End) {
DEBUGMSG ((DBG_ERROR, "Access beyond length of buffer in pGetKeyStructWithProxy (offset %u)", Offset));
return NULL;
}
#endif
KeyStruct = (PKEYSTRUCT) (g_db->Buf + Offset);
#ifdef DEBUG
if (!g_UseDebugStructs) {
KeyStruct = (PKEYSTRUCT) (g_db->Buf + Offset - sizeof (DWORD));
} else if (KeyStruct->Signature != SIGNATURE) {
DEBUGMSG ((DBG_ERROR, "Signature does not match in pGetKeyStructWithProxy at offset %u!", Offset));
return NULL;
}
#endif
if (KeyStruct->Flags & KSF_PROXY_NODE) {
return pGetKeyStructWithProxy (KeyStruct->dwValue);
}
return KeyStruct;
}
PKEYSTRUCT
GetKeyStruct (
IN DWORD Offset
)
/*++
Routine Description:
GetKeyStruct returns a pointer given an offset. It does not support proxy
nodes, so the rest of memdb accesses the unaltered tree. The debug version
checks the signature and validity of each offset. It is assumed that Offset
is always valid.
Arguments:
Offset - Specifies the offset to the node
Return Value:
The pointer to the node.
--*/
{
PKEYSTRUCT KeyStruct;
#ifdef DEBUG
if (Offset == INVALID_OFFSET) {
DEBUGMSG ((DBG_ERROR, "Invalid root accessed in GetKeyStruct at offset %u", Offset));
return NULL;
}
if (!g_db->Buf) {
DEBUGMSG ((DBG_ERROR, "Attempt to access non-existent buffer at %u", Offset));
return NULL;
}
if (Offset > g_db->End) {
DEBUGMSG ((DBG_ERROR, "Access beyond length of buffer in GetKeyStruct (offset %u)", Offset));
return NULL;
}
#endif
KeyStruct = (PKEYSTRUCT) (g_db->Buf + Offset);
#ifdef DEBUG
if (!g_UseDebugStructs) {
KeyStruct = (PKEYSTRUCT) (g_db->Buf + Offset - sizeof (DWORD));
} else if (KeyStruct->Signature != SIGNATURE) {
DEBUGMSG ((DBG_ERROR, "Signature does not match in GetKeyStruct at offset %u!", Offset));
return NULL;
}
#endif
return KeyStruct;
}
DWORD
FindKeyStruct (
IN DWORD RootOffset,
IN PCWSTR KeyName
)
/*++
Routine Description:
FindKeyStruct takes a key name and looks for the
offset in the tree specified by RootOffset. The key
name must not contain backslashes.
Arguments:
RootOffset - An offset to the root of the level
KeyName - The name of the key to find in the binary tree
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
PKEYSTRUCT KeyStruct;
int cmp;
//
// Walk the binary tree looking for KeyName
//
while (RootOffset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (RootOffset);
cmp = StringICompareW (KeyName, GetKeyToken (KeyStruct->KeyToken));
if (!cmp) {
break;
}
if (cmp < 0) {
RootOffset = KeyStruct->Left;
} else {
RootOffset = KeyStruct->Right;
}
}
return RootOffset;
}
DWORD
GetFirstOffset (
IN DWORD RootOffset
)
/*++
Routine Description:
GetFirstOffset walks down the left side of the binary tree
pointed to by RootOffset, and returns the left-most node.
Arguments:
RootOffset - An offset to the root of the level
Return Value:
An offset to the leftmost structure, or INVALID_OFFSET if the
root was invalid.
--*/
{
PKEYSTRUCT KeyStruct;
if (RootOffset == INVALID_OFFSET) {
return INVALID_OFFSET;
}
//
// Go to leftmost node of root
//
KeyStruct = GetKeyStruct (RootOffset);
while (KeyStruct->Left != INVALID_OFFSET) {
RootOffset = KeyStruct->Left;
KeyStruct = GetKeyStruct (RootOffset);
}
return RootOffset;
}
DWORD
GetNextOffset (
IN DWORD NodeOffset
)
/*++
Routine Description:
GetNextOffset traverses the binary tree in order. This
technique relies on parent links to traverse without a
stack or recursion.
Arguments:
NodeOffset - Offset to a node in the tree, usually the
return value from GetFirstOffset or GetNextOffset.
Return Value:
An offset to the next structure, or INVALID_OFFSET if the
end is reached.
--*/
{
PKEYSTRUCT KeyStruct;
DWORD Last;
KeyStruct = GetKeyStruct (NodeOffset);
//
// If right child exist, go to leftmost node of right child
//
if (KeyStruct->Right != INVALID_OFFSET) {
//
// Go to right child
//
NodeOffset = KeyStruct->Right;
//
// Go to left-most of right child
//
KeyStruct = GetKeyStruct (NodeOffset);
while (KeyStruct->Left != INVALID_OFFSET) {
NodeOffset = KeyStruct->Left;
KeyStruct = GetKeyStruct (NodeOffset);
}
}
//
// Else move up to parent
//
else {
//
// Climb to top of processed nodes
//
do {
Last = NodeOffset;
NodeOffset = KeyStruct->Parent;
if (NodeOffset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (NodeOffset);
} else {
break; // reached the root of tree
}
} while (Last == KeyStruct->Right);
}
return NodeOffset;
}
DWORD
pFindPatternKeyStruct (
IN DWORD RootOffset,
IN DWORD NodeOffset,
IN PCWSTR KeyName
)
/*++
Routine Description:
pFindPatternKeyStruct takes a key name and looks for the
offset in the tree specified by RootOffset. The key name must
not contain backslashes, and the stored key name is
treated as a pattern.
Arguments:
RootOffset - An offset to the root of the level
NodeOffset - The previous return value from pFindPatternKeyStruct
(for enumeration) or INVALID_OFFSET for the first
call.
KeyName - The name of the key to find in the binary tree
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
PKEYSTRUCT KeyStruct;
//
// if NodeOffset is invalid, this is the first search item
//
if (NodeOffset == INVALID_OFFSET) {
NodeOffset = GetFirstOffset (RootOffset);
}
//
// otherwise advance NodeOffset
//
else {
NodeOffset = GetNextOffset (NodeOffset);
}
//
// Examine key as a pattern, then go to next node
//
while (NodeOffset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (NodeOffset);
// Compare key (string in KeyStruct->KeyToken is the pattern)
if (IsPatternMatchW (GetKeyToken (KeyStruct->KeyToken), KeyName)) {
return NodeOffset;
}
// No match yet - go to next node
NodeOffset = GetNextOffset (NodeOffset);
}
return INVALID_OFFSET;
}
DWORD
pFindKeyStructUsingPattern (
IN DWORD RootOffset,
IN DWORD NodeOffset,
IN PCWSTR PatternStr
)
/*++
Routine Description:
pFindKeyStructUsingPattern takes a key pattern and looks
for the offset in the tree specified by RootOffset. The key
name must not contain backslashes, but can contain wildcards.
Arguments:
RootOffset - An offset to the root of the level
NodeOffset - The previous return value from pFindPatternKeyStruct
(for enumeration) or INVALID_OFFSET for the first
call.
KeyName - The name of the key to find in the binary tree
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
PKEYSTRUCT KeyStruct;
// if NodeOffset is invalid, this is the first search item
if (NodeOffset == INVALID_OFFSET) {
NodeOffset = GetFirstOffset (RootOffset);
}
// otherwise advance NodeOffset
else {
NodeOffset = GetNextOffset (NodeOffset);
}
//
// Examine key as a pattern, then go to next node
//
while (NodeOffset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (NodeOffset);
// Compare key
if (IsPatternMatchW (PatternStr, GetKeyToken (KeyStruct->KeyToken))) {
return NodeOffset;
}
// No match yet - go to next node
NodeOffset = GetNextOffset (NodeOffset);
}
return INVALID_OFFSET;
}
DWORD
pFindPatternKeyStructUsingPattern (
IN DWORD RootOffset,
IN DWORD NodeOffset,
IN PCWSTR PatternStr
)
/*++
Routine Description:
pFindPatternKeyStructUsingPattern takes a key pattern and looks
for the offset in the tree specified by RootOffset. The key name
must not contain backslashes, but can contain wildcards. The
wildcards in the stored key are processed as well.
Arguments:
RootOffset - An offset to the root of the level
NodeOffset - The previous return value from pFindPatternKeyStruct
(for enumeration) or INVALID_OFFSET for the first
call.
KeyName - The name of the key to find in the binary tree
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
PKEYSTRUCT KeyStruct;
// if NodeOffset is invalid, this is the first search item
if (NodeOffset == INVALID_OFFSET) {
NodeOffset = GetFirstOffset (RootOffset);
}
// otherwise advance NodeOffset
else {
NodeOffset = GetNextOffset (NodeOffset);
}
//
// Examine key as a pattern, then go to next node
//
while (NodeOffset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (NodeOffset);
// Compare key (PatternStr is the pattern)
if (IsPatternMatchW (PatternStr, GetKeyToken (KeyStruct->KeyToken))) {
return NodeOffset;
}
// Compare key (string in KeyStruct->KeyToken is the pattern)
if (IsPatternMatchW (GetKeyToken (KeyStruct->KeyToken), PatternStr)) {
return NodeOffset;
}
// No match yet - go to next node
NodeOffset = GetNextOffset (NodeOffset);
}
return INVALID_OFFSET;
}
DWORD
FindKey (
IN PCWSTR FullKeyPath
)
/*++
Routine Description:
FindKey locates a complete key string and returns
the offset to the KEYSTRUCT, or INVALID_OFFSET if
the key path does not exist. The FullKeyPath
must supply the complete path to the KEYSTRUCT.
Arguments:
FullKeyPath - A backslash-delimited key path to a value
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
return FindStringInHashTable (FullKeyPath, NULL);
}
DWORD
FindPatternKey (
IN DWORD RootOffset,
IN PCWSTR FullKeyPath,
IN BOOL EndPatternAllowed
)
/*++
Routine Description:
FindPatternKey locates a complete key string and returns
the offset to the KEYSTRUCT, or INVALID_OFFSET if the
key path does not exist. Each stored part of the key is
treated as a pattern, and FullKeyPath must supply the
complete path to the KEYSTRUCT without wildcards.
Arguments:
RootOffset - An offset to the level's binary tree root
FullKeyPath - A backslash-delimited key path to a value
without wildcards.
EndPatternAllowed - Specifies TRUE if the stored pattern can
have an asterisk at the end, to indicate
any subkeys, or FALSE if the pattern matches
on the same level only.
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
WCHAR Path[MEMDB_MAX + 1];
PWSTR p;
PCWSTR End;
//
// Divide the string into a multi-sz
//
StringCopyW (Path, FullKeyPath);
for (p = Path ; *p ; p++) {
if (*p == L'\\') {
*p = 0;
}
}
End = p;
if (End > Path && *(End - 1) == 0) {
//
// Special case: the wack was at the end of the string.
// Therefore, inc End so we test that final empty value.
//
End++;
}
if (End == Path) {
DEBUGMSG ((DBG_ERROR, "FindPatternKey: Empty key not allowed"));
return INVALID_OFFSET;
}
//
// Now test the key against all stored patterns
//
return pFindPatternKeyWorker (Path, End, RootOffset, EndPatternAllowed);
}
DWORD
pFindPatternKeyWorker (
IN PCWSTR SubKey,
IN PCWSTR End,
IN DWORD RootOffset,
IN BOOL EndPatternAllowed
)
{
DWORD Offset;
PCWSTR NextSubKey;
DWORD MatchOffset;
PKEYSTRUCT KeyStruct;
NextSubKey = GetEndOfString (SubKey) + 1;
// Begin an enumeration of the matches
Offset = pFindPatternKeyStruct (RootOffset, INVALID_OFFSET, SubKey);
while (Offset != INVALID_OFFSET) {
//
// Is there more in the caller's key string to test?
//
if (NextSubKey < End) {
//
// Yes, call pFindPatternKeyWorker recursively
//
MatchOffset = pFindPatternKeyWorker (
NextSubKey,
End,
GetKeyStruct (Offset)->NextLevelRoot,
EndPatternAllowed
);
if (MatchOffset != INVALID_OFFSET) {
//
// We found one match. There may be others, but
// we return this one.
//
return MatchOffset;
}
} else {
//
// No, if this is an endpoint, return the match.
//
KeyStruct = GetKeyStruct (Offset);
if (KeyStruct->Flags & KSF_ENDPOINT) {
return Offset;
}
}
// Continue enumeration
Offset = pFindPatternKeyStruct (RootOffset, Offset, SubKey);
}
//
// The normal search failed. Now we test for an endpoint that has
// just an asterisk. If we find one, we return it as our match.
// This only applies when we have more subkeys, and EndPatternAllowed
// is TRUE.
//
if (NextSubKey < End && EndPatternAllowed) {
// Begin another enumeration of the matches
Offset = pFindPatternKeyStruct (RootOffset, INVALID_OFFSET, SubKey);
while (Offset != INVALID_OFFSET) {
//
// If EndPatternAllowed is TRUE, then test this offset
// for an exact match with an asterisk.
//
KeyStruct = GetKeyStruct (Offset);
if (KeyStruct->Flags & KSF_ENDPOINT) {
if (StringMatchW (GetKeyToken (KeyStruct->KeyToken), L"*")) {
return Offset;
}
}
// Continue enumeration
Offset = pFindPatternKeyStruct (RootOffset, Offset, SubKey);
}
}
//
// No match was found
//
return INVALID_OFFSET;
}
DWORD
FindKeyUsingPattern (
IN DWORD RootOffset,
IN PCWSTR FullKeyPath
)
/*++
Routine Description:
FindKeyUsingPattern locates a key string using a pattern
and returns the offset to the KEYSTRUCT, or INVALID_OFFSET
if the key path does not exist. Each part of the stored key
is treated as a literal string.
Arguments:
RootOffset - An offset to the level's binary tree root
FullKeyPath - A backslash-delimited key path to a value
with optional wildcards.
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
WCHAR Path[MEMDB_MAX];
PWSTR p;
PWSTR Start, End;
DWORD Offset, NextLevelOffset;
StringCopyW (Path, FullKeyPath);
End = Path;
//
// Split string at backslash
//
Start = End;
p = wcschr (End, '\\');
if (p) {
End = _wcsinc (p);
*p = 0;
} else {
End = NULL;
}
//
// Look at this level for the very first key
//
Offset = pFindKeyStructUsingPattern (RootOffset, INVALID_OFFSET, Start);
//
// If this is the last level, we may have found the key!
//
if (!End) {
while (Offset != INVALID_OFFSET) {
if (GetKeyStruct (Offset)->Flags & KSF_ENDPOINT) {
return Offset;
}
Offset = pFindKeyStructUsingPattern (RootOffset, Offset, Start);
}
}
//
// Otherwise recursively examine next level
//
while (Offset != INVALID_OFFSET) {
//
// Look at all subkeys for a match
//
NextLevelOffset = GetKeyStruct (Offset)->NextLevelRoot;
NextLevelOffset = FindKeyUsingPattern (NextLevelOffset, End);
//
// When the recursive search succeeded, propagate the return value
//
if (NextLevelOffset != INVALID_OFFSET) {
return NextLevelOffset;
}
//
// No match, continue looking in this level for another match
//
Offset = pFindKeyStructUsingPattern (RootOffset, Offset, Start);
}
return INVALID_OFFSET;
}
DWORD
FindPatternKeyUsingPattern (
IN DWORD RootOffset,
IN PCWSTR FullKeyPath
)
/*++
Routine Description:
pFindPatternKeyUsingPattern locates a patterned key string
using a pattern and returns the offset to the KEYSTRUCT,
or INVALID_OFFSET if the key path does not exist. Each
part of the key is treated as a pattern.
Arguments:
RootOffset - An offset to the level's binary tree root
FullKeyPath - A backslash-delimited key path to a value
with optional wildcards.
Return Value:
An offset to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
WCHAR Path[MEMDB_MAX];
PWSTR p;
PWSTR Start, End;
DWORD Offset, NextLevelOffset;
StringCopyW (Path, FullKeyPath);
End = Path;
// Split string at backslash
Start = End;
p = wcschr (End, L'\\');
if (p) {
End = p + 1;
*p = 0;
}
else
End = NULL;
// Look at this level for the very first key
Offset = pFindPatternKeyStructUsingPattern (RootOffset, INVALID_OFFSET, Start);
// If this is the last level, we may have found the key!
if (!End) {
while (Offset != INVALID_OFFSET) {
if (GetKeyStruct (Offset)->Flags & KSF_ENDPOINT)
return Offset;
Offset = pFindPatternKeyStructUsingPattern (RootOffset, Offset, Start);
}
}
// Otherwise recursively examine next level
while (Offset != INVALID_OFFSET) {
// Look at all subkeys for a match
NextLevelOffset = GetKeyStruct (Offset)->NextLevelRoot;
NextLevelOffset = FindPatternKeyUsingPattern (NextLevelOffset, End);
// When the recursive search succeeded, propagate the return value
if (NextLevelOffset != INVALID_OFFSET)
return NextLevelOffset;
// No match, continue looking in this level for another match
Offset = pFindPatternKeyStructUsingPattern (RootOffset, Offset, Start);
}
return INVALID_OFFSET;
}
DWORD
pNewKey (
IN PCWSTR KeyStr,
IN PCWSTR KeyStrWithHive,
IN BOOL Endpoint
)
/*++
Routine Description:
NewKey allocates a key struct off our heap, and links it into the binary
tree. KeyStr must be a full key path, and any part of the path that does
not exist will be created. KeyStr must not already exist (though parts
of it can exist).
Arguments:
KeyStr - The full path to the value, separated by backslashes.
Each string between backslashes will cause a key
struct to be allocated and linked. Some of the
structs may already have been allocated.
KeyStrWithHive - The full path to the value, plus the hive
prefix (if any). Can be the same as KeyStr
if there is no hive prefix.
Endpoint - Specifies TRUE if new node is an endpoint, or FALSE if
it is not.
Return Value:
An offset to the last node of the new structure, or
INVALID_OFFSET if the key could not be allocated.
--*/
{
WCHAR Path[MEMDB_MAX];
PWSTR p;
PWSTR Start, End;
DWORD Offset, ThisLevelRoot;
PDWORD ParentOffsetPtr;
PKEYSTRUCT KeyStruct;
DWORD LastLevel;
BOOL NewNodeCreated = FALSE;
StringCopyW (Path, KeyStr);
End = Path;
ThisLevelRoot = g_db->FirstLevelRoot;
ParentOffsetPtr = &g_db->FirstLevelRoot;
LastLevel = INVALID_OFFSET;
do {
// Split string at backslash
Start = End;
p = wcschr (End, L'\\');
if (p) {
End = p + 1;
*p = 0;
}
else
End = NULL;
// Look in tree for key
if (!NewNodeCreated) {
Offset = FindKeyStruct (ThisLevelRoot, Start);
} else {
Offset = INVALID_OFFSET;
}
if (Offset == INVALID_OFFSET) {
// Add a new key if it was not found
Offset = pAllocKeyStruct (ParentOffsetPtr, Start, LastLevel);
if (Offset == INVALID_OFFSET) {
return Offset;
}
NewNodeCreated = TRUE;
}
// Continue to next level
KeyStruct = GetKeyStruct (Offset);
LastLevel = Offset;
ThisLevelRoot = KeyStruct->NextLevelRoot;
ParentOffsetPtr = &KeyStruct->NextLevelRoot;
} while (End);
if (Endpoint) {
if (!(KeyStruct->Flags & KSF_ENDPOINT)) {
NewNodeCreated = TRUE;
}
KeyStruct->Flags |= KSF_ENDPOINT;
if (NewNodeCreated) {
AddHashTableEntry (KeyStr, Offset);
}
}
return Offset;
}
DWORD
NewKey (
IN PCWSTR KeyStr,
IN PCWSTR KeyStrWithHive
)
{
return pNewKey (KeyStr, KeyStrWithHive, TRUE);
}
VOID
DeleteKey (
IN PCWSTR KeyStr,
IN OUT PDWORD RootPtr,
IN BOOL MustMatch
)
/*++
Routine Description:
DeleteKey takes a key path and puts the key struct in the deleted
block chain. Any sub-levels are deleted as well. Optionally,
the binary tree in which the key participates in may be updated.
Arguments:
KeyStr - The full path to the value, separated by backslashes.
RootPtr - A pointer to the level's binary tree root variable.
If necessary, the variable is updated.
MustMatch - A flag indicating if the delete only applies to
end points or if any matching struct is to be deleted.
TRUE indicates only endpoints can be deleted.
Return Value:
none
--*/
{
WCHAR Path[MEMDB_MAX];
PWSTR p;
PWSTR Start, End;
DWORD Offset;
DWORD NextOffset;
PKEYSTRUCT KeyStruct;
INCSTAT(g_Deletions);
StringCopyW (Path, KeyStr);
End = Path;
//
// Split string at backslash
//
Start = End;
p = wcschr (End, L'\\');
if (p) {
End = _wcsinc (p);
*p = 0;
} else {
End = NULL;
}
//
// Look at this level for the very first key
//
Offset = pFindKeyStructUsingPattern (*RootPtr, INVALID_OFFSET, Start);
//
// If this is the last level, delete the matching keys
// (may need to be endpoints if MustMatch is TRUE)
//
if (!End) {
while (Offset != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (Offset);
NextOffset = pFindKeyStructUsingPattern (*RootPtr, Offset, Start);
//
// If must match and lower levels exist, don't delete, just turn
// off the endpoint flag
//
if (MustMatch && KeyStruct->NextLevelRoot != INVALID_OFFSET) {
// Call to clean up, not to delink or recurse
pDeallocKeyStruct (Offset, RootPtr, FALSE, TRUE);
}
//
// Else delete the struct if an endpoint or don't care about
// endpoints
//
else if (!MustMatch || (KeyStruct->Flags & KSF_ENDPOINT)) {
// Call to free the entire key struct and all children
pDeallocKeyStruct (Offset, RootPtr, TRUE, FALSE);
}
Offset = NextOffset;
}
}
//
// Otherwise recursively examine next level for each match
//
else {
while (Offset != INVALID_OFFSET) {
//
// Delete all matching subkeys
//
NextOffset = pFindKeyStructUsingPattern (*RootPtr, Offset, Start);
DeleteKey (End, &GetKeyStruct (Offset)->NextLevelRoot, MustMatch);
//
// If this is not an endpoint and has no children, delete it
//
KeyStruct = GetKeyStruct (Offset);
if (KeyStruct->NextLevelRoot == INVALID_OFFSET &&
!(KeyStruct->Flags & KSF_ENDPOINT)
) {
// Call to free the entire key struct
pDeallocKeyStruct (Offset, RootPtr, TRUE, FALSE);
}
//
// Continue looking in this level for another match
//
Offset = NextOffset;
}
}
}
VOID
CopyValToPtr (
PKEYSTRUCT KeyStruct,
PDWORD ValPtr
)
{
if (ValPtr) {
if (!(KeyStruct->Flags & KSF_BINARY)) {
*ValPtr = KeyStruct->dwValue;
} else {
*ValPtr = 0;
}
}
}
VOID
CopyFlagsToPtr (
PKEYSTRUCT KeyStruct,
PDWORD ValPtr
)
{
if (ValPtr) {
*ValPtr = KeyStruct->Flags & KSF_USERFLAG_MASK;
}
}
BOOL
PrivateBuildKeyFromOffset (
IN DWORD StartLevel, // zero-based
IN DWORD TailOffset,
OUT PWSTR Buffer, OPTIONAL
OUT PDWORD ValPtr, OPTIONAL
OUT PDWORD UserFlagsPtr, OPTIONAL
OUT PDWORD Chars OPTIONAL
)
/*++
Routine Description:
PrivateBuildKeyFromOffset generates the key string given an offset. The
caller can specify the start level to skip root nodes. It is assumed that
TailOffset is always valid.
Arguments:
StartLevel - Specifies the zero-based level to begin building the key
string. This is used to skip the root portion of the key
string.
TailOffset - Specifies the offset to the last level of the key string.
Buffer - Receives the key string, must be able to hold MEMDB_MAX
characters.
ValPtr - Receives the key's value
UserFlagsPtr - Receives the user flags
Chars - Receives the number of characters in Buffer
Return Value:
TRUE if the key was build properly, FALSE otherwise.
--*/
{
static DWORD Offsets[MEMDB_MAX];
PKEYSTRUCT KeyStruct;
DWORD CurrentOffset;
DWORD OffsetEnd;
DWORD OffsetStart;
register PWSTR p;
register PCWSTR s;
//
// Build string
//
OffsetEnd = MEMDB_MAX;
OffsetStart = OffsetEnd;
CurrentOffset = TailOffset;
while (CurrentOffset != INVALID_OFFSET) {
//
// Record offset
//
OffsetStart--;
Offsets[OffsetStart] = CurrentOffset;
//
// Dec for start level and go to parent
//
CurrentOffset = pGetKeyStructWithProxy (CurrentOffset)->PrevLevelNode;
}
//
// Filter for "string is not long enough"
//
OffsetStart += StartLevel;
if (OffsetStart >= OffsetEnd) {
return FALSE;
}
//
// Transfer node's value and flags to caller's variables
//
CopyValToPtr (pGetKeyStructWithProxy (TailOffset), ValPtr);
CopyFlagsToPtr (pGetKeyStructWithProxy (TailOffset), UserFlagsPtr);
//
// Copy each piece of the string to Buffer and calculate character count
//
if (Buffer) {
p = Buffer;
for (CurrentOffset = OffsetStart ; CurrentOffset < OffsetEnd ; CurrentOffset++) {
KeyStruct = pGetKeyStructWithProxy (Offsets[CurrentOffset]);
s = GetKeyToken (KeyStruct->KeyToken);
while (*s) {
*p++ = *s++;
}
*p++ = L'\\';
}
p--;
*p = 0;
if (Chars) {
*Chars = (p - Buffer) / sizeof (WCHAR);
}
} else if (Chars) {
*Chars = 0;
for (CurrentOffset = OffsetStart ; CurrentOffset < OffsetEnd ; CurrentOffset++) {
KeyStruct = pGetKeyStructWithProxy (Offsets[CurrentOffset]);
*Chars += wcslen(GetKeyToken (KeyStruct->KeyToken)) + 1;
}
*Chars -= 1;
}
return TRUE;
}
UINT
pComputeTokenHash (
IN PCWSTR KeyName
)
{
UINT hash;
hash = 0;
while (*KeyName) {
hash = (hash << 1) ^ (*KeyName++);
}
return hash % TOKENBUCKETS;
}
DWORD
pFindKeyToken (
IN PCWSTR KeyName,
OUT PUINT Hash
)
{
DWORD offset;
PTOKENSTRUCT tokenStruct;
INT cmp;
*Hash = pComputeTokenHash (KeyName);
offset = g_db->TokenBuckets[*Hash];
while (offset != INVALID_OFFSET) {
tokenStruct = (PTOKENSTRUCT) (g_db->Buf + offset);
if (StringMatchW (tokenStruct->String, KeyName)) {
break;
}
offset = tokenStruct->Right;
}
return offset;
}
DWORD
pAllocKeyToken (
IN PCWSTR KeyName,
OUT PINT AdjustFactor
)
{
PTOKENSTRUCT tokenStruct;
PTOKENSTRUCT tokenParent;
DWORD tokenOffset;
UINT size;
PDWORD parentToChildLink;
DWORD newNodeParentOffset;
DWORD pivotPoint;
INT cmp;
UINT hash;
//
// Use existing token first
//
tokenOffset = pFindKeyToken (KeyName, &hash);
if (tokenOffset != INVALID_OFFSET) {
*AdjustFactor = 0;
return tokenOffset;
}
//
// Existing token does not exist -- allocate a new one
//
size = sizeof (TOKENSTRUCT) + SizeOfStringW (KeyName);
tokenStruct = (PTOKENSTRUCT) pAllocMemoryFromDb (
size,
&tokenOffset,
AdjustFactor
);
tokenStruct->Right = g_db->TokenBuckets[hash];
StringCopyW (tokenStruct->String, KeyName);
g_db->TokenBuckets[hash] = tokenOffset;
return tokenOffset;
}
VOID
pDeallocToken (
IN DWORD Token
)
{
return;
}
PCWSTR
GetKeyToken (
IN DWORD Token
)
{
PTOKENSTRUCT tokenStruct;
tokenStruct = (PTOKENSTRUCT) (g_db->Buf + Token);
return tokenStruct->String;
}