mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
1880 lines
56 KiB
C
1880 lines
56 KiB
C
/*
|
|
* PROJECT: ReactOS Registry Library
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: lib/cmlib/cmindex.c
|
|
* PURPOSE: Configuration Manager - Cell Indexes
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include "cmlib.h"
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
#define INVALID_INDEX 0x80000000
|
|
|
|
#define CmpMaxFastIndexPerHblock \
|
|
((HBLOCK_SIZE - (sizeof(HBIN) + sizeof(HCELL) + \
|
|
FIELD_OFFSET(CM_KEY_FAST_INDEX, List))) / sizeof(CM_INDEX))
|
|
|
|
#define CmpMaxIndexPerHblock \
|
|
((HBLOCK_SIZE - (sizeof(HBIN) + sizeof(HCELL) + \
|
|
FIELD_OFFSET(CM_KEY_INDEX, List))) / sizeof(HCELL_INDEX) - 1)
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
LONG
|
|
NTAPI
|
|
CmpDoCompareKeyName(IN PHHIVE Hive,
|
|
IN PCUNICODE_STRING SearchName,
|
|
IN HCELL_INDEX Cell)
|
|
{
|
|
PCM_KEY_NODE Node;
|
|
UNICODE_STRING KeyName;
|
|
LONG Result;
|
|
|
|
/* Get the node */
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|
if (!Node) return 2;
|
|
|
|
/* Check if it's compressed */
|
|
if (Node->Flags & KEY_COMP_NAME)
|
|
{
|
|
/* Compare compressed names */
|
|
Result = CmpCompareCompressedName(SearchName,
|
|
Node->Name,
|
|
Node->NameLength);
|
|
}
|
|
else
|
|
{
|
|
/* Compare the Unicode name directly */
|
|
KeyName.Buffer = Node->Name;
|
|
KeyName.Length = Node->NameLength;
|
|
KeyName.MaximumLength = KeyName.Length;
|
|
Result = RtlCompareUnicodeString(SearchName, &KeyName, TRUE);
|
|
}
|
|
|
|
/* Release the cell and return the normalized result */
|
|
HvReleaseCell(Hive, Cell);
|
|
return (Result == 0) ? Result : ((Result > 0) ? 1 : -1);
|
|
}
|
|
|
|
LONG
|
|
NTAPI
|
|
CmpCompareInIndex(IN PHHIVE Hive,
|
|
IN PCUNICODE_STRING SearchName,
|
|
IN ULONG Count,
|
|
IN PCM_KEY_INDEX Index,
|
|
IN PHCELL_INDEX SubKey)
|
|
{
|
|
PCM_KEY_FAST_INDEX FastIndex;
|
|
PCM_INDEX FastEntry;
|
|
LONG Result;
|
|
ULONG i;
|
|
ULONG ActualNameLength = 4, CompareLength, NameLength;
|
|
WCHAR p, pp;
|
|
|
|
/* Assume failure */
|
|
*SubKey = HCELL_NIL;
|
|
|
|
/* Check if we are a fast or hashed leaf */
|
|
if ((Index->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Index->Signature == CM_KEY_HASH_LEAF))
|
|
{
|
|
/* Get the Fast/Hash Index */
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Index;
|
|
FastEntry = &FastIndex->List[Count];
|
|
|
|
/* Check if we are a hash leaf, in which case we skip all this */
|
|
if (Index->Signature == CM_KEY_FAST_LEAF)
|
|
{
|
|
/* Find out just how much of the name is there */
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
/* Check if this entry is empty */
|
|
if (!FastEntry->NameHint[i])
|
|
{
|
|
/* Only this much! */
|
|
ActualNameLength = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* How large is the name and how many characters to compare */
|
|
NameLength = SearchName->Length / sizeof(WCHAR);
|
|
CompareLength = min(NameLength, ActualNameLength);
|
|
|
|
/* Loop all the chars we'll test */
|
|
for (i = 0; i < CompareLength; i++)
|
|
{
|
|
/* Get one char from each buffer */
|
|
p = SearchName->Buffer[i];
|
|
pp = FastEntry->NameHint[i];
|
|
|
|
/* See if they match and return result if they don't */
|
|
Result = (LONG)RtlUpcaseUnicodeChar(p) -
|
|
(LONG)RtlUpcaseUnicodeChar(pp);
|
|
if (Result) return (Result > 0) ? 1 : -1;
|
|
}
|
|
}
|
|
|
|
/* If we got here then we have to do a full compare */
|
|
Result = CmpDoCompareKeyName(Hive, SearchName, FastEntry->Cell);
|
|
if (Result == 2) return Result;
|
|
if (!Result) *SubKey = FastEntry->Cell;
|
|
}
|
|
else
|
|
{
|
|
/* We aren't, so do a name compare and return the subkey found */
|
|
Result = CmpDoCompareKeyName(Hive, SearchName, Index->List[Count]);
|
|
if (Result == 2) return Result;
|
|
if (!Result) *SubKey = Index->List[Count];
|
|
}
|
|
|
|
/* Return the comparison result */
|
|
return Result;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
CmpFindSubKeyInRoot(IN PHHIVE Hive,
|
|
IN PCM_KEY_INDEX Index,
|
|
IN PCUNICODE_STRING SearchName,
|
|
IN PHCELL_INDEX SubKey)
|
|
{
|
|
ULONG High, Low = 0, i = 0, ReturnIndex;
|
|
HCELL_INDEX LeafCell;
|
|
PCM_KEY_INDEX Leaf;
|
|
LONG Result;
|
|
|
|
/* Verify Index for validity */
|
|
ASSERT(Index->Count != 0);
|
|
ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);
|
|
|
|
/* Set high limit and loop */
|
|
High = Index->Count - 1;
|
|
while (TRUE)
|
|
{
|
|
/* Choose next entry */
|
|
i = ((High - Low) / 2) + Low;
|
|
|
|
/* Get the leaf cell and then the leaf itself */
|
|
LeafCell = Index->List[i];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (Leaf)
|
|
{
|
|
/* Make sure the leaf is valid */
|
|
ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_HASH_LEAF));
|
|
ASSERT(Leaf->Count != 0);
|
|
|
|
/* Do the compare */
|
|
Result = CmpCompareInIndex(Hive,
|
|
SearchName,
|
|
Leaf->Count - 1,
|
|
Leaf,
|
|
SubKey);
|
|
if (Result == 2) goto Big;
|
|
|
|
/* Check if we found the leaf */
|
|
if (!Result)
|
|
{
|
|
/* We found the leaf */
|
|
*SubKey = LeafCell;
|
|
ReturnIndex = i;
|
|
goto Return;
|
|
}
|
|
|
|
/* Check for negative result */
|
|
if (Result < 0)
|
|
{
|
|
/* If we got here, we should be at -1 */
|
|
ASSERT(Result == -1);
|
|
|
|
/* Do another lookup, since we might still be in the right leaf */
|
|
Result = CmpCompareInIndex(Hive,
|
|
SearchName,
|
|
0,
|
|
Leaf,
|
|
SubKey);
|
|
if (Result == 2) goto Big;
|
|
|
|
/* Check if it's not below */
|
|
if (Result >= 0)
|
|
{
|
|
/*
|
|
* If the name was first below, and now it is above,
|
|
* then this means that it is somewhere in this leaf.
|
|
* Make sure we didn't get some weird result
|
|
*/
|
|
ASSERT((Result == 1) || (Result == 0));
|
|
|
|
/* Return it */
|
|
*SubKey = LeafCell;
|
|
ReturnIndex = i;
|
|
goto Return;
|
|
}
|
|
|
|
/* Update the limit to this index, since we know it's not higher. */
|
|
High = i;
|
|
}
|
|
else
|
|
{
|
|
/* Update the base to this index, since we know it's not lower. */
|
|
Low = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Big:
|
|
/* This was some sort of special key */
|
|
ReturnIndex = INVALID_INDEX;
|
|
goto ReturnFailure;
|
|
}
|
|
|
|
/* Check if there is only one entry left */
|
|
if ((High - Low) <= 1) break;
|
|
|
|
/* Release the leaf cell */
|
|
HvReleaseCell(Hive, LeafCell);
|
|
}
|
|
|
|
/* Make sure we got here for the right reasons */
|
|
ASSERT((High - Low == 1) || (High == Low));
|
|
|
|
/* Get the leaf cell and the leaf */
|
|
LeafCell = Index->List[Low];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (!Leaf) goto Big;
|
|
|
|
/* Do the compare */
|
|
Result = CmpCompareInIndex(Hive,
|
|
SearchName,
|
|
Leaf->Count-1,
|
|
Leaf,
|
|
SubKey);
|
|
if (Result == 2) goto Big;
|
|
|
|
/* Check if we found it */
|
|
if (!Result)
|
|
{
|
|
/* We got lucky... return it */
|
|
*SubKey = LeafCell;
|
|
ReturnIndex = Low;
|
|
goto Return;
|
|
}
|
|
|
|
/* It's below, so could still be in this leaf */
|
|
if (Result < 0)
|
|
{
|
|
/* Make sure we're -1 */
|
|
ASSERT(Result == -1);
|
|
|
|
/* Do a search from the bottom */
|
|
Result = CmpCompareInIndex(Hive, SearchName, 0, Leaf, SubKey);
|
|
if (Result == 2) goto Big;
|
|
|
|
/*
|
|
* Check if it's above, which means that it's within the ranges of our
|
|
* leaf (since we were below before).
|
|
*/
|
|
if (Result >= 0)
|
|
{
|
|
/* Sanity check */
|
|
ASSERT((Result == 1) || (Result == 0));
|
|
|
|
/* Yep, so we're in the right leaf; return it. */
|
|
*SubKey = LeafCell;
|
|
ReturnIndex = Low;
|
|
goto Return;
|
|
}
|
|
|
|
/* It's still below us, so fail */
|
|
ReturnIndex = Low;
|
|
goto ReturnFailure;
|
|
}
|
|
|
|
/* Release the leaf cell */
|
|
HvReleaseCell(Hive, LeafCell);
|
|
|
|
/* Well the low didn't work too well, so try the high. */
|
|
LeafCell = Index->List[High];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (!Leaf) goto Big;
|
|
|
|
/* Do the compare */
|
|
Result = CmpCompareInIndex(Hive,
|
|
SearchName,
|
|
Leaf->Count - 1,
|
|
Leaf,
|
|
SubKey);
|
|
if (Result == 2) goto Big;
|
|
|
|
/* Check if we found it */
|
|
if (Result == 0)
|
|
{
|
|
/* We got lucky... return it */
|
|
*SubKey = LeafCell;
|
|
ReturnIndex = High;
|
|
goto Return;
|
|
}
|
|
|
|
/* Check if we are too high */
|
|
if (Result < 0)
|
|
{
|
|
/* Make sure we're -1 */
|
|
ASSERT(Result == -1);
|
|
|
|
/*
|
|
* Once again... since we were first too low and now too high, then
|
|
* this means we are within the range of this leaf... return it.
|
|
*/
|
|
*SubKey = LeafCell;
|
|
ReturnIndex = High;
|
|
goto Return;
|
|
}
|
|
|
|
/* If we got here, then we are too low, again. */
|
|
ReturnIndex = High;
|
|
|
|
/* Failure path */
|
|
ReturnFailure:
|
|
*SubKey = HCELL_NIL;
|
|
|
|
/* Return path...check if we have a leaf to free */
|
|
Return:
|
|
if (Leaf) HvReleaseCell(Hive, LeafCell);
|
|
|
|
/* Return the index */
|
|
return ReturnIndex;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
CmpFindSubKeyInLeaf(IN PHHIVE Hive,
|
|
IN PCM_KEY_INDEX Index,
|
|
IN PCUNICODE_STRING SearchName,
|
|
IN PHCELL_INDEX SubKey)
|
|
{
|
|
ULONG High, Low = 0, i;
|
|
LONG Result;
|
|
|
|
/* Verify Index for validity */
|
|
ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Index->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Index->Signature == CM_KEY_HASH_LEAF));
|
|
|
|
/* Get the upper bound and middle entry */
|
|
High = Index->Count - 1;
|
|
i = High / 2;
|
|
|
|
/* Check if we don't actually have any entries */
|
|
if (!Index->Count)
|
|
{
|
|
/* Return failure */
|
|
*SubKey = HCELL_NIL;
|
|
return 0;
|
|
}
|
|
|
|
/* Start compare loop */
|
|
while (TRUE)
|
|
{
|
|
/* Do the actual comparison and check the result */
|
|
Result = CmpCompareInIndex(Hive, SearchName, i, Index, SubKey);
|
|
if (Result == 2)
|
|
{
|
|
/* Fail with special value */
|
|
*SubKey = HCELL_NIL;
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
/* Check if we got lucky and found it */
|
|
if (!Result) return i;
|
|
|
|
/* Check if the result is below us */
|
|
if (Result < 0)
|
|
{
|
|
/* Set the new bound; it can't be higher then where we are now. */
|
|
ASSERT(Result == -1);
|
|
High = i;
|
|
}
|
|
else
|
|
{
|
|
/* Set the new bound... it can't be lower then where we are now. */
|
|
ASSERT(Result == 1);
|
|
Low = i;
|
|
}
|
|
|
|
/* Check if this is the last entry, if so, break out and handle it */
|
|
if ((High - Low) <= 1) break;
|
|
|
|
/* Set the new index */
|
|
i = ((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, SubKey);
|
|
if (Result == 2)
|
|
{
|
|
/* Fail with special value */
|
|
*SubKey = HCELL_NIL;
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
/* Check if we got lucky and found it */
|
|
if (!Result) return Low;
|
|
|
|
/* Check if the result is below us */
|
|
if (Result < 0)
|
|
{
|
|
/* Return the low entry */
|
|
ASSERT(Result == -1);
|
|
return Low;
|
|
}
|
|
|
|
/*
|
|
* If we got here, then just check the high and return it no matter what
|
|
* the result is (since we're a leaf, it has to be near there anyway).
|
|
*/
|
|
Result = CmpCompareInIndex(Hive, SearchName, High, Index, SubKey);
|
|
if (Result == 2)
|
|
{
|
|
/* Fail with special value */
|
|
*SubKey = HCELL_NIL;
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
/* Return the high */
|
|
return High;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
CmpComputeHashKey(IN ULONG Hash,
|
|
IN PCUNICODE_STRING Name,
|
|
IN BOOLEAN AllowSeparators)
|
|
{
|
|
LPWSTR Cp;
|
|
ULONG Value, i;
|
|
|
|
/* Make some sanity checks on our parameters */
|
|
ASSERT((Name->Length == 0) ||
|
|
(AllowSeparators) ||
|
|
(Name->Buffer[0] != OBJ_NAME_PATH_SEPARATOR));
|
|
|
|
/* If the name is empty, there is nothing to hash! */
|
|
if (!Name->Length) return Hash;
|
|
|
|
/* Set the buffer and loop every character */
|
|
Cp = Name->Buffer;
|
|
for (i = 0; i < Name->Length; i += sizeof(WCHAR), Cp++)
|
|
{
|
|
/* Make sure we don't have a separator when we shouldn't */
|
|
ASSERT(AllowSeparators || (*Cp != OBJ_NAME_PATH_SEPARATOR));
|
|
|
|
/* Check what kind of char we have */
|
|
if (*Cp >= L'a')
|
|
{
|
|
/* In the lower case region... is it truly lower case? */
|
|
if (*Cp < L'z')
|
|
{
|
|
/* Yes! Calculate it ourselves! */
|
|
Value = *Cp - L'a' + L'A';
|
|
}
|
|
else
|
|
{
|
|
/* No, use the API */
|
|
Value = RtlUpcaseUnicodeChar(*Cp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Reuse the char, it's already upcased */
|
|
Value = *Cp;
|
|
}
|
|
|
|
/* Multiply by a prime and add our value */
|
|
Hash *= 37;
|
|
Hash += Value;
|
|
}
|
|
|
|
/* Return the hash */
|
|
return Hash;
|
|
}
|
|
|
|
HCELL_INDEX
|
|
NTAPI
|
|
CmpDoFindSubKeyByNumber(IN PHHIVE Hive,
|
|
IN PCM_KEY_INDEX Index,
|
|
IN ULONG Number)
|
|
{
|
|
ULONG i;
|
|
HCELL_INDEX LeafCell = 0;
|
|
PCM_KEY_INDEX Leaf = NULL;
|
|
PCM_KEY_FAST_INDEX FastIndex;
|
|
HCELL_INDEX Result;
|
|
|
|
/* Check if this is a root */
|
|
if (Index->Signature == CM_KEY_INDEX_ROOT)
|
|
{
|
|
/* Loop the index */
|
|
for (i = 0; i < Index->Count; i++)
|
|
{
|
|
/* Check if this isn't the first iteration */
|
|
if (i)
|
|
{
|
|
/* Make sure we had something valid, and release it */
|
|
ASSERT(Leaf != NULL );
|
|
ASSERT(LeafCell == Index->List[i - 1]);
|
|
HvReleaseCell(Hive, LeafCell);
|
|
}
|
|
|
|
/* Get the leaf cell and the leaf for this index */
|
|
LeafCell = Index->List[i];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (!Leaf) return HCELL_NIL;
|
|
|
|
/* Check if the index may be inside it */
|
|
if (Number < Leaf->Count)
|
|
{
|
|
/* Check if this is a fast or hash leaf */
|
|
if ((Leaf->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_HASH_LEAF))
|
|
{
|
|
/* Get the fast index */
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
|
|
|
|
/* Look inside the list to get our actual cell */
|
|
Result = FastIndex->List[Number].Cell;
|
|
HvReleaseCell(Hive, LeafCell);
|
|
return Result;
|
|
}
|
|
else
|
|
{
|
|
/* Regular leaf, so just use the index directly */
|
|
Result = Leaf->List[Number];
|
|
|
|
/* Release and return it */
|
|
HvReleaseCell(Hive,LeafCell);
|
|
return Result;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, skip this leaf */
|
|
Number = Number - Leaf->Count;
|
|
}
|
|
}
|
|
|
|
/* Should never get here */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* If we got here, then the cell is in this index */
|
|
ASSERT(Number < Index->Count);
|
|
|
|
/* Check if we're a fast or hash leaf */
|
|
if ((Index->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Index->Signature == CM_KEY_HASH_LEAF))
|
|
{
|
|
/* We are, get the fast index and get the cell from the list */
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Index;
|
|
return FastIndex->List[Number].Cell;
|
|
}
|
|
else
|
|
{
|
|
/* We aren't, so use the index directly to grab the cell */
|
|
return Index->List[Number];
|
|
}
|
|
}
|
|
|
|
HCELL_INDEX
|
|
NTAPI
|
|
CmpFindSubKeyByNumber(IN PHHIVE Hive,
|
|
IN PCM_KEY_NODE Node,
|
|
IN ULONG Number)
|
|
{
|
|
PCM_KEY_INDEX Index;
|
|
HCELL_INDEX Result = HCELL_NIL;
|
|
|
|
/* Check if it's in the stable list */
|
|
if (Number < Node->SubKeyCounts[Stable])
|
|
{
|
|
/* Get the actual key index */
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Stable]);
|
|
if (!Index) return HCELL_NIL;
|
|
|
|
/* Do a search inside it */
|
|
Result = CmpDoFindSubKeyByNumber(Hive, Index, Number);
|
|
|
|
/* Release the cell and return the result */
|
|
HvReleaseCell(Hive, Node->SubKeyLists[Stable]);
|
|
return Result;
|
|
}
|
|
else if (Hive->StorageTypeCount > Volatile)
|
|
{
|
|
/* It's in the volatile list */
|
|
Number = Number - Node->SubKeyCounts[Stable];
|
|
if (Number < Node->SubKeyCounts[Volatile])
|
|
{
|
|
/* Get the actual key index */
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Volatile]);
|
|
if (!Index) return HCELL_NIL;
|
|
|
|
/* Do a search inside it */
|
|
Result = CmpDoFindSubKeyByNumber(Hive, Index, Number);
|
|
|
|
/* Release the cell and return the result */
|
|
HvReleaseCell(Hive, Node->SubKeyLists[Volatile]);
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
/* Nothing was found */
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
static HCELL_INDEX
|
|
NTAPI
|
|
CmpFindSubKeyByHash(IN PHHIVE Hive,
|
|
IN PCM_KEY_FAST_INDEX FastIndex,
|
|
IN PCUNICODE_STRING SearchName)
|
|
{
|
|
ULONG HashKey, i;
|
|
PCM_INDEX FastEntry;
|
|
|
|
/* Make sure it's really a hash */
|
|
ASSERT(FastIndex->Signature == CM_KEY_HASH_LEAF);
|
|
|
|
/* Compute the hash key for the name */
|
|
HashKey = CmpComputeHashKey(0, SearchName, FALSE);
|
|
|
|
/* Loop all the entries */
|
|
for (i = 0; i < FastIndex->Count; i++)
|
|
{
|
|
/* Get the entry */
|
|
FastEntry = &FastIndex->List[i];
|
|
|
|
/* Compare the hash first */
|
|
if (FastEntry->HashKey == HashKey)
|
|
{
|
|
/* Go ahead for a full compare */
|
|
if (!(CmpDoCompareKeyName(Hive, SearchName, FastEntry->Cell)))
|
|
{
|
|
/* It matched, return the cell */
|
|
return FastEntry->Cell;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we got here then we failed */
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
HCELL_INDEX
|
|
NTAPI
|
|
CmpFindSubKeyByName(IN PHHIVE Hive,
|
|
IN PCM_KEY_NODE Parent,
|
|
IN PCUNICODE_STRING SearchName)
|
|
{
|
|
ULONG i;
|
|
PCM_KEY_INDEX IndexRoot;
|
|
HCELL_INDEX SubKey, CellToRelease;
|
|
ULONG Found;
|
|
|
|
/* Loop each storage type */
|
|
for (i = 0; i < Hive->StorageTypeCount; i++)
|
|
{
|
|
/* Make sure the parent node has subkeys */
|
|
if (Parent->SubKeyCounts[i])
|
|
{
|
|
/* Get the Index */
|
|
IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, Parent->SubKeyLists[i]);
|
|
if (!IndexRoot) return HCELL_NIL;
|
|
|
|
/* Get the cell we'll need to release */
|
|
CellToRelease = Parent->SubKeyLists[i];
|
|
|
|
/* Check if this is another index root */
|
|
if (IndexRoot->Signature == CM_KEY_INDEX_ROOT)
|
|
{
|
|
/* Lookup the name in the root */
|
|
Found = CmpFindSubKeyInRoot(Hive,
|
|
IndexRoot,
|
|
SearchName,
|
|
&SubKey);
|
|
|
|
/* Release the previous cell */
|
|
ASSERT(CellToRelease != HCELL_NIL);
|
|
HvReleaseCell(Hive, CellToRelease);
|
|
|
|
/* Make sure we found something valid */
|
|
if (Found & INVALID_INDEX) break;
|
|
|
|
/* Get the new Index Root and set the new cell to be released */
|
|
if (SubKey == HCELL_NIL) continue;
|
|
CellToRelease = SubKey;
|
|
IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, SubKey);
|
|
}
|
|
|
|
/* Make sure the signature is what we expect it to be */
|
|
ASSERT((IndexRoot->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(IndexRoot->Signature == CM_KEY_FAST_LEAF) ||
|
|
(IndexRoot->Signature == CM_KEY_HASH_LEAF));
|
|
|
|
/* Check if this isn't a hashed leaf */
|
|
if (IndexRoot->Signature != CM_KEY_HASH_LEAF)
|
|
{
|
|
/* Find the subkey in the leaf */
|
|
Found = CmpFindSubKeyInLeaf(Hive,
|
|
IndexRoot,
|
|
SearchName,
|
|
&SubKey);
|
|
|
|
/* Release the previous cell */
|
|
ASSERT(CellToRelease != HCELL_NIL);
|
|
HvReleaseCell(Hive, CellToRelease);
|
|
|
|
/* Make sure we found a valid index */
|
|
if (Found & INVALID_INDEX) break;
|
|
}
|
|
else
|
|
{
|
|
/* Find the subkey in the hash */
|
|
SubKey = CmpFindSubKeyByHash(Hive,
|
|
(PCM_KEY_FAST_INDEX)IndexRoot,
|
|
SearchName);
|
|
|
|
/* Release the previous cell */
|
|
ASSERT(CellToRelease != HCELL_NIL);
|
|
HvReleaseCell(Hive, CellToRelease);
|
|
}
|
|
|
|
/* Make sure we got a valid subkey and return it */
|
|
if (SubKey != HCELL_NIL) return SubKey;
|
|
}
|
|
}
|
|
|
|
/* If we got here, then we failed */
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CmpMarkIndexDirty(IN PHHIVE Hive,
|
|
IN HCELL_INDEX ParentKey,
|
|
IN HCELL_INDEX TargetKey)
|
|
{
|
|
PCM_KEY_NODE Node;
|
|
UNICODE_STRING SearchName;
|
|
BOOLEAN IsCompressed;
|
|
ULONG i, Result;
|
|
PCM_KEY_INDEX Index;
|
|
HCELL_INDEX IndexCell, Child = HCELL_NIL, CellToRelease = HCELL_NIL;
|
|
|
|
/* Get the target key node */
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
|
|
if (!Node) return FALSE;
|
|
|
|
/* Check if it's compressed */
|
|
if (Node->Flags & KEY_COMP_NAME)
|
|
{
|
|
/* Remember this for later */
|
|
IsCompressed = TRUE;
|
|
|
|
/* Build the search name */
|
|
SearchName.Length = CmpCompressedNameSize(Node->Name,
|
|
Node->NameLength);
|
|
SearchName.MaximumLength = SearchName.Length;
|
|
SearchName.Buffer = CmpAllocate(SearchName.Length,
|
|
TRUE,
|
|
TAG_CM);
|
|
if (!SearchName.Buffer)
|
|
{
|
|
/* Fail */
|
|
HvReleaseCell(Hive, TargetKey);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Copy it */
|
|
CmpCopyCompressedName(SearchName.Buffer,
|
|
SearchName.MaximumLength,
|
|
Node->Name,
|
|
Node->NameLength);
|
|
}
|
|
else
|
|
{
|
|
/* Name isn't compressed, build it directly from the node */
|
|
IsCompressed = FALSE;
|
|
SearchName.Length = Node->NameLength;
|
|
SearchName.MaximumLength = Node->NameLength;
|
|
SearchName.Buffer = Node->Name;
|
|
}
|
|
|
|
/* We can release the target key now */
|
|
HvReleaseCell(Hive, TargetKey);
|
|
|
|
/* Now get the parent key node */
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
|
|
if (!Node) goto Quickie;
|
|
|
|
/* Loop all hive storage */
|
|
for (i = 0; i < Hive->StorageTypeCount; i++)
|
|
{
|
|
/* Check if any subkeys are in this index */
|
|
if (Node->SubKeyCounts[i])
|
|
{
|
|
/* Get the cell index */
|
|
//ASSERT(HvIsCellAllocated(Hive, Node->SubKeyLists[i]));
|
|
IndexCell = Node->SubKeyLists[i];
|
|
|
|
/* Check if we had anything to release from before */
|
|
if (CellToRelease != HCELL_NIL)
|
|
{
|
|
/* Release it now */
|
|
HvReleaseCell(Hive, CellToRelease);
|
|
CellToRelease = HCELL_NIL;
|
|
}
|
|
|
|
/* Get the key index for the cell */
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
|
|
if (!Index) goto Quickie;
|
|
|
|
/* Release it at the next iteration or below */
|
|
CellToRelease = IndexCell;
|
|
|
|
/* Check if this is a root */
|
|
if (Index->Signature == CM_KEY_INDEX_ROOT)
|
|
{
|
|
/* Get the child inside the root */
|
|
Result = CmpFindSubKeyInRoot(Hive, Index, &SearchName, &Child);
|
|
if (Result & INVALID_INDEX) goto Quickie;
|
|
if (Child == HCELL_NIL) continue;
|
|
|
|
/* We found it, mark the cell dirty */
|
|
HvMarkCellDirty(Hive, IndexCell, FALSE);
|
|
|
|
/* Check if we had anything to release from before */
|
|
if (CellToRelease != HCELL_NIL)
|
|
{
|
|
/* Release it now */
|
|
HvReleaseCell(Hive, CellToRelease);
|
|
CellToRelease = HCELL_NIL;
|
|
}
|
|
|
|
/* Now this child is the index, get the actual key index */
|
|
IndexCell = Child;
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, Child);
|
|
if (!Index) goto Quickie;
|
|
|
|
/* Release it later */
|
|
CellToRelease = Child;
|
|
}
|
|
|
|
/* Make sure this is a valid index */
|
|
ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Index->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Index->Signature == CM_KEY_HASH_LEAF));
|
|
|
|
/* Find the child in the leaf */
|
|
Result = CmpFindSubKeyInLeaf(Hive, Index, &SearchName, &Child);
|
|
if (Result & INVALID_INDEX) goto Quickie;
|
|
if (Child != HCELL_NIL)
|
|
{
|
|
/* We found it, free the name now */
|
|
if (IsCompressed) CmpFree(SearchName.Buffer, 0);
|
|
|
|
/* Release the parent key */
|
|
HvReleaseCell(Hive, ParentKey);
|
|
|
|
/* Check if we had a left over cell to release */
|
|
if (CellToRelease != HCELL_NIL)
|
|
{
|
|
/* Release it */
|
|
HvReleaseCell(Hive, CellToRelease);
|
|
}
|
|
|
|
/* And mark the index cell dirty */
|
|
HvMarkCellDirty(Hive, IndexCell, FALSE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
Quickie:
|
|
/* Release any cells that we still hold */
|
|
if (Node) HvReleaseCell(Hive, ParentKey);
|
|
if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
|
|
|
|
/* Free the search name and return failure */
|
|
if (IsCompressed) CmpFree(SearchName.Buffer, 0);
|
|
return FALSE;
|
|
}
|
|
|
|
HCELL_INDEX
|
|
NTAPI
|
|
CmpAddToLeaf(IN PHHIVE Hive,
|
|
IN HCELL_INDEX LeafCell,
|
|
IN HCELL_INDEX NewKey,
|
|
IN PUNICODE_STRING Name)
|
|
{
|
|
PCM_KEY_INDEX Leaf;
|
|
PCM_KEY_FAST_INDEX FastLeaf;
|
|
ULONG Size, OldSize, EntrySize, i, j;
|
|
HCELL_INDEX NewCell, Child;
|
|
LONG Result;
|
|
|
|
/* Mark the leaf dirty */
|
|
HvMarkCellDirty(Hive, LeafCell, FALSE);
|
|
|
|
/* Get the leaf cell */
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (!Leaf)
|
|
{
|
|
/* Shouldn't happen */
|
|
ASSERT(FALSE);
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
/* Release it */
|
|
HvReleaseCell(Hive, LeafCell);
|
|
|
|
/* Check if this is an index leaf */
|
|
if (Leaf->Signature == CM_KEY_INDEX_LEAF)
|
|
{
|
|
/* This is an old-style leaf */
|
|
FastLeaf = NULL;
|
|
EntrySize = sizeof(HCELL_INDEX);
|
|
}
|
|
else
|
|
{
|
|
/* Sanity check */
|
|
ASSERT((Leaf->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_HASH_LEAF));
|
|
|
|
/* This is a new-style optimized fast (or hash) leaf */
|
|
FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
|
|
EntrySize = sizeof(CM_INDEX);
|
|
}
|
|
|
|
/* Get the current size of the leaf */
|
|
OldSize = HvGetCellSize(Hive, Leaf);
|
|
|
|
/* Calculate the size of the free entries */
|
|
Size = OldSize;
|
|
Size -= EntrySize * Leaf->Count + FIELD_OFFSET(CM_KEY_INDEX, List);
|
|
|
|
/* Assume we'll re-use the same leaf */
|
|
NewCell = LeafCell;
|
|
|
|
/* Check if we're out of space */
|
|
if ((Size / EntrySize) < 1)
|
|
{
|
|
/* Grow the leaf by 1.5x, making sure we can at least fit this entry */
|
|
Size = OldSize + OldSize / 2;
|
|
if (Size < (OldSize + EntrySize)) Size = OldSize + EntrySize;
|
|
|
|
/* Re-allocate the leaf */
|
|
NewCell = HvReallocateCell(Hive, LeafCell, Size);
|
|
if (NewCell == HCELL_NIL) return HCELL_NIL;
|
|
|
|
/* Get the leaf cell */
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, NewCell);
|
|
if (!Leaf)
|
|
{
|
|
/* This shouldn't happen */
|
|
ASSERT(FALSE);
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
/* Release the cell */
|
|
HvReleaseCell(Hive, NewCell);
|
|
|
|
/* Update the fast leaf pointer if we had one */
|
|
if (FastLeaf) FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
|
|
}
|
|
|
|
/* Find the insertion point for our entry */
|
|
i = CmpFindSubKeyInLeaf(Hive, Leaf, Name, &Child);
|
|
if (i & INVALID_INDEX) return HCELL_NIL;
|
|
ASSERT(Child == HCELL_NIL);
|
|
|
|
/* Check if we're not last */
|
|
if (i != Leaf->Count)
|
|
{
|
|
/* Find out where we should go */
|
|
Result = CmpCompareInIndex(Hive,
|
|
Name,
|
|
i,
|
|
Leaf,
|
|
&Child);
|
|
if (Result == 2) return HCELL_NIL;
|
|
ASSERT(Result != 0);
|
|
|
|
/* Check if we come after */
|
|
if (Result > 0)
|
|
{
|
|
/* We do, insert us after the key */
|
|
ASSERT(Result == 1);
|
|
i++;
|
|
}
|
|
|
|
/* Check if we're still not last */
|
|
if (i != Leaf->Count)
|
|
{
|
|
/* Check if we had a fast leaf or not */
|
|
if (FastLeaf)
|
|
{
|
|
/* Copy the fast indexes */
|
|
RtlMoveMemory(&FastLeaf->List[i + 1],
|
|
&FastLeaf->List[i],
|
|
(FastLeaf->Count - i) * sizeof(CM_INDEX));
|
|
}
|
|
else
|
|
{
|
|
/* Copy the indexes themselves */
|
|
RtlMoveMemory(&Leaf->List[i + 1],
|
|
&Leaf->List[i],
|
|
(Leaf->Count - i) * sizeof(HCELL_INDEX));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check if this is a new-style leaf */
|
|
if (FastLeaf)
|
|
{
|
|
/* Set our cell */
|
|
FastLeaf->List[i].Cell = NewKey;
|
|
|
|
/* Check if this is a hash leaf */
|
|
if( FastLeaf->Signature == CM_KEY_HASH_LEAF )
|
|
{
|
|
/* Set our hash key */
|
|
FastLeaf->List[i].HashKey = CmpComputeHashKey(0, Name, FALSE);
|
|
}
|
|
else
|
|
{
|
|
/* First, clear the name */
|
|
FastLeaf->List[i].NameHint[0] = 0;
|
|
FastLeaf->List[i].NameHint[1] = 0;
|
|
FastLeaf->List[i].NameHint[2] = 0;
|
|
FastLeaf->List[i].NameHint[3] = 0;
|
|
|
|
/* Now, figure out if we can fit */
|
|
if (Name->Length / sizeof(WCHAR) < 4)
|
|
{
|
|
/* We can fit, use our length */
|
|
j = Name->Length / sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
/* We can't, use a maximum of 4 */
|
|
j = 4;
|
|
}
|
|
|
|
/* Now fill out the name hint */
|
|
do
|
|
{
|
|
/* Look for invalid characters and break out if we found one */
|
|
if ((USHORT)Name->Buffer[j - 1] > (UCHAR)-1) break;
|
|
|
|
/* Otherwise, copy the a character */
|
|
FastLeaf->List[i].NameHint[j - 1] = (UCHAR)Name->Buffer[j - 1];
|
|
} while (--j > 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This is an old-style leaf, just set our index directly */
|
|
Leaf->List[i] = NewKey;
|
|
}
|
|
|
|
/* Update the leaf count and return the new cell */
|
|
Leaf->Count += 1;
|
|
return NewCell;
|
|
}
|
|
|
|
HCELL_INDEX
|
|
NTAPI
|
|
CmpSplitLeaf(IN PHHIVE Hive,
|
|
IN HCELL_INDEX RootCell,
|
|
IN ULONG RootSelect,
|
|
IN HSTORAGE_TYPE Type)
|
|
{
|
|
PCM_KEY_INDEX IndexKey, LeafKey, NewKey;
|
|
PCM_KEY_FAST_INDEX FastLeaf;
|
|
HCELL_INDEX LeafCell, NewCell;
|
|
USHORT FirstHalf, LastHalf;
|
|
ULONG EntrySize, TotalSize;
|
|
|
|
/* Get the cell */
|
|
IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);
|
|
|
|
/* Check if we've got valid IndexKey */
|
|
if (!IndexKey) return HCELL_NIL;
|
|
|
|
/* Get the leaf cell and key */
|
|
LeafCell = IndexKey->List[RootSelect];
|
|
LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
/* Check if we've got valid LeafKey */
|
|
if (!LeafKey) return HCELL_NIL;
|
|
|
|
/* We are going to divide this leaf into two halves */
|
|
FirstHalf = (LeafKey->Count / 2);
|
|
LastHalf = LeafKey->Count - FirstHalf;
|
|
|
|
/* Now check what kind of hive we're dealing with,
|
|
* and compute entry size
|
|
*/
|
|
if (Hive->Version >= 5)
|
|
{
|
|
/* XP Hive: Use hash leaf */
|
|
ASSERT(LeafKey->Signature == CM_KEY_HASH_LEAF);
|
|
EntrySize = sizeof(CM_INDEX);
|
|
}
|
|
else
|
|
{
|
|
/* Use index leaf */
|
|
ASSERT(LeafKey->Signature == CM_KEY_INDEX_LEAF);
|
|
EntrySize = sizeof(HCELL_INDEX);
|
|
}
|
|
|
|
/* Compute the total size */
|
|
TotalSize = (EntrySize * LastHalf) + FIELD_OFFSET(CM_KEY_INDEX, List) + 1;
|
|
|
|
/* Mark the leaf cell dirty */
|
|
HvMarkCellDirty(Hive, LeafCell, FALSE);
|
|
|
|
/* Make sure its type is the same */
|
|
ASSERT(HvGetCellType(LeafCell) == Type);
|
|
|
|
/* Allocate the cell, fail in case of error */
|
|
NewCell = HvAllocateCell(Hive, TotalSize, Type, LeafCell);
|
|
if (NewCell == HCELL_NIL) return NewCell;
|
|
|
|
/* Get the key */
|
|
NewKey = (PCM_KEY_INDEX)HvGetCell(Hive, NewCell);
|
|
if (!NewKey)
|
|
{
|
|
/* Free the cell and exit - should not happen! */
|
|
ASSERT(FALSE);
|
|
HvFreeCell(Hive, NewCell);
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
/* Release the newly created cell */
|
|
HvReleaseCell(Hive, NewCell);
|
|
|
|
/* Set its signature according to the version of the hive */
|
|
if (Hive->Version >= 5)
|
|
{
|
|
/* XP Hive: Use hash leaf signature */
|
|
NewKey->Signature = CM_KEY_HASH_LEAF;
|
|
}
|
|
else
|
|
{
|
|
/* Use index leaf signature */
|
|
NewKey->Signature = CM_KEY_INDEX_LEAF;
|
|
}
|
|
|
|
/* Calculate the size of the free entries in the root key */
|
|
TotalSize = HvGetCellSize(Hive, IndexKey) -
|
|
(IndexKey->Count * sizeof(HCELL_INDEX)) -
|
|
FIELD_OFFSET(CM_KEY_INDEX, List);
|
|
|
|
/* Check if we're out of space */
|
|
if (TotalSize / sizeof(HCELL_INDEX) < 1)
|
|
{
|
|
/* Grow the leaf by one more entry */
|
|
TotalSize = HvGetCellSize(Hive, IndexKey) + sizeof(HCELL_INDEX);
|
|
|
|
/* Re-allocate the root */
|
|
RootCell = HvReallocateCell(Hive, RootCell, TotalSize);
|
|
if (RootCell == HCELL_NIL)
|
|
{
|
|
/* Free the cell and exit */
|
|
HvFreeCell(Hive, NewCell);
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
/* Get the leaf cell */
|
|
IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);
|
|
if (!IndexKey)
|
|
{
|
|
/* This shouldn't happen */
|
|
ASSERT(FALSE);
|
|
return HCELL_NIL;
|
|
}
|
|
}
|
|
|
|
/* Splitting is done, now we need to copy the contents,
|
|
* according to the hive version
|
|
*/
|
|
if (Hive->Version >= 5)
|
|
{
|
|
/* Copy the fast indexes */
|
|
FastLeaf = (PCM_KEY_FAST_INDEX)LeafKey;
|
|
RtlMoveMemory(&NewKey->List[0],
|
|
&FastLeaf->List[FirstHalf],
|
|
LastHalf * EntrySize);
|
|
}
|
|
else
|
|
{
|
|
/* Copy the indexes themselves */
|
|
RtlMoveMemory(&NewKey->List[0],
|
|
&LeafKey->List[FirstHalf],
|
|
LastHalf * EntrySize);
|
|
}
|
|
|
|
/* Shift the data inside the root key */
|
|
if ((RootSelect + 1) < IndexKey->Count)
|
|
{
|
|
RtlMoveMemory(&IndexKey->List[RootSelect + 2],
|
|
&IndexKey->List[RootSelect + 1],
|
|
(IndexKey->Count -
|
|
(RootSelect + 1)) * sizeof(HCELL_INDEX));
|
|
}
|
|
|
|
/* Make sure both old and new computed counts are valid */
|
|
ASSERT(FirstHalf != 0);
|
|
ASSERT(LastHalf != 0);
|
|
|
|
/* Update the count value of old and new keys */
|
|
LeafKey->Count = FirstHalf;
|
|
NewKey->Count = LastHalf;
|
|
|
|
/* Increase the count value of the root key */
|
|
IndexKey->Count++;
|
|
|
|
/* Set the new cell in root's list */
|
|
IndexKey->List[RootSelect + 1] = NewCell;
|
|
|
|
/* Return the root cell */
|
|
return RootCell;
|
|
}
|
|
|
|
HCELL_INDEX
|
|
NTAPI
|
|
CmpSelectLeaf(IN PHHIVE Hive,
|
|
IN PCM_KEY_NODE KeyNode,
|
|
IN PUNICODE_STRING Name,
|
|
IN HSTORAGE_TYPE Type,
|
|
IN PHCELL_INDEX *RootCell)
|
|
{
|
|
PCM_KEY_INDEX IndexKey, LeafKey;
|
|
PCM_KEY_FAST_INDEX FastLeaf;
|
|
HCELL_INDEX LeafCell, CurrentCell;
|
|
ULONG SubKeyIndex;
|
|
LONG Result;
|
|
|
|
/* Mark it as dirty */
|
|
HvMarkCellDirty(Hive, KeyNode->SubKeyLists[Type], FALSE);
|
|
|
|
/* Get the cell */
|
|
IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, KeyNode->SubKeyLists[Type]);
|
|
|
|
/* Check if we've got a valid key */
|
|
if (!IndexKey)
|
|
{
|
|
/* Should not happen! */
|
|
ASSERTMSG("IndexKey = NULL!, it should not happen!\n", FALSE);
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
/* Sanity check */
|
|
ASSERT(IndexKey->Signature == CM_KEY_INDEX_ROOT);
|
|
|
|
while (TRUE)
|
|
{
|
|
/* Find subkey */
|
|
SubKeyIndex = CmpFindSubKeyInRoot(Hive, IndexKey, Name, &LeafCell);
|
|
|
|
/* Make sure we found something valid */
|
|
if (SubKeyIndex & INVALID_INDEX) return HCELL_NIL;
|
|
|
|
/* Try to fit it into the LeafCell, if it was found */
|
|
if (LeafCell != HCELL_NIL)
|
|
{
|
|
/* Get the leaf key */
|
|
LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
/* Check for failure */
|
|
if (!LeafKey) return HCELL_NIL;
|
|
|
|
/* Check if it fits into this leaf and break */
|
|
if (LeafKey->Count < CmpMaxIndexPerHblock)
|
|
{
|
|
/* Fill in the result and return it */
|
|
*RootCell = &IndexKey->List[SubKeyIndex];
|
|
return LeafCell;
|
|
}
|
|
|
|
/* It didn't fit, so proceed to splitting */
|
|
}
|
|
else
|
|
{
|
|
/* Get the leaf cell at the very end */
|
|
LeafCell = IndexKey->List[SubKeyIndex];
|
|
LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
/* Return an error in case of problems */
|
|
if (!LeafKey) return HCELL_NIL;
|
|
|
|
/* Choose the cell to search from depending on the key type */
|
|
if ((LeafKey->Signature == CM_KEY_FAST_LEAF) ||
|
|
(LeafKey->Signature == CM_KEY_HASH_LEAF))
|
|
{
|
|
FastLeaf = (PCM_KEY_FAST_INDEX)LeafKey;
|
|
CurrentCell = FastLeaf->List[0].Cell;
|
|
}
|
|
else
|
|
{
|
|
/* Make sure it's an index leaf */
|
|
ASSERT(LeafKey->Signature == CM_KEY_INDEX_LEAF);
|
|
CurrentCell = LeafKey->List[0];
|
|
}
|
|
|
|
/* Do a name compare */
|
|
Result = CmpDoCompareKeyName(Hive, Name, CurrentCell);
|
|
|
|
/* Check for failure */
|
|
if (Result == 2) return HCELL_NIL;
|
|
|
|
/* Names can't be equal, ensure that */
|
|
ASSERT(Result != 0);
|
|
|
|
/* Check if it's above */
|
|
if (Result >= 0)
|
|
{
|
|
/* Get the cell in the index */
|
|
LeafCell = IndexKey->List[SubKeyIndex];
|
|
LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
/* Return an error in case of problems */
|
|
if (!LeafKey) return HCELL_NIL;
|
|
|
|
/* Check if it fits into this leaf */
|
|
if (LeafKey->Count < CmpMaxIndexPerHblock)
|
|
{
|
|
/* Fill in the result and return the cell */
|
|
*RootCell = &IndexKey->List[SubKeyIndex];
|
|
return LeafCell;
|
|
}
|
|
|
|
/* No, it doesn't fit, check the next adjacent leaf */
|
|
if ((SubKeyIndex + 1) < IndexKey->Count)
|
|
{
|
|
/* Yes, there is space */
|
|
LeafCell = IndexKey->List[SubKeyIndex + 1];
|
|
LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
/* Return an error in case of problems */
|
|
if (!LeafKey) return HCELL_NIL;
|
|
|
|
/* Check if it fits and break */
|
|
if (LeafKey->Count < CmpMaxIndexPerHblock)
|
|
{
|
|
/* Fill in the result and return the cell */
|
|
*RootCell = &IndexKey->List[SubKeyIndex + 1];
|
|
return LeafCell;
|
|
}
|
|
}
|
|
|
|
/* It didn't fit, so proceed to splitting */
|
|
}
|
|
else
|
|
{
|
|
/* No, it's below, check the subkey index */
|
|
if (SubKeyIndex > 0)
|
|
{
|
|
/* There should be space at the leaf one before that */
|
|
LeafCell = IndexKey->List[SubKeyIndex - 1];
|
|
LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
/* Return an error in case of problems */
|
|
if (!LeafKey) return HCELL_NIL;
|
|
|
|
/* Check if it fits and break */
|
|
if (LeafKey->Count < CmpMaxIndexPerHblock)
|
|
{
|
|
/* Fill in the result and return the cell */
|
|
*RootCell = &IndexKey->List[SubKeyIndex - 1];
|
|
return LeafCell;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use the first leaf, if possible */
|
|
LeafCell = IndexKey->List[0];
|
|
LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
|
|
/* Return an error in case of problems */
|
|
if (!LeafKey) return HCELL_NIL;
|
|
|
|
/* Check if it fits and break */
|
|
if (LeafKey->Count < CmpMaxIndexPerHblock)
|
|
{
|
|
/* Fill in the result and return the cell */
|
|
*RootCell = &IndexKey->List[0];
|
|
return LeafCell;
|
|
}
|
|
}
|
|
|
|
/* It didn't fit into either, so proceed to splitting */
|
|
}
|
|
}
|
|
|
|
/* We need to split */
|
|
CurrentCell = CmpSplitLeaf(Hive,
|
|
KeyNode->SubKeyLists[Type],
|
|
SubKeyIndex,
|
|
Type);
|
|
|
|
/* Return failure in case splitting failed */
|
|
if (CurrentCell == HCELL_NIL) return CurrentCell;
|
|
|
|
/* Set the SubKeyLists value with the new key */
|
|
KeyNode->SubKeyLists[Type] = CurrentCell;
|
|
|
|
/* Get the new cell */
|
|
IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, KeyNode->SubKeyLists[Type]);
|
|
|
|
/* Return in case of failure */
|
|
if (!IndexKey) return HCELL_NIL;
|
|
|
|
/* Make sure the new key became index root */
|
|
ASSERT(IndexKey->Signature == CM_KEY_INDEX_ROOT);
|
|
|
|
/* Now loop over with the new IndexKey value, which definately
|
|
* has the space now
|
|
*/
|
|
}
|
|
|
|
/* Shouldn't come here */
|
|
return HCELL_NIL;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CmpAddSubKey(IN PHHIVE Hive,
|
|
IN HCELL_INDEX Parent,
|
|
IN HCELL_INDEX Child)
|
|
{
|
|
PCM_KEY_NODE KeyNode;
|
|
PCM_KEY_INDEX Index;
|
|
PCM_KEY_FAST_INDEX OldIndex;
|
|
UNICODE_STRING Name;
|
|
HCELL_INDEX IndexCell = HCELL_NIL, CellToRelease = HCELL_NIL, LeafCell;
|
|
PHCELL_INDEX RootPointer = NULL;
|
|
ULONG Type, i;
|
|
BOOLEAN IsCompressed;
|
|
PAGED_CODE();
|
|
|
|
/* Get the key node */
|
|
KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Child);
|
|
if (!KeyNode)
|
|
{
|
|
/* Shouldn't happen */
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check if the name is compressed */
|
|
if (KeyNode->Flags & KEY_COMP_NAME)
|
|
{
|
|
/* Remember for later */
|
|
IsCompressed = TRUE;
|
|
|
|
/* Create the compressed name and allocate it */
|
|
Name.Length = CmpCompressedNameSize(KeyNode->Name, KeyNode->NameLength);
|
|
Name.MaximumLength = Name.Length;
|
|
Name.Buffer = Hive->Allocate(Name.Length, TRUE, TAG_CM);
|
|
if (!Name.Buffer)
|
|
{
|
|
/* Release the cell and fail */
|
|
HvReleaseCell(Hive, Child);
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Copy the compressed name */
|
|
CmpCopyCompressedName(Name.Buffer,
|
|
Name.MaximumLength,
|
|
KeyNode->Name,
|
|
KeyNode->NameLength);
|
|
}
|
|
else
|
|
{
|
|
/* Remember for later */
|
|
IsCompressed = FALSE;
|
|
|
|
/* Build the unicode string */
|
|
Name.Length = KeyNode->NameLength;
|
|
Name.MaximumLength = KeyNode->NameLength;
|
|
Name.Buffer = &KeyNode->Name[0];
|
|
}
|
|
|
|
/* Release the cell */
|
|
HvReleaseCell(Hive, Child);
|
|
|
|
/* Get the parent node */
|
|
KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Parent);
|
|
if (!KeyNode)
|
|
{
|
|
/* Not handled */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Find out the type of the cell, and check if this is the first subkey */
|
|
Type = HvGetCellType(Child);
|
|
if (!KeyNode->SubKeyCounts[Type])
|
|
{
|
|
/* Allocate a fast leaf */
|
|
IndexCell = HvAllocateCell(Hive, sizeof(CM_KEY_FAST_INDEX), Type, HCELL_NIL);
|
|
if (IndexCell == HCELL_NIL)
|
|
{
|
|
/* Not handled */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Get the leaf cell */
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
|
|
if (!Index)
|
|
{
|
|
/* Shouldn't happen */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Now check what kind of hive we're dealing with */
|
|
if (Hive->Version >= 5)
|
|
{
|
|
/* XP Hive: Use hash leaf */
|
|
Index->Signature = CM_KEY_HASH_LEAF;
|
|
}
|
|
else if (Hive->Version >= 3)
|
|
{
|
|
/* Windows 2000 and ReactOS: Use fast leaf */
|
|
Index->Signature = CM_KEY_FAST_LEAF;
|
|
}
|
|
else
|
|
{
|
|
/* NT 4: Use index leaf */
|
|
Index->Signature = CM_KEY_INDEX_LEAF;
|
|
}
|
|
|
|
/* Setup the index list */
|
|
Index->Count = 0;
|
|
KeyNode->SubKeyLists[Type] = IndexCell;
|
|
}
|
|
else
|
|
{
|
|
/* We already have an index, get it */
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, KeyNode->SubKeyLists[Type]);
|
|
if (!Index)
|
|
{
|
|
/* Not handled */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Remember to release the cell later */
|
|
CellToRelease = KeyNode->SubKeyLists[Type];
|
|
|
|
/* Check if this is a fast leaf that's gotten too full */
|
|
if ((Index->Signature == CM_KEY_FAST_LEAF) &&
|
|
(Index->Count >= CmpMaxFastIndexPerHblock))
|
|
{
|
|
DPRINT("Doing Fast->Slow Leaf conversion\n");
|
|
|
|
/* Mark this cell as dirty */
|
|
HvMarkCellDirty(Hive, CellToRelease, FALSE);
|
|
|
|
/* Convert */
|
|
OldIndex = (PCM_KEY_FAST_INDEX)Index;
|
|
|
|
for (i = 0; i < OldIndex->Count; i++)
|
|
{
|
|
Index->List[i] = OldIndex->List[i].Cell;
|
|
}
|
|
|
|
/* Set the new type value */
|
|
Index->Signature = CM_KEY_INDEX_LEAF;
|
|
}
|
|
else if (((Index->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Index->Signature == CM_KEY_HASH_LEAF)) &&
|
|
(Index->Count >= CmpMaxIndexPerHblock))
|
|
{
|
|
/* This is an old/hashed leaf that's gotten too large, root it */
|
|
IndexCell = HvAllocateCell(Hive,
|
|
sizeof(CM_KEY_INDEX) +
|
|
sizeof(HCELL_INDEX),
|
|
Type,
|
|
HCELL_NIL);
|
|
if (IndexCell == HCELL_NIL)
|
|
{
|
|
/* Not handled */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Get the index cell */
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
|
|
if (!Index)
|
|
{
|
|
/* Shouldn't happen */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Mark the index as a root, and set the index cell */
|
|
Index->Signature = CM_KEY_INDEX_ROOT;
|
|
Index->Count = 1;
|
|
Index->List[0] = KeyNode->SubKeyLists[Type];
|
|
KeyNode->SubKeyLists[Type] = IndexCell;
|
|
}
|
|
}
|
|
|
|
/* Now we can choose the leaf cell */
|
|
LeafCell = KeyNode->SubKeyLists[Type];
|
|
|
|
/* Check if we turned the index into a root */
|
|
if (Index->Signature == CM_KEY_INDEX_ROOT)
|
|
{
|
|
DPRINT("Leaf->Root Index Conversion\n");
|
|
|
|
/* Get the leaf where to add the new entry (the routine will do
|
|
* the splitting if necessary)
|
|
*/
|
|
LeafCell = CmpSelectLeaf(Hive, KeyNode, &Name, Type, &RootPointer);
|
|
if (LeafCell == HCELL_NIL)
|
|
{
|
|
/* Not handled */
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
/* Add our leaf cell */
|
|
LeafCell = CmpAddToLeaf(Hive, LeafCell, Child, &Name);
|
|
if (LeafCell == HCELL_NIL)
|
|
{
|
|
/* Not handled */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Update the key counts */
|
|
KeyNode->SubKeyCounts[Type]++;
|
|
|
|
/* Check if caller wants us to return the leaf */
|
|
if (RootPointer)
|
|
{
|
|
/* Return it */
|
|
*RootPointer = LeafCell;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, mark it as the list index for the cell */
|
|
KeyNode->SubKeyLists[Type] = LeafCell;
|
|
}
|
|
|
|
/* If the name was compressed, free our copy */
|
|
if (IsCompressed) Hive->Free(Name.Buffer, 0);
|
|
|
|
/* Release all our cells */
|
|
if (IndexCell != HCELL_NIL) HvReleaseCell(Hive, IndexCell);
|
|
if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
|
|
HvReleaseCell(Hive, Parent);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CmpRemoveSubKey(IN PHHIVE Hive,
|
|
IN HCELL_INDEX ParentKey,
|
|
IN HCELL_INDEX TargetKey)
|
|
{
|
|
PCM_KEY_NODE Node;
|
|
UNICODE_STRING SearchName;
|
|
BOOLEAN IsCompressed;
|
|
WCHAR Buffer[50];
|
|
HCELL_INDEX RootCell = HCELL_NIL, LeafCell, ChildCell;
|
|
PCM_KEY_INDEX Root = NULL, Leaf;
|
|
PCM_KEY_FAST_INDEX Child;
|
|
ULONG Storage, RootIndex = INVALID_INDEX, LeafIndex;
|
|
BOOLEAN Result = FALSE;
|
|
HCELL_INDEX CellToRelease1 = HCELL_NIL, CellToRelease2 = HCELL_NIL;
|
|
|
|
/* Get the target key node */
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
|
|
if (!Node) return FALSE;
|
|
|
|
/* Make sure it's dirty, then release it */
|
|
ASSERT(HvIsCellDirty(Hive, TargetKey));
|
|
HvReleaseCell(Hive, TargetKey);
|
|
|
|
/* Check if the name is compressed */
|
|
if (Node->Flags & KEY_COMP_NAME)
|
|
{
|
|
/* Remember for later */
|
|
IsCompressed = TRUE;
|
|
|
|
/* Build the search name */
|
|
SearchName.Length = CmpCompressedNameSize(Node->Name,
|
|
Node->NameLength);
|
|
SearchName.MaximumLength = SearchName.Length;
|
|
|
|
/* Do we need an extra bufer? */
|
|
if (SearchName.MaximumLength > sizeof(Buffer))
|
|
{
|
|
/* Allocate one */
|
|
SearchName.Buffer = CmpAllocate(SearchName.Length,
|
|
TRUE,
|
|
TAG_CM);
|
|
if (!SearchName.Buffer) return FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, use our local stack buffer */
|
|
SearchName.Buffer = Buffer;
|
|
}
|
|
|
|
/* Copy the compressed name */
|
|
CmpCopyCompressedName(SearchName.Buffer,
|
|
SearchName.MaximumLength,
|
|
Node->Name,
|
|
Node->NameLength);
|
|
}
|
|
else
|
|
{
|
|
/* It's not compressed, build the name directly from the node */
|
|
IsCompressed = FALSE;
|
|
SearchName.Length = Node->NameLength;
|
|
SearchName.MaximumLength = Node->NameLength;
|
|
SearchName.Buffer = Node->Name;
|
|
}
|
|
|
|
/* Now get the parent node */
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
|
|
if (!Node) goto Exit;
|
|
|
|
/* Make sure it's dirty, then release it */
|
|
ASSERT(HvIsCellDirty(Hive, ParentKey));
|
|
HvReleaseCell(Hive, ParentKey);
|
|
|
|
/* Get the storage type and make sure it's not empty */
|
|
Storage = HvGetCellType(TargetKey);
|
|
ASSERT(Node->SubKeyCounts[Storage] != 0);
|
|
//ASSERT(HvIsCellAllocated(Hive, Node->SubKeyLists[Storage]));
|
|
|
|
/* Get the leaf cell now */
|
|
LeafCell = Node->SubKeyLists[Storage];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (!Leaf) goto Exit;
|
|
|
|
/* Remember to release it later */
|
|
CellToRelease1 = LeafCell;
|
|
|
|
/* Check if the leaf is a root */
|
|
if (Leaf->Signature == CM_KEY_INDEX_ROOT)
|
|
{
|
|
/* Find the child inside the root */
|
|
RootIndex = CmpFindSubKeyInRoot(Hive, Leaf, &SearchName, &ChildCell);
|
|
if (RootIndex & INVALID_INDEX) goto Exit;
|
|
ASSERT(ChildCell != FALSE);
|
|
|
|
/* The root cell is now this leaf */
|
|
Root = Leaf;
|
|
RootCell = LeafCell;
|
|
|
|
/* And the new leaf is now this child */
|
|
LeafCell = ChildCell;
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if (!Leaf) goto Exit;
|
|
|
|
/* Remember to release it later */
|
|
CellToRelease2 = LeafCell;
|
|
}
|
|
|
|
/* Make sure the leaf is valid */
|
|
ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_HASH_LEAF));
|
|
|
|
/* Now get the child in the leaf */
|
|
LeafIndex = CmpFindSubKeyInLeaf(Hive, Leaf, &SearchName, &ChildCell);
|
|
if (LeafIndex & INVALID_INDEX) goto Exit;
|
|
ASSERT(ChildCell != HCELL_NIL);
|
|
|
|
/* Decrement key counts and check if this was the last leaf entry */
|
|
Node->SubKeyCounts[Storage]--;
|
|
if (!(--Leaf->Count))
|
|
{
|
|
/* Free the leaf */
|
|
HvFreeCell(Hive, LeafCell);
|
|
|
|
/* Check if we were inside a root */
|
|
if (Root)
|
|
{
|
|
/* Decrease the root count too, since the leaf is going away */
|
|
if (!(--Root->Count))
|
|
{
|
|
/* The root is gone too,n ow */
|
|
HvFreeCell(Hive, RootCell);
|
|
Node->SubKeyLists[Storage] = HCELL_NIL;
|
|
}
|
|
else if (RootIndex < Root->Count)
|
|
{
|
|
/* Bring everything up by one */
|
|
RtlMoveMemory(&Root->List[RootIndex],
|
|
&Root->List[RootIndex + 1],
|
|
(Root->Count - RootIndex) * sizeof(HCELL_INDEX));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, just clear the cell */
|
|
Node->SubKeyLists[Storage] = HCELL_NIL;
|
|
}
|
|
}
|
|
else if (LeafIndex < Leaf->Count)
|
|
{
|
|
/* Was the leaf a normal index? */
|
|
if (Leaf->Signature == CM_KEY_INDEX_LEAF)
|
|
{
|
|
/* Bring everything up by one */
|
|
RtlMoveMemory(&Leaf->List[LeafIndex],
|
|
&Leaf->List[LeafIndex + 1],
|
|
(Leaf->Count - LeafIndex) * sizeof(HCELL_INDEX));
|
|
}
|
|
else
|
|
{
|
|
/* This is a fast index, bring everything up by one */
|
|
Child = (PCM_KEY_FAST_INDEX)Leaf;
|
|
RtlMoveMemory(&Child->List[LeafIndex],
|
|
&Child->List[LeafIndex+1],
|
|
(Child->Count - LeafIndex) * sizeof(CM_INDEX));
|
|
}
|
|
}
|
|
|
|
/* If we got here, now we're done */
|
|
Result = TRUE;
|
|
|
|
Exit:
|
|
/* Release any cells we may have been holding */
|
|
if (CellToRelease1 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease1);
|
|
if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
|
|
|
|
/* Check if the name was compressed and not inside our local buffer */
|
|
if ((IsCompressed) && (SearchName.MaximumLength > sizeof(Buffer)))
|
|
{
|
|
/* Free the buffer we allocated */
|
|
CmpFree(SearchName.Buffer, 0);
|
|
}
|
|
|
|
/* Return the result */
|
|
return Result;
|
|
}
|