/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    cmindex.c

Abstract:

    This module contains cm routines that understand the structure
    of child subkey indicies.

--*/

/* -

The Structure:

    Use a 1 or 2 level tree.  Leaf nodes are arrays of pointers to
    cells, sorted.  Binary search to find cell of interest.  Directory
    node (can be only one) is an array of pointers to leaf blocks.
    Do compare on last entry of each leaf block.

    One Level:

        Key--->+----+
               |    |
               |  x----------><key whose name is "apple", string in key>
               |    |
               +----+
               |    |
               |  x----------><as above, but key named "banana">
               |    |
               +----+
               |    |
               |    |
               |    |
               +----+
               |    |
               |    |
               |    |
               +----+
               |    |
               |  x----------><as above, but key named "zumwat">
               |    |
               +----+


    Two Level:

        Key--->+----+
               |    |    +-----+
               |  x----->|     |
               |    |    |  x----------------->"aaa"
               +----+    |     |
               |    |    +-----+
               |    |    |     |
               |    |    |     |
               +----+    |     |
               |    |    +-----+
               |    |    |     |
               |    |    |  x----------------->"abc"
               +----+    |     |
               |    |    +-----+
               |    |
               |    |
               +----+
               |    |    +-----+
               |  x----->|     |
               |    |    |  x----------------->"w"
               +----+    |     |
                         +-----+
                         |     |
                         |     |
                         |     |
                         +-----+
                         |     |
                         |  x----------------->"z"
                         |     |
                         +-----+


    Never more than two levels.

    Each block must fix in on HBLOCK_SIZE Cell.  Allows about 1000
    entries.  Max of 1 million total, best case.  Worst case something
    like 1/4 of that.

*/

#include    "cmp.h"

ULONG
CmpFindSubKeyInRoot(
    PHHIVE          Hive,
    PCM_KEY_INDEX   Index,
    PUNICODE_STRING SearchName,
    PHCELL_INDEX    Child
    );

ULONG
CmpFindSubKeyInLeaf(
    PHHIVE          Hive,
    PCM_KEY_INDEX   Index,
    PUNICODE_STRING SearchName,
    PHCELL_INDEX    Child
    );

LONG
CmpCompareInIndex(
    PHHIVE          Hive,
    PUNICODE_STRING SearchName,
    ULONG           Count,
    PCM_KEY_INDEX   Index,
    PHCELL_INDEX    Child
    );

LONG
CmpDoCompareKeyName(
    PHHIVE          Hive,
    PUNICODE_STRING SearchName,
    HCELL_INDEX     Cell
    );

HCELL_INDEX
CmpDoFindSubKeyByNumber(
    PHHIVE          Hive,
    PCM_KEY_INDEX   Index,
    ULONG           Number
    );

HCELL_INDEX
CmpAddToLeaf(
    PHHIVE          Hive,
    HCELL_INDEX     LeafCell,
    HCELL_INDEX     NewKey,
    PUNICODE_STRING NewName
    );

HCELL_INDEX
CmpSelectLeaf(
    PHHIVE          Hive,
    PCM_KEY_NODE    ParentKey,
    PUNICODE_STRING NewName,
    HSTORAGE_TYPE   Type,
    PHCELL_INDEX    *RootPointer
    );

HCELL_INDEX
CmpSplitLeaf(
    PHHIVE          Hive,
    HCELL_INDEX     RootCell,
    ULONG           RootSelect,
    HSTORAGE_TYPE   Type
    );

HCELL_INDEX
CmpFindSubKeyByHash(
    PHHIVE                  Hive,
    PCM_KEY_FAST_INDEX      FastIndex,
    PUNICODE_STRING         SearchName
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CmpFindSubKeyByName)
#pragma alloc_text(PAGE,CmpFindSubKeyInRoot)
#pragma alloc_text(PAGE,CmpFindSubKeyInLeaf)
#pragma alloc_text(PAGE,CmpDoCompareKeyName)
#pragma alloc_text(PAGE,CmpCompareInIndex)
#pragma alloc_text(PAGE,CmpFindSubKeyByNumber)
#pragma alloc_text(PAGE,CmpDoFindSubKeyByNumber)
#pragma alloc_text(PAGE,CmpAddSubKey)
#pragma alloc_text(PAGE,CmpAddToLeaf)
#pragma alloc_text(PAGE,CmpSelectLeaf)
#pragma alloc_text(PAGE,CmpSplitLeaf)
#pragma alloc_text(PAGE,CmpMarkIndexDirty)
#pragma alloc_text(PAGE,CmpRemoveSubKey)
#pragma alloc_text(PAGE,CmpComputeHashKey)
#pragma alloc_text(PAGE,CmpComputeHashKeyForCompressedName)
#pragma alloc_text(PAGE,CmpFindSubKeyByHash)
#pragma alloc_text(PAGE,CmpDuplicateIndex)
#pragma alloc_text(PAGE,CmpUpdateParentForEachSon)
#pragma alloc_text(PAGE,CmpRemoveSubKeyCellNoCellRef)
#endif


HCELL_INDEX
CmpFindSubKeyByName(
    PHHIVE          Hive,
    PCM_KEY_NODE    Parent,
    PUNICODE_STRING SearchName
    )
/*++

Routine Description:

    Find the child cell (either subkey or value) specified by name.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Parent - cell of key body which is parent of child of interest

    SearchName - name of child of interest

Return Value:

    Cell of matching child key, or HCELL_NIL if none.

--*/
{
    PCM_KEY_INDEX   IndexRoot;
    HCELL_INDEX     Child;
    ULONG           i;
    ULONG           FoundIndex;
    HCELL_INDEX     CellToRelease = HCELL_NIL;

    CM_PAGED_CODE();

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpFindSubKeyByName:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p Parent=%p SearchName=%p\n", Hive, Parent, SearchName));

    //
    // Try first the Stable, then the Volatile store.  Assumes that
    // all Volatile refs in Stable space are zeroed out at boot.
    //
    for (i = 0; i < Hive->StorageTypeCount; i++) {
        if (Parent->SubKeyCounts[i] != 0) {
            IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, Parent->SubKeyLists[i]);
            ASSERT( (IndexRoot == NULL) || HvIsCellAllocated(Hive, Parent->SubKeyLists[i]) );
            if( IndexRoot == NULL ) {
                //
                // we couldn't map a view for the bin containing this cell
                //
                return HCELL_NIL;
            }
            CellToRelease = Parent->SubKeyLists[i];

            if (IndexRoot->Signature == CM_KEY_INDEX_ROOT) {
                if( INVALID_INDEX & CmpFindSubKeyInRoot(Hive, IndexRoot, SearchName, &Child) ) {
                    //
                    // couldn't map view inside
                    //
                    ASSERT( CellToRelease != HCELL_NIL );
                    HvReleaseCell(Hive,CellToRelease);
                    return HCELL_NIL;
                }

                ASSERT( CellToRelease != HCELL_NIL );
                HvReleaseCell(Hive,CellToRelease);

                if (Child == HCELL_NIL) {
                    continue;
                }
                IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, Child);
                if( IndexRoot == NULL ) {
                    //
                    // we couldn't map a view for the bin containing this cell
                    //
                    return HCELL_NIL;
                }
                CellToRelease = Child;
            }
            ASSERT((IndexRoot->Signature == CM_KEY_INDEX_LEAF)  ||
                   (IndexRoot->Signature == CM_KEY_FAST_LEAF)   ||
                   (IndexRoot->Signature == CM_KEY_HASH_LEAF)
                   );


            if( IndexRoot->Signature == CM_KEY_HASH_LEAF ) {
                Child = CmpFindSubKeyByHash(Hive,(PCM_KEY_FAST_INDEX)IndexRoot,SearchName);
                ASSERT( CellToRelease != HCELL_NIL );
                HvReleaseCell(Hive,CellToRelease);
            } else {
                FoundIndex = CmpFindSubKeyInLeaf(Hive,
                                                 IndexRoot,
                                                 SearchName,
                                                 &Child);

                ASSERT( CellToRelease != HCELL_NIL );
                HvReleaseCell(Hive,CellToRelease);

                if( INVALID_INDEX & FoundIndex ) {
                    //
                    // couldn't map view
                    // 
                    return HCELL_NIL;
                }
            }

            if (Child != HCELL_NIL) {
                //
                // success
                //
                return Child;
            }
        }
    }
    return HCELL_NIL;
}


ULONG
CmpFindSubKeyInRoot(
    PHHIVE          Hive,
    PCM_KEY_INDEX   Index,
    PUNICODE_STRING SearchName,
    PHCELL_INDEX    Child
    )
/*++

Routine Description:

    Find the leaf index that would contain a key, if there is one.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Index - pointer to root index block

    SearchName - pointer to name of key of interest

    Child - pointer to variable to receive hcell_index of found leaf index
            block, HCELL_NIL if none.  Non nil does not necessarily mean
            the key is present, call FindSubKeyInLeaf to decide that.

Return Value:

    Index in List of last Leaf Cell entry examined.  If Child != HCELL_NIL,
    Index is entry that matched, else, index is for last entry we looked
    at.  (Target Leaf will be this value plus or minus 1)

    If an error appears while searching the subkey (i.e. a cell cannot be 
    mapped into memory) INVALID_INDEX is returned.

--*/
{
    ULONG           High;
    ULONG           Low;
    ULONG           CanCount;
    HCELL_INDEX     LeafCell;
    PCM_KEY_INDEX   Leaf;
    LONG            Result;
    ULONG           ReturnIndex = INVALID_INDEX;

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpFindSubKeyInRoot:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p Index=%p SearchName=%p\n",Hive,Index,SearchName));


    ASSERT(Index->Count != 0);
    ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);

    High = Index->Count - 1;
    Low = 0;

    while (TRUE) {

        //
        // Compute where to look next, get correct pointer, do compare
        //
        CanCount = ((High-Low)/2)+Low;
        LeafCell = Index->List[CanCount];
        Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
        if( Leaf == NULL ) {
            //
            // we couldn't map the bin containing this cell
            //
            *Child = HCELL_NIL;
            ReturnIndex = INVALID_INDEX;
            goto JustReturn;
        }

        ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF) ||
               (Leaf->Signature == CM_KEY_FAST_LEAF)  || 
               (Leaf->Signature == CM_KEY_HASH_LEAF)
               );
        ASSERT(Leaf->Count != 0);

        Result = CmpCompareInIndex(Hive,
                                   SearchName,
                                   Leaf->Count-1,
                                   Leaf,
                                   Child);

        if( Result == 2 ) {
            //
            // couldn't map view inside; bail out
            //
            *Child = HCELL_NIL;
            ReturnIndex = INVALID_INDEX;
            goto JustReturn;
        }
        if (Result == 0) {

            //
            // SearchName == KeyName of last key in leaf, so
            //  this is our leaf
            //
            *Child = LeafCell;
            ReturnIndex = CanCount;
            goto JustReturn;
        }

        if (Result < 0) {

            ASSERT( Result == -1 );
            //
            // SearchName < KeyName, so this may still be our leaf
            //
            Result = CmpCompareInIndex(Hive,
                                       SearchName,
                                       0,
                                       Leaf,
                                       Child);

            if( Result == 2 ) {
                //
                // couldn't map view inside; bail out
                //
                *Child = HCELL_NIL;
                ReturnIndex = INVALID_INDEX;
                goto JustReturn;
            }

            if (Result >= 0) {

                ASSERT( (Result == 1) || (Result == 0) );
                //
                // we know from above that SearchName is less than
                // last key in leaf.
                // since it is also >= first key in leaf, it must
                // reside in leaf somewhere, and we are done
                //
                *Child = LeafCell;
                ReturnIndex = CanCount;
                goto JustReturn;
            }

            High = CanCount;

        } else {

            //
            // SearchName > KeyName
            //
            Low = CanCount;
        }

        if ((High - Low) <= 1) {
            break;
        }
        HvReleaseCell(Hive, LeafCell);
    }

    HvReleaseCell(Hive, LeafCell);
    //
    // If we get here, High - Low = 1 or High == Low
    //
    ASSERT((High - Low == 1) || (High == Low));
    LeafCell = Index->List[Low];
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if( Leaf == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        *Child = HCELL_NIL;
        ReturnIndex = INVALID_INDEX;
        goto JustReturn;
    }
    Result = CmpCompareInIndex(Hive,
                               SearchName,
                               Leaf->Count-1,
                               Leaf,
                               Child);

    if( Result == 2 ) {
        //
        // couldn't map view inside; bail out
        //
        *Child = HCELL_NIL;
        ReturnIndex = INVALID_INDEX;
        goto JustReturn;
    }

    if (Result == 0) {

        //
        // found it
        //
        *Child = LeafCell;
        ReturnIndex = Low;
        goto JustReturn;
    }

    if (Result < 0) {

        ASSERT( Result == -1 );
        //
        // SearchName < KeyName, so this may still be our leaf
        //
        Result = CmpCompareInIndex(Hive,
                                   SearchName,
                                   0,
                                   Leaf,
                                   Child);

        if( Result == 2 ) {
            //
            // couldn't map view inside; bail out
            //
            *Child = HCELL_NIL;
            ReturnIndex = INVALID_INDEX;
            goto JustReturn;
        }

        if (Result >= 0) {

            ASSERT( (Result == 1) || (Result == 0) );
            //
            // we know from above that SearchName is less than
            // last key in leaf.
            // since it is also >= first key in leaf, it must
            // reside in leaf somewhere, and we are done
            //
            *Child = LeafCell;
            ReturnIndex = Low;
            goto JustReturn;
        }

        //
        // does not exist, but belongs in Low or Leaf below low
        //
        *Child = HCELL_NIL;
        ReturnIndex = Low;
        goto JustReturn;
    }

    HvReleaseCell(Hive, LeafCell);
    //
    // see if High matches
    //
    LeafCell = Index->List[High];
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if( Leaf == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        *Child = HCELL_NIL;
        ReturnIndex = INVALID_INDEX;
        goto JustReturn;
    }
    Result = CmpCompareInIndex(Hive,
                               SearchName,
                               Leaf->Count - 1,
                               Leaf,
                               Child);
    if( Result == 2 ) {
        //
        // couldn't map view inside; bail out
        //
        *Child = HCELL_NIL;
        ReturnIndex = INVALID_INDEX;
        goto JustReturn;
    }
    if (Result == 0) {

        //
        // found it
        //
        *Child = LeafCell;
        ReturnIndex = High;
        goto JustReturn;

    } else if (Result < 0) {

        ASSERT( Result == -1 );
        //
        // Clearly greater than low, or we wouldn't be here.
        // So regardless of whether it's below the start
        // of this leaf, it would be in this leaf if it were
        // where, so report this leaf.
        //
        *Child = LeafCell;
        ReturnIndex = High;
        goto JustReturn;

    }

    //
    // Off the high end
    //
    *Child = HCELL_NIL;
    ReturnIndex = High;

JustReturn:
    if(Leaf != NULL){
        HvReleaseCell(Hive, LeafCell);
    }
    return ReturnIndex;
}


ULONG
CmpFindSubKeyInLeaf(
    PHHIVE          Hive,
    PCM_KEY_INDEX   Index,
    PUNICODE_STRING SearchName,
    PHCELL_INDEX    Child
    )
/*++

Routine Description:

    Find a named key in a leaf index, if it exists. The supplied index
    may be either a fast index or a slow one.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Index - pointer to leaf block

    SearchName - pointer to name of key of interest

    Child - pointer to variable to receive hcell_index of found key
            HCELL_NIL if none found

Return Value:

    Index in List of last cell.  If Child != HCELL_NIL, is offset in
    list at which Child was found.  Else, is offset of last place
    we looked.

    INVALID_INDEX - resources problem; couldn't map view
--*/
{
    ULONG       High;
    ULONG       Low;
    ULONG       CanCount;
    LONG        Result;

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpFindSubKeyInLeaf:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p Index=%p SearchName=%p\n",Hive,Index,SearchName));

    ASSERT((Index->Signature == CM_KEY_INDEX_LEAF)  ||
           (Index->Signature == CM_KEY_FAST_LEAF)   ||
           (Index->Signature == CM_KEY_HASH_LEAF)
           );

    High = Index->Count - 1;
    Low = 0;
    CanCount = High/2;

    if (Index->Count == 0) {
        *Child = HCELL_NIL;
        return 0;
    }

    while (TRUE) {

        //
        // Compute where to look next, get correct pointer, do compare
        //
        Result = CmpCompareInIndex(Hive,
                                   SearchName,
                                   CanCount,
                                   Index,
                                   Child);

        if( Result == 2 ) {
            //
            // couldn't map view inside; bail out
            //
            *Child = HCELL_NIL;
            return INVALID_INDEX;
        }

        if (Result == 0) {

            //
            // SearchName == KeyName
            //
            return CanCount;
        }

        if (Result < 0) {

            ASSERT( Result == -1 );
            //
            // SearchName < KeyName
            //
            High = CanCount;

        } else {

            ASSERT( Result == 1 );
            //
            // SearchName > KeyName
            //
            Low = CanCount;
        }

        if ((High - Low) <= 1) {
            break;
        }
        CanCount = ((High-Low)/2)+Low;
    }

    //
    // If we get here, High - Low = 1 or High == Low
    // Simply look first at Low, then at High
    //
    Result = CmpCompareInIndex(Hive,
                               SearchName,
                               Low,
                               Index,
                               Child);
    if( Result == 2 ) {
        //
        // couldn't map view inside; bail out
        //
        *Child = HCELL_NIL;
        return INVALID_INDEX;
    }

    if (Result == 0) {

        //
        // found it
        //
        return Low;
    }

    if (Result < 0) {

        ASSERT( Result == -1 );
        //
        // does not exist, under
        //
        return Low;
    }

    //
    // see if High matches, we will return High as the
    // closest key regardless.
    //
    Result = CmpCompareInIndex(Hive,
                               SearchName,
                               High,
                               Index,
                               Child);
    if( Result == 2 ) {
        //
        // couldn't map view inside; bail out
        //
        *Child = HCELL_NIL;
        return INVALID_INDEX;
    }

    return High;
}


LONG
CmpCompareInIndex(
    PHHIVE          Hive,
    PUNICODE_STRING SearchName,
    ULONG           Count,
    PCM_KEY_INDEX   Index,
    PHCELL_INDEX    Child
    )
/*++

Routine Description:

    Do a compare of a name in an index. This routine handles both
    fast leafs and slow ones.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    SearchName - pointer to name of key we are searching for

    Count - supplies index that we are searching at.

    Index - Supplies pointer to either a CM_KEY_INDEX or
            a CM_KEY_FAST_INDEX. This routine will determine which
            type of index it is passed.

    Child - pointer to variable to receive hcell_index of found key
            HCELL_NIL if result != 0

Return Value:

    0 = SearchName == KeyName (of Cell)

    -1 = SearchName < KeyName

    +1 = SearchName > KeyName

    +2 = Error, insufficient resources

--*/
{
    PCM_KEY_FAST_INDEX  FastIndex;
    LONG                Result;
    ULONG               i;
    WCHAR               c1;
    WCHAR               c2;
    ULONG               HintLength;
    ULONG               ValidChars;
    ULONG               NameLength;
    PCM_INDEX           Hint;

    *Child = HCELL_NIL;
    if ( (Index->Signature == CM_KEY_FAST_LEAF) ||
         (Index->Signature == CM_KEY_HASH_LEAF) ) {
        FastIndex = (PCM_KEY_FAST_INDEX)Index;
        Hint = &FastIndex->List[Count];

        if(Index->Signature == CM_KEY_FAST_LEAF) {
            //
            // Compute the number of valid characters in the hint to compare.
            //
            HintLength = 4;
            for (i=0;i<4;i++) {
                if (Hint->NameHint[i] == 0) {
                    HintLength = i;
                    break;
                }
            }
            NameLength = SearchName->Length / sizeof(WCHAR);
            if (NameLength < HintLength) {
                ValidChars = NameLength;
            } else {
                ValidChars = HintLength;
            }
            for (i=0; i<ValidChars; i++) {
                c1 = SearchName->Buffer[i];
                c2 = FastIndex->List[Count].NameHint[i];
                Result = (LONG)CmUpcaseUnicodeChar(c1) -
                         (LONG)CmUpcaseUnicodeChar(c2);
                if (Result != 0) {

                    //
                    // We have found a mismatched character in the hint,
                    // we can now tell which direction to go.
                    //
                    return (Result > 0) ? 1 : -1 ;
                }
            }
        }

        //
        // We have compared all the available characters without a
        // discrepancy. Go ahead and do the actual comparison now.
        //
        Result = CmpDoCompareKeyName(Hive,SearchName,FastIndex->List[Count].Cell);
        if( Result == 2 ) {
            //
            // couldn't map view inside; signal it to the caller
            //
            return 2;
        }
        if (Result == 0) {
            *Child = Hint->Cell;
        }
    } else {
        //
        // This is just a normal old slow index.
        //
        Result = CmpDoCompareKeyName(Hive,SearchName,Index->List[Count]);
        if( Result == 2 ) {
            //
            // couldn't map view inside; signal it to the caller
            //
            return 2;
        }
        if (Result == 0) {
            *Child = Index->List[Count];
        }
    }
    return(Result);
}


LONG
CmpDoCompareKeyName(
    PHHIVE          Hive,
    PUNICODE_STRING SearchName,
    HCELL_INDEX     Cell
    )
/*++

Routine Description:

    Do a compare of a name with a key.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    SearchName - pointer to name of key we are searching for

    Cell - cell of key we are to compare with

Return Value:

    0   = SearchName == KeyName (of Cell)

    -1  = SearchName < KeyName

    +1  = SearchName > KeyName

    +2  = Error (couldn't map bin)

--*/
{
    PCM_KEY_NODE    Pcan;
    UNICODE_STRING  KeyName;
    LONG            Result;

    Pcan = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
    if( Pcan == NULL ) {
        //
        // we couldn't map the bin containing this cell
        // return error, so the caller could safely bail out
        //
        return 2;
    }
    if (Pcan->Flags & KEY_COMP_NAME) {
        
        Result = CmpCompareCompressedName(SearchName,
                                        Pcan->Name,
                                        Pcan->NameLength,
                                        0);
    } else {
        KeyName.Buffer = &(Pcan->Name[0]);
        KeyName.Length = Pcan->NameLength;
        KeyName.MaximumLength = KeyName.Length;
        Result = RtlCompareUnicodeString(SearchName,
                                        &KeyName,
                                        TRUE);
    }
    
    HvReleaseCell(Hive, Cell);

    if( Result == 0 ) {
        //
        // match
        //
        return 0;
    }
    
    return (Result < 0) ? -1 : 1;
}


HCELL_INDEX
CmpFindSubKeyByNumber(
    PHHIVE          Hive,
    PCM_KEY_NODE    Node,
    ULONG           Number
    )
/*++

Routine Description:

    Find the Number'th entry in the index, starting from 0.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Node - pointer to key body which is parent of child of interest

    Number - ordinal of child key to return

Return Value:

    Cell of matching child key, or HCELL_NIL if none or error.

--*/
{
    PCM_KEY_INDEX   Index;
    HCELL_INDEX     Result = HCELL_NIL;

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpFindSubKeyByNumber:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p Node=%p Number=%08lx\n",Hive,Node,Number));

    if (Number < Node->SubKeyCounts[Stable]) {

        //
        // It's in the stable set
        //
        Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Stable]);
        if( Index == NULL ) {
            //
            // we couldn't map the bin containing this cell
            //
            return HCELL_NIL;
        }
        Result = CmpDoFindSubKeyByNumber(Hive, Index, Number);
        HvReleaseCell(Hive, Node->SubKeyLists[Stable]);
        return Result;

    } else if (Hive->StorageTypeCount > Volatile) {

        //
        // It's in the volatile set
        //
        Number = Number - Node->SubKeyCounts[Stable];
        if (Number < Node->SubKeyCounts[Volatile]) {

            Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Volatile]);
            if( Index == NULL ) {
                //
                // we couldn't map the bin containing this cell
                //
                return HCELL_NIL;
            }
            Result = CmpDoFindSubKeyByNumber(Hive, Index, Number);
            HvReleaseCell(Hive, Node->SubKeyLists[Volatile]);
            return Result;
        }
    }
    //
    // It's nowhere
    //
    return HCELL_NIL;
}


HCELL_INDEX
CmpDoFindSubKeyByNumber(
    PHHIVE          Hive,
    PCM_KEY_INDEX   Index,
    ULONG           Number
    )
/*++

Routine Description:

    Helper for CmpFindSubKeyByNumber,
    Find the Number'th entry in the index, starting from 0.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Index - root or leaf of the index

    Number - ordinal of child key to return

Return Value:

    Cell of requested entry. HCELL_NIL on resources problem

--*/
{
    ULONG           i;
    HCELL_INDEX     LeafCell = 0;
    PCM_KEY_INDEX   Leaf = NULL;
    PCM_KEY_FAST_INDEX FastIndex;
    HCELL_INDEX     Result;

    if (Index->Signature == CM_KEY_INDEX_ROOT) {

        //
        // step through root, till we find the right leaf
        //
        for (i = 0; i < Index->Count; i++) {
            if( i ) {
                ASSERT( Leaf!= NULL );
                ASSERT( LeafCell == Index->List[i-1] );
                HvReleaseCell(Hive,LeafCell);
            }
            LeafCell = Index->List[i];
            Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
            if( Leaf == NULL ) {
                //
                // we couldn't map the bin containing this cell
                //
                return HCELL_NIL;
            }
            if (Number < Leaf->Count) {
                if ( (Leaf->Signature == CM_KEY_FAST_LEAF) ||
                     (Leaf->Signature == CM_KEY_HASH_LEAF) ) {
                    FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
                    Result = FastIndex->List[Number].Cell;
                    HvReleaseCell(Hive,LeafCell);
                    return Result;
                } else {
                    Result = Leaf->List[Number];
                    HvReleaseCell(Hive,LeafCell);
                    return Result;
                }
            } else {
                Number = Number - Leaf->Count;
            }
        }
        ASSERT(FALSE);
    }
    ASSERT(Number < Index->Count);
    if ( (Index->Signature == CM_KEY_FAST_LEAF) ||
         (Index->Signature == CM_KEY_HASH_LEAF) ) {
        FastIndex = (PCM_KEY_FAST_INDEX)Index;
        return(FastIndex->List[Number].Cell);
    } else {
        return (Index->List[Number]);
    }
}

BOOLEAN
CmpRemoveSubKeyCellNoCellRef(
    PHHIVE          Hive,
    HCELL_INDEX     Parent,
    HCELL_INDEX     Child
    )
/*++

Routine Description:

    Removes a subkey by cell index; Also marks relevant data dirty.
    Intended for self healing process.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Parent - cell of key that will be parent of new key

    Child - key to delete from Paren't sub key list

Return Value:

    TRUE - it worked

    FALSE - resource problem

--*/
{
    PCM_KEY_NODE        Node = NULL;
    PCM_KEY_INDEX       Index = NULL;
    BOOLEAN             Result = TRUE;
    ULONG               i,j;
    HCELL_INDEX         LeafCell = 0;
    PCM_KEY_INDEX       Leaf = NULL;
    PCM_KEY_FAST_INDEX  FastIndex;

    CM_PAGED_CODE();

    Node = (PCM_KEY_NODE)HvGetCell(Hive,Parent);
    if( Node == NULL ) {
        Result = FALSE;
        goto Exit;
    }
    Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Stable]);
    if( Index == NULL ) {
        Result = FALSE;
        goto Exit;
    }
    if (Index->Signature == CM_KEY_INDEX_ROOT) {
        //
        // step through root, till we find the right leaf
        //
        for (i = 0; i < Index->Count; i++) {
            if( i ) {
                ASSERT( Leaf!= NULL );
                ASSERT( LeafCell == Index->List[i-1] );
                HvReleaseCell(Hive,LeafCell);
            }
            LeafCell = Index->List[i];
            Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
            if( Leaf == NULL ) {
                Result = FALSE;
                goto Exit;
            }
            for(j=0;j<Leaf->Count;j++) {
                if ( (Leaf->Signature == CM_KEY_FAST_LEAF) ||
                     (Leaf->Signature == CM_KEY_HASH_LEAF) ) {
                    FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
                    if( FastIndex->List[j].Cell == Child ) {
                        //
                        // found it!
                        //
                        HvReleaseCell(Hive,LeafCell);
                        HvMarkCellDirty(Hive,LeafCell,FALSE);
                        FastIndex->Count--;
                        RtlMoveMemory((PVOID)&(FastIndex->List[j]),
                                      (PVOID)&(FastIndex->List[j+1]),
                                      (FastIndex->Count - j) * sizeof(CM_INDEX));
                        goto DirtyParent;
                    }
                } else {
                    if( Leaf->List[j] == Child ) {
                        //
                        // found it!
                        //
                        HvReleaseCell(Hive,LeafCell);
                        HvMarkCellDirty(Hive,LeafCell,FALSE);
                        Leaf->Count--;
                        RtlMoveMemory((PVOID)&(Leaf->List[j]),
                                      (PVOID)&(Leaf->List[j+1]),
                                      (Leaf->Count - j) * sizeof(HCELL_INDEX));
                        goto DirtyParent;
                    }
                }
            }
        }
    } else {
        for(j=0;j<Index->Count;j++) {
            if ( (Index->Signature == CM_KEY_FAST_LEAF) ||
                 (Index->Signature == CM_KEY_HASH_LEAF) ) {
                FastIndex = (PCM_KEY_FAST_INDEX)Index;
                if( FastIndex->List[j].Cell == Child ) {
                    //
                    // found it!
                    //
                    RtlMoveMemory((PVOID)&(FastIndex->List[j]),
                                  (PVOID)&(FastIndex->List[j+1]),
                                  (FastIndex->Count - j) * sizeof(CM_INDEX));
				    HvMarkCellDirty(Hive,Node->SubKeyLists[Stable],FALSE);
				    Index->Count--;
                    goto DirtyParent;
                }
            } else {
                if( Index->List[j] == Child ) {
                    //
                    // found it!
                    //
                    RtlMoveMemory((PVOID)&(Index->List[j]),
                                  (PVOID)&(Index->List[j+1]),
                                  (Index->Count - j) * sizeof(HCELL_INDEX));
				    HvMarkCellDirty(Hive,Node->SubKeyLists[Stable],FALSE);
				    Index->Count--;
                    goto DirtyParent;
                }
            }
        }
    }
    ASSERT( FALSE );

DirtyParent:
    //
    // mark parent and index dirty and decrement index count.
    //
    HvMarkCellDirty(Hive,Parent,FALSE);
    Node->SubKeyCounts[Stable]--;
Exit:
    if( Index ) {
        ASSERT( Node );
        HvReleaseCell(Hive,Node->SubKeyLists[Stable]);
    }
    if( Node ) {
        HvReleaseCell(Hive,Parent);
    }
    return Result;
}

BOOLEAN
CmpAddSubKey(
    PHHIVE          Hive,
    HCELL_INDEX     Parent,
    HCELL_INDEX     Child
    )
/*++

Routine Description:

    Add a new child subkey to the subkey index for a cell.  The
    child MUST NOT already be present (bugcheck if so.)

    NOTE:   We expect Parent to already be marked dirty.
            We will mark stuff in Index dirty

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Parent - cell of key that will be parent of new key

    Child - new key to put in Paren't sub key list

Return Value:

    TRUE - it worked

    FALSE - resource problem

--*/
{
    PCM_KEY_NODE    pcell;
    HCELL_INDEX     WorkCell = HCELL_NIL;
    PCM_KEY_INDEX   Index;
    PCM_KEY_FAST_INDEX FastIndex;
    UNICODE_STRING  NewName;
    HCELL_INDEX     LeafCell;
    PHCELL_INDEX    RootPointer = NULL;
    ULONG           cleanup = 0;
    ULONG           Type = 0;
    BOOLEAN         IsCompressed;
    ULONG           i;
    HCELL_INDEX     CellToRelease = HCELL_NIL;

    CM_PAGED_CODE();

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpAddSubKey:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p Parent=%08lx Child=%08lx\n",Hive,Parent,Child));

    //
    // we have the lock exclusive or nobody is operating inside this hive
    //

    //
    // build a name string
    //
    pcell = (PCM_KEY_NODE)HvGetCell(Hive, Child);
    if( pcell == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        return FALSE;
    }

    if (pcell->Flags & KEY_COMP_NAME) {
        IsCompressed = TRUE;
        NewName.Length = CmpCompressedNameSize(pcell->Name, pcell->NameLength);
        NewName.MaximumLength = NewName.Length;
        NewName.Buffer = (Hive->Allocate)(NewName.Length, FALSE,CM_FIND_LEAK_TAG8);
        if (NewName.Buffer==NULL) {
            HvReleaseCell(Hive, Child);
            return(FALSE);
        }
        CmpCopyCompressedName(NewName.Buffer,
                              NewName.MaximumLength,
                              pcell->Name,
                              pcell->NameLength);
    } else {
        IsCompressed = FALSE;
        NewName.Length = pcell->NameLength;
        NewName.MaximumLength = pcell->NameLength;
        NewName.Buffer = &(pcell->Name[0]);
    }
    HvReleaseCell(Hive, Child);

    pcell = (PCM_KEY_NODE)HvGetCell(Hive, Parent);
    if( pcell == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        goto ErrorExit;
    }

    Type = HvGetCellType(Child);

    if (pcell->SubKeyCounts[Type] == 0) {

        //
        // we must allocate a leaf
        //
        WorkCell = HvAllocateCell(Hive, sizeof(CM_KEY_FAST_INDEX), Type,(HvGetCellType(Parent)==Type)?Parent:HCELL_NIL);
        if (WorkCell == HCELL_NIL) {
            goto ErrorExit;
        }
        Index = (PCM_KEY_INDEX)HvGetCell(Hive, WorkCell);
        if( Index == NULL ) {
            //
            // we couldn't map the bin containing this cell
            // this shouldn't happen 'cause we just allocated this
            // cell (i.e. bin is PINNED in memory ! )
            //
            ASSERT( FALSE );
            goto ErrorExit;
        }
        if( UseHashIndex(Hive) ) {
            Index->Signature = CM_KEY_HASH_LEAF;
        } else if( UseFastIndex(Hive) ) {
            Index->Signature = CM_KEY_FAST_LEAF;
        } else {
            Index->Signature = CM_KEY_INDEX_LEAF;
        }
        Index->Count = 0;
        pcell->SubKeyLists[Type] = WorkCell;
        cleanup = 1;
    } else {

        Index = (PCM_KEY_INDEX)HvGetCell(Hive, pcell->SubKeyLists[Type]);
        if( Index == NULL ) {
            //
            // we couldn't map the bin containing this cell
            //
            goto ErrorExit;
        }
        CellToRelease = pcell->SubKeyLists[Type];

        if ( (Index->Signature == CM_KEY_FAST_LEAF) &&
             (Index->Count >= (CM_MAX_FAST_INDEX)) ) {
            //
            // We must change fast index to a slow index to accommodate
            // growth.
            //
            if( !HvMarkCellDirty(Hive, pcell->SubKeyLists[Type],FALSE) ) {
                goto ErrorExit;
            }

            FastIndex = (PCM_KEY_FAST_INDEX)Index;
            for (i=0; i<Index->Count; i++) {
                Index->List[i] = FastIndex->List[i].Cell;
            }
            Index->Signature = CM_KEY_INDEX_LEAF;

        } else if (((Index->Signature == CM_KEY_INDEX_LEAF) ||
                    (Index->Signature == CM_KEY_HASH_LEAF)) &&
                   (Index->Count >= (CM_MAX_INDEX - 1) )) {
            //
            // We must change flat entry to a root/leaf tree
            //
            WorkCell = HvAllocateCell(
                         Hive,
                         sizeof(CM_KEY_INDEX) + sizeof(HCELL_INDEX), // allow for 2
                         Type,
                         (HvGetCellType(Parent)==Type)?Parent:HCELL_NIL
                         );
            if (WorkCell == HCELL_NIL) {
                goto ErrorExit;
            }

            Index = (PCM_KEY_INDEX)HvGetCell(Hive, WorkCell);
            if( Index == NULL ) {
                //
                // we couldn't map the bin containing this cell
                // this shouldn't happen 'cause we just allocated this
                // cell (i.e. bin is PINNED in memory
                ASSERT( FALSE );
                goto ErrorExit;
            }

            Index->Signature = CM_KEY_INDEX_ROOT;
            Index->Count = 1;
            Index->List[0] = pcell->SubKeyLists[Type];
            pcell->SubKeyLists[Type] = WorkCell;
            cleanup = 2;
        }
    }
    LeafCell = pcell->SubKeyLists[Type];

    //
    // LeafCell is target for add, or perhaps root
    // Index is pointer to fast leaf, slow Leaf or Root, whichever applies
    //
    if (Index->Signature == CM_KEY_INDEX_ROOT) {
        LeafCell = CmpSelectLeaf(Hive, pcell, &NewName, Type, &RootPointer);
        if (LeafCell == HCELL_NIL) {
            goto ErrorExit;
        }
    }

    //
    // Add new cell to Leaf, update pointers
    //
    LeafCell = CmpAddToLeaf(Hive, LeafCell, Child, &NewName);

    if (LeafCell == HCELL_NIL) {
        goto ErrorExit;
    }

    pcell->SubKeyCounts[Type] += 1;

    if (RootPointer != NULL) {
        *RootPointer = LeafCell;
    } else {
        pcell->SubKeyLists[Type] = LeafCell;
    }

    if (IsCompressed) {
        (Hive->Free)(NewName.Buffer, NewName.Length);
    }

    if( WorkCell != HCELL_NIL ) {
        HvReleaseCell(Hive, WorkCell);
    }
    if( CellToRelease != HCELL_NIL ) {
        HvReleaseCell(Hive, CellToRelease);
    }
    HvReleaseCell(Hive, Parent);
    return TRUE;
ErrorExit:
    if( WorkCell != HCELL_NIL ) {
        HvReleaseCell(Hive, WorkCell);
    }
    if( CellToRelease != HCELL_NIL ) {
        HvReleaseCell(Hive, CellToRelease);
    }
    if (IsCompressed) {
        (Hive->Free)(NewName.Buffer, NewName.Length);
    }

    switch (cleanup) {
    case 1:
        HvFreeCell(Hive, pcell->SubKeyLists[Type]);
        pcell->SubKeyLists[Type] = HCELL_NIL;
        break;

    case 2:
        Index = (PCM_KEY_INDEX)HvGetCell(Hive, pcell->SubKeyLists[Type]);
        if( Index == NULL ) {
            //
            // we couldn't map the bin containing this cell
            // this shouldn't happen 'cause we just allocated this
            // cell (i.e. bin is PINNED in memory). 
            // But ... better safe than sorry
            //
            ASSERT( FALSE );
            HvReleaseCell(Hive, Parent);
            return FALSE;
        }
        WorkCell = Index->List[0];
        {
            HCELL_INDEX CellTemp = pcell->SubKeyLists[Type];
            HvFreeCell(Hive, pcell->SubKeyLists[Type]);
            pcell->SubKeyLists[Type] = WorkCell;
            HvReleaseCell(Hive, CellTemp);
        }
        break;
    }

    HvReleaseCell(Hive, Parent);
    return  FALSE;
}


HCELL_INDEX
CmpAddToLeaf(
    PHHIVE          Hive,
    HCELL_INDEX     LeafCell,
    HCELL_INDEX     NewKey,
    PUNICODE_STRING NewName
    )
/*++

Routine Description:

    Insert a new subkey into a Leaf index. Supports both fast and slow
    leaf indexes and will determine which sort of index the given leaf is.

    NOTE:   We expect Root to already be marked dirty by caller if non NULL.
            We expect Leaf to always be marked dirty by caller.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    LeafCell - cell of index leaf node we are to add entry too

    NewKey - cell of KEY_NODE we are to add

    NewName - pointer to unicode string with name to we are to add

Return Value:

    HCELL_NIL - some resource problem

    Else - cell of Leaf index when are done, caller is expected to
            set this into Root index or Key body.

--*/
{
    PCM_KEY_INDEX   Leaf;
    PCM_KEY_FAST_INDEX FastLeaf;
    ULONG           Size;
    ULONG           OldSize;
    ULONG           freecount;
    HCELL_INDEX     NewCell;
    HCELL_INDEX     Child;
    ULONG           Select;
    LONG            Result;
    ULONG           EntrySize;
    ULONG           i;

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpAddToLeaf:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p LeafCell=%08lx NewKey=%08lx\n",Hive,LeafCell,NewKey));

    //
    // we have the lock exclusive or nobody is operating inside this hive
    //

    if (!HvMarkCellDirty(Hive, LeafCell, FALSE)) {
        return HCELL_NIL;
    }

    //
    // compute number free slots left in the leaf
    //
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if( Leaf == NULL ) {
        //
        // we couldn't map the bin containing this cell
        // this shouldn't happen as marking dirty means 
        // PINNING the view into memory
        //
        ASSERT( FALSE );
        return HCELL_NIL;
    }

    // release the cell here; as the view is pinned
    HvReleaseCell(Hive, LeafCell);

    if (Leaf->Signature == CM_KEY_INDEX_LEAF) {
        FastLeaf = NULL;
        EntrySize = sizeof(HCELL_INDEX);
    } else {
        ASSERT( (Leaf->Signature == CM_KEY_FAST_LEAF) ||
                (Leaf->Signature == CM_KEY_HASH_LEAF)
            );
        FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
        EntrySize = sizeof(CM_INDEX);
    }
    OldSize = HvGetCellSize(Hive, Leaf);
    Size = OldSize - ((EntrySize * Leaf->Count) +
              FIELD_OFFSET(CM_KEY_INDEX, List));
    freecount = Size / EntrySize;

    //
    // grow the leaf if it isn't big enough
    //
    NewCell = LeafCell;
    if (freecount < 1) {
        Size = OldSize + OldSize / 2;
        if (Size < (OldSize + EntrySize)) {
            Size = OldSize + EntrySize;
        }
        NewCell = HvReallocateCell(Hive, LeafCell, Size);
        if (NewCell == HCELL_NIL) {
            return HCELL_NIL;
        }
        Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, NewCell);
        if( Leaf == NULL ) {
            //
            // we couldn't map the bin containing this cell
            // this shouldn't happen 'cause we just allocated this
            // cell (i.e. bin is PINNED in memory)
            //
            ASSERT( FALSE );
            return HCELL_NIL;
        }
        // release the cell here; as the view is pinned
        HvReleaseCell(Hive, NewCell);
        if (FastLeaf != NULL) {
            FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
        }
    }

    //
    // Find where to put the new entry
    //
    Select = CmpFindSubKeyInLeaf(Hive, Leaf, NewName, &Child);
    if( INVALID_INDEX & Select ) {
        //
        // couldn't map view
        // 
        return HCELL_NIL;
    }

    ASSERT(Child == HCELL_NIL);

    //
    // Select is the index in List of the entry nearest where the
    // new entry should go.
    // Decide whether the new entry goes before or after Offset entry,
    // and then ripple copy and set.
    // If Select == Count, then the leaf is empty, so simply set our entry
    //
    if (Select != Leaf->Count) {

        Result = CmpCompareInIndex(Hive,
                                   NewName,
                                   Select,
                                   Leaf,
                                   &Child);
        if( Result == 2 ) {
            //
            // couldn't map view inside; bail out
            //
            return HCELL_NIL;
        }

        ASSERT(Result != 0);

        //
        // Result -1 - NewName/NewKey less than selected key, insert before
        //        +1 - NewName/NewKey greater than selected key, insert after
        //
        if (Result > 0) {
            ASSERT( Result == 1 );
            Select++;
        }

        if (Select != Leaf->Count) {

            //
            // ripple copy to make space and insert
            //

            if (FastLeaf != NULL) {
                RtlMoveMemory((PVOID)&(FastLeaf->List[Select+1]),
                              (PVOID)&(FastLeaf->List[Select]),
                              sizeof(CM_INDEX)*(FastLeaf->Count - Select));
            } else {
                RtlMoveMemory((PVOID)&(Leaf->List[Select+1]),
                              (PVOID)&(Leaf->List[Select]),
                              sizeof(HCELL_INDEX)*(Leaf->Count - Select));
            }
        }
    }
    if (FastLeaf != NULL) {
        FastLeaf->List[Select].Cell = NewKey;
        if( FastLeaf->Signature == CM_KEY_HASH_LEAF ) {
            //
            // Hash leaf; store the HashKey
            //
            FastLeaf->List[Select].HashKey = CmpComputeHashKey(0,NewName
#if DBG
                                                                , FALSE
#endif
                );
        } else {
            FastLeaf->List[Select].NameHint[0] = 0;
            FastLeaf->List[Select].NameHint[1] = 0;
            FastLeaf->List[Select].NameHint[2] = 0;
            FastLeaf->List[Select].NameHint[3] = 0;
            if (NewName->Length/sizeof(WCHAR) < 4) {
                i = NewName->Length/sizeof(WCHAR);
            } else {
                i = 4;
            }
            do {
                if ((USHORT)NewName->Buffer[i-1] > (UCHAR)-1) {
                    //
                    // Can't compress this name. Leave NameHint[0]==0
                    // to force the name to be looked up in the key.
                    //
                    break;
                }
                FastLeaf->List[Select].NameHint[i-1] = (UCHAR)NewName->Buffer[i-1];
                i--;
            } while ( i>0 );
        }
    } else {
        Leaf->List[Select] = NewKey;
    }
    Leaf->Count += 1;
    
	return NewCell;
}


HCELL_INDEX
CmpSelectLeaf(
    PHHIVE          Hive,
    PCM_KEY_NODE    ParentKey,
    PUNICODE_STRING NewName,
    HSTORAGE_TYPE   Type,
    PHCELL_INDEX    *RootPointer
    )
/*++

Routine Description:

    This routine is only called if the subkey index for a cell is NOT
    simply a single Leaf index block.

    It selects the Leaf index block to which a new entry is to be
    added.  It may create this block by splitting an existing Leaf
    block.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    ParentKey - mapped pointer to parent key

    NewName - pointer to unicode string naming entry to add

    Type - Stable or Volatile, describes Child's storage

    RootPointer - pointer to variable to receive address of HCELL_INDEX
                that points to Leaf block returned as function argument.
                Used for updates.

Return Value:

    HCELL_NIL - resource problem

    Else, cell index of Leaf index block to add entry to

--*/
{
    HCELL_INDEX         LeafCell;
    HCELL_INDEX         WorkCell;
    PCM_KEY_INDEX       Index;
    PCM_KEY_INDEX       Leaf;
    PCM_KEY_FAST_INDEX  FastLeaf;
    ULONG               RootSelect;
    LONG                Result;
    HV_TRACK_CELL_REF   CellRef = {0};

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpSelectLeaf:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p ParentKey=%p\n", Hive, ParentKey));

    //
    // we have the lock exclusive or nobody is operating inside this hive
    //

    //
    // Force root to always be dirty, since we'll either grow it or edit it,
    // and it needs to be marked dirty for BOTH cases.  (Edit may not
    // occur until after we leave
    //
    if (! HvMarkCellDirty(Hive, ParentKey->SubKeyLists[Type], FALSE)) {
        return HCELL_NIL;
    }

    //
    // must find the proper leaf
    //
    Index = (PCM_KEY_INDEX)HvGetCell(Hive, ParentKey->SubKeyLists[Type]);
    if( Index == NULL ) {
        //
        // we couldn't map the bin containing this cell
        // this shouldn't happen as marking dirty means 
        // PINNING the view into memory
        //
        ASSERT( FALSE );
        return HCELL_NIL;
    }
    ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);

    if( !HvTrackCellRef(&CellRef,Hive,ParentKey->SubKeyLists[Type]) ) {
        goto ErrorExit;
    }

    while (TRUE) {

        RootSelect = CmpFindSubKeyInRoot(Hive, Index, NewName, &LeafCell);
        if( INVALID_INDEX & RootSelect ) {
            //
            // couldn't map view inside; bail out
            //
            goto ErrorExit;
        }

        if (LeafCell == HCELL_NIL) {

            //
            // Leaf of interest is somewhere near RootSelect
            //
            // . Always use lowest order leaf we can get away with
            // . Never split a leaf if there's one with space we can use
            // . When we split a leaf, we have to repeat search
            //
            // If (NewKey is below lowest key in selected)
            //    If there's a Leaf below selected with space
            //       use the leaf below
            //    else
            //       use the leaf (split it if not enough space)
            // Else
            //    must be above highest key in selected, less than
            //      lowest key in Leaf to right of selected
            //       if space in selected
            //          use selected
            //       else if space in leaf above selected
            //          use leaf above
            //       else
            //          split selected
            //
            LeafCell = Index->List[RootSelect];
            Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
            if( Leaf == NULL ) {
                //
                // we couldn't map the bin containing this cell
                //
                goto ErrorExit;
            }
            if( !HvTrackCellRef(&CellRef,Hive,LeafCell) ) {
                goto ErrorExit;
            }

            if( (Leaf->Signature == CM_KEY_FAST_LEAF)   ||
                (Leaf->Signature == CM_KEY_HASH_LEAF) ) {
                FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
                WorkCell = FastLeaf->List[0].Cell;
            } else {
                ASSERT( Leaf->Signature == CM_KEY_INDEX_LEAF );
                WorkCell = Leaf->List[0];
            }

            Result = CmpDoCompareKeyName(Hive, NewName, WorkCell);
            if( Result == 2 ) {
                //
                // couldn't map view inside; bail out
                // 
                goto ErrorExit;
            }
            ASSERT(Result != 0);

            if (Result < 0) {

                //
                // new is off the left end of Selected
                //
                if (RootSelect > 0) {

                    //
                    // there's a Leaf to the left, try to use it
                    //
                    LeafCell = Index->List[RootSelect-1];
                    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
                    if( Leaf == NULL ) {
                        //
                        // we couldn't map the bin containing this cell
                        //
                        goto ErrorExit;
                    }
                    if( !HvTrackCellRef(&CellRef,Hive,LeafCell) ) {
                        goto ErrorExit;
                    }

                    if (Leaf->Count < (CM_MAX_INDEX - 1)) {
                        RootSelect--;
                        *RootPointer = &(Index->List[RootSelect]);
                        break;
                    }

                } else {
                    //
                    // new key is off the left end of the leftmost leaf.
                    // Use the leftmost leaf, if there's enough room
                    //
                    LeafCell = Index->List[0];
                    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
                    if( Leaf == NULL ) {
                        //
                        // we couldn't map the bin containing this cell
                        //
                        goto ErrorExit;
                    }
                    if( !HvTrackCellRef(&CellRef,Hive,LeafCell) ) {
                        goto ErrorExit;
                    }
                    if (Leaf->Count < (CM_MAX_INDEX - 1)) {
                        *RootPointer = &(Index->List[0]);
                        break;
                    }
                }

                //
                // else fall to split case
                //

            } else {

                //
                // since new key is not in a Leaf, and is not off
                // the left end of the ResultSelect Leaf, it must
                // be off the right end.
                //
                LeafCell = Index->List[RootSelect];
                Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
                if( Leaf == NULL ) {
                    //
                    // we couldn't map the bin containing this cell
                    //
                    goto ErrorExit;
                }
                if( !HvTrackCellRef(&CellRef,Hive,LeafCell) ) {
                    goto ErrorExit;
                }

                if (Leaf->Count < (CM_MAX_INDEX - 1)) {
                    *RootPointer = &(Index->List[RootSelect]);
                    break;
                }

                //
                // No space, see if there's a leaf to the rigth
                // and if it has space
                //
                if (RootSelect < (ULONG)(Index->Count - 1)) {

                    LeafCell = Index->List[RootSelect+1];
                    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
                    if( Leaf == NULL ) {
                        //
                        // we couldn't map the bin containing this cell
                        //
                        goto ErrorExit;
                    }
                    if( !HvTrackCellRef(&CellRef,Hive,LeafCell) ) {
                        goto ErrorExit;
                    }

                    if (Leaf->Count < (CM_MAX_INDEX - 1)) {
                        *RootPointer = &(Index->List[RootSelect+1]);
                        break;
                    }
                }

                //
                // fall to split case
                //
            }

        } else {   // LeafCell != HCELL_NIL

            //
            // Since newkey cannot already be in tree, it must be
            // greater than the bottom of Leaf and less than the top,
            // therefore it must go in Leaf.  If no space, split it.
            //
            Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
            if( Leaf == NULL ) {
                //
                // we couldn't map the bin containing this cell
                //
                goto ErrorExit;
            }

            if( !HvTrackCellRef(&CellRef,Hive,LeafCell) ) {
                goto ErrorExit;
            }

            if (Leaf->Count < (CM_MAX_INDEX - 1)) {

                *RootPointer = &(Index->List[RootSelect]);
                break;
            }

            //
            // fall to split case
            //
        }

        //
        // either no neigbor, or no space in neighbor, so split
        //
        WorkCell = CmpSplitLeaf(
                        Hive,
                        ParentKey->SubKeyLists[Type],       // root cell
                        RootSelect,
                        Type
                        );
        if (WorkCell == HCELL_NIL) {
            goto ErrorExit;
        }

        ParentKey->SubKeyLists[Type] = WorkCell;
        Index = (PCM_KEY_INDEX)HvGetCell(Hive, WorkCell);
        if( Index == NULL ) {
            //
            // we couldn't map the bin containing this cell
            //
            goto ErrorExit;
        }

        if( !HvTrackCellRef(&CellRef,Hive,WorkCell) ) {
            goto ErrorExit;
        }

        ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);

    } // while(true)
    HvReleaseFreeCellRefArray(&CellRef);
    return LeafCell;

ErrorExit:
    HvReleaseFreeCellRefArray(&CellRef);
    return HCELL_NIL;
}


HCELL_INDEX
CmpSplitLeaf(
    PHHIVE          Hive,
    HCELL_INDEX     RootCell,
    ULONG           RootSelect,
    HSTORAGE_TYPE   Type
    )
/*++

Routine Description:

    Split the Leaf index block specified by RootSelect, causing both
    of the split out Leaf blocks to appear in the Root index block
    specified by RootCell.

    Caller is expected to have marked old root cell dirty.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    RootCell - cell of the Root index block of index being grown

    RootSelect - indicates which child of Root to split

    Type - Stable or Volatile

Return Value:

    HCELL_NIL - some resource problem

    Else - cell of new (e.g. reallocated) Root index block

--*/
{
    PCM_KEY_INDEX   Root;
    HCELL_INDEX     LeafCell;
    PCM_KEY_INDEX   Leaf;
    HCELL_INDEX     NewLeafCell;
    PCM_KEY_INDEX   NewLeaf;
	PCM_KEY_FAST_INDEX	FastLeaf;
    ULONG           Size;
    ULONG           freecount;
    USHORT          OldCount;
    USHORT          KeepCount;
    USHORT          NewCount;
    USHORT          ElemSize;
    HV_TRACK_CELL_REF   CellRef = {0};

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"CmpSplitLeaf:\n\t"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_INDEX,"Hive=%p RootCell=%08lx RootSelect\n", Hive, RootCell, RootSelect));

    //
    // we have the lock exclusive or nobody is operating inside this hive
    //

    //
    // allocate new Leaf index block
    //
    Root = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);
    if( Root == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        return HCELL_NIL;
    }

    if( !HvTrackCellRef(&CellRef,Hive,RootCell) ) {
        goto ErrorExit;
    }

    LeafCell = Root->List[RootSelect];
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if( Leaf == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        goto ErrorExit;
    }
    OldCount = Leaf->Count;

    if( !HvTrackCellRef(&CellRef,Hive,LeafCell) ) {
        goto ErrorExit;
    }

    KeepCount = (USHORT)(OldCount / 2);     // # of entries to keep in org. Leaf
    NewCount = (OldCount - KeepCount);      // # of entries to move

    if( UseHashIndex(Hive) ) {
        ASSERT( Leaf->Signature == CM_KEY_HASH_LEAF );
        ElemSize = sizeof(CM_INDEX);
    } else {
        ElemSize = sizeof(HCELL_INDEX);
        ASSERT( Leaf->Signature == CM_KEY_INDEX_LEAF );
    }

    ASSERT( FIELD_OFFSET(CM_KEY_INDEX, List) == FIELD_OFFSET(CM_KEY_FAST_INDEX, List) );
    Size = (ElemSize * NewCount) +
            FIELD_OFFSET(CM_KEY_INDEX, List) + 1;   // +1 to assure room for add

    if (!HvMarkCellDirty(Hive, LeafCell,FALSE)) {
        goto ErrorExit;
    }
    //
    //
    //
    ASSERT( (HvGetCellType(LeafCell) == (ULONG)Type) );

    NewLeafCell = HvAllocateCell(Hive, Size, Type,LeafCell);
    if (NewLeafCell == HCELL_NIL) {
        goto ErrorExit;
    }
    NewLeaf = (PCM_KEY_INDEX)HvGetCell(Hive, NewLeafCell);
    if( NewLeaf == NULL ) {
        //
        // we couldn't map the bin containing this cell
        // this shouldn't happen as we just allocated this cell
        // so it's bin should be PINNED into memory
        //
        ASSERT( FALSE );
        HvFreeCell(Hive, NewLeafCell);
        goto ErrorExit;
    }
    // release it right here as is dirty (view is pinned)
    HvReleaseCell(Hive,NewLeafCell);

    if( UseHashIndex(Hive) ) {
        NewLeaf->Signature = CM_KEY_HASH_LEAF;
    } else {
        NewLeaf->Signature = CM_KEY_INDEX_LEAF;
    }

    //
    // compute number of free slots left in the root
    //
    Size = HvGetCellSize(Hive, Root);
    Size = Size - ((sizeof(HCELL_INDEX) * Root->Count) +
              FIELD_OFFSET(CM_KEY_INDEX, List));
    freecount = Size / sizeof(HCELL_INDEX);


    //
    // grow the root if it isn't big enough
    //
    if (freecount < 1) {
        Size = HvGetCellSize(Hive, Root) + sizeof(HCELL_INDEX);
        RootCell = HvReallocateCell(Hive, RootCell, Size);
        if (RootCell == HCELL_NIL) {
            HvFreeCell(Hive, NewLeafCell);
            goto ErrorExit;
        }
        Root = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);
        if( Root == NULL ) {
            //
            // we couldn't map the bin containing this cell
            // this shouldn't happen as we just allocated this cell
            // so it's bin should be PINNED into memory
            //
            ASSERT( FALSE );
            HvFreeCell(Hive, NewLeafCell);
            goto ErrorExit;
        }
        if( !HvTrackCellRef(&CellRef,Hive,RootCell) ) {
            goto ErrorExit;
        }
    }


    //
    // copy data from one Leaf to the other
    //
    //
    if( UseHashIndex(Hive) ) {
		FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
		RtlMoveMemory(
			(PVOID)&(NewLeaf->List[0]),
			(PVOID)&(FastLeaf->List[KeepCount]),
			ElemSize * NewCount
			);
	} else {
		RtlMoveMemory(
			(PVOID)&(NewLeaf->List[0]),
			(PVOID)&(Leaf->List[KeepCount]),
			ElemSize * NewCount
			);
	}

    ASSERT(KeepCount != 0);
    ASSERT(NewCount  != 0);

    Leaf->Count = KeepCount;
    NewLeaf->Count = NewCount;


    //
    // make an open slot in the root
    //
    if (RootSelect < (ULONG)(Root->Count-1)) {
        RtlMoveMemory(
            (PVOID)&(Root->List[RootSelect+2]),
            (PVOID)&(Root->List[RootSelect+1]),
            (Root->Count - (RootSelect + 1)) * sizeof(HCELL_INDEX)
            );
    }

    //
    // update the root
    //
    Root->Count += 1;
    Root->List[RootSelect+1] = NewLeafCell;

    HvReleaseFreeCellRefArray(&CellRef);
    return RootCell;

ErrorExit:
    HvReleaseFreeCellRefArray(&CellRef);
    return HCELL_NIL;
}


BOOLEAN
CmpMarkIndexDirty(
    PHHIVE          Hive,
    HCELL_INDEX     ParentKey,
    HCELL_INDEX     TargetKey
    )
/*++

Routine Description:

    Mark as dirty relevant cells of a subkey index.  The Leaf that
    points to TargetKey, and the Root index block, if applicable,
    will be marked dirty.  This call assumes we are setting up
    for a subkey delete.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    ParentKey - key from whose subkey list delete is to be performed

    TargetKey - key being deleted

Return Value:

    TRUE - it worked, FALSE - it didn't, some resource problem

--*/
{
    PCM_KEY_NODE    pcell;
    ULONG           i;
    HCELL_INDEX     IndexCell;
    PCM_KEY_INDEX   Index;
    HCELL_INDEX     Child = HCELL_NIL;
    UNICODE_STRING  SearchName;
    BOOLEAN         IsCompressed;
    HCELL_INDEX     CellToRelease = HCELL_NIL;


    pcell = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
    if( pcell == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        return FALSE;
    }

    if (pcell->Flags & KEY_COMP_NAME) {
        IsCompressed = TRUE;
        SearchName.Length = CmpCompressedNameSize(pcell->Name, pcell->NameLength);
        SearchName.MaximumLength = SearchName.Length;
        SearchName.Buffer = ExAllocatePool(PagedPool, SearchName.Length);
        if (SearchName.Buffer==NULL) {
            HvReleaseCell(Hive, TargetKey);
            return(FALSE);
        }
        CmpCopyCompressedName(SearchName.Buffer,
                              SearchName.MaximumLength,
                              pcell->Name,
                              pcell->NameLength);
    } else {
        IsCompressed = FALSE;
        SearchName.Length = pcell->NameLength;
        SearchName.MaximumLength = pcell->NameLength;
        SearchName.Buffer = &(pcell->Name[0]);
    }

    HvReleaseCell(Hive, TargetKey);

    pcell = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
    if( pcell == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        goto ErrorExit;
    }

    for (i = 0; i < Hive->StorageTypeCount; i++) {
        if (pcell->SubKeyCounts[i] != 0) {
            ASSERT(HvIsCellAllocated(Hive, pcell->SubKeyLists[i]));
            IndexCell = pcell->SubKeyLists[i];
            if( CellToRelease != HCELL_NIL ) {
                HvReleaseCell(Hive, CellToRelease);
                CellToRelease = HCELL_NIL;
            }
            Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
            if( Index == NULL ) {
                //
                // we couldn't map the bin containing this cell
                //
                goto ErrorExit;
            }
            CellToRelease = IndexCell;

            if (Index->Signature == CM_KEY_INDEX_ROOT) {

                //
                // target even in index?
                //
                if( INVALID_INDEX & CmpFindSubKeyInRoot(Hive, Index, &SearchName, &Child) ) {
                    //
                    // couldn't map view inside; bail out
                    //
                    goto ErrorExit;
                }

                if (Child == HCELL_NIL) {
                    continue;
                }

                //
                // mark root dirty
                //
                if (! HvMarkCellDirty(Hive, IndexCell, FALSE)) {
                    goto ErrorExit;
                }

                if( CellToRelease != HCELL_NIL ) {
                    HvReleaseCell(Hive, CellToRelease);
                    CellToRelease = HCELL_NIL;
                }
                IndexCell = Child;
                Index = (PCM_KEY_INDEX)HvGetCell(Hive, Child);
                if( Index == NULL ) {
                    //
                    // we couldn't map the bin containing this cell
                    //
                    goto ErrorExit;
                }

                CellToRelease = Child;

            }
            ASSERT((Index->Signature == CM_KEY_INDEX_LEAF)  ||
                   (Index->Signature == CM_KEY_FAST_LEAF)   ||
                   (Index->Signature == CM_KEY_HASH_LEAF)
                   );

            if( INVALID_INDEX & CmpFindSubKeyInLeaf(Hive, Index, &SearchName, &Child) ) {
                //
                // couldn't map view
                // 
                goto ErrorExit;
            }

            if (Child != HCELL_NIL) {
                if (IsCompressed) {
                    ExFreePool(SearchName.Buffer);
                }
                // cleanup
                HvReleaseCell(Hive, ParentKey);
                if( CellToRelease != HCELL_NIL ) {
                    HvReleaseCell(Hive, CellToRelease);
                }
                return(HvMarkCellDirty(Hive, IndexCell, FALSE));
            }
        }
    }

ErrorExit:
    if( pcell!= NULL ) {
        HvReleaseCell(Hive, ParentKey);
    }
    if( CellToRelease != HCELL_NIL ) {
        HvReleaseCell(Hive, CellToRelease);
    }

    if (IsCompressed) {
        ExFreePool(SearchName.Buffer);
    }
    return FALSE;
}


BOOLEAN
CmpRemoveSubKey(
    PHHIVE          Hive,
    HCELL_INDEX     ParentKey,
    HCELL_INDEX     TargetKey
    )
/*++

Routine Description:

    Remove the subkey TargetKey refers to from ParentKey's list.

    NOTE:   Assumes that caller has marked relevant cells dirty,
            see CmpMarkIndexDirty.

Arguments:

    Hive - pointer to hive control structure for hive of interest

    ParentKey - key from whose subkey list delete is to be performed

    TargetKey - key being deleted

Return Value:

    TRUE - it worked, FALSE - it didn't, some resource problem

--*/
{
    PCM_KEY_NODE    pcell;
    HCELL_INDEX     LeafCell;
    PCM_KEY_INDEX   Leaf;
    PCM_KEY_FAST_INDEX FastIndex;
    HCELL_INDEX     RootCell = HCELL_NIL;
    PCM_KEY_INDEX   Root = NULL;
    HCELL_INDEX     Child;
    ULONG           Type;
    ULONG           RootSelect;
    ULONG           LeafSelect;
    UNICODE_STRING  SearchName;
    BOOLEAN         IsCompressed;
    WCHAR           CompressedBuffer[50];
    BOOLEAN         Result;
    HCELL_INDEX     CellToRelease1 = HCELL_NIL,CellToRelease2  = HCELL_NIL;

    pcell = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
    if( pcell == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        return FALSE;
    }

    ASSERT_CELL_DIRTY(Hive,TargetKey);

    //
    // release the cell here; as key is dirty/pinned
    //
    HvReleaseCell(Hive, TargetKey);

    if (pcell->Flags & KEY_COMP_NAME) {
        IsCompressed = TRUE;
        SearchName.Length = CmpCompressedNameSize(pcell->Name, pcell->NameLength);
        SearchName.MaximumLength = SearchName.Length;
        if (SearchName.MaximumLength > sizeof(CompressedBuffer)) {
            SearchName.Buffer = ExAllocatePool(PagedPool, SearchName.Length);
            if (SearchName.Buffer==NULL) {
                return(FALSE);
            }
        } else {
            SearchName.Buffer = CompressedBuffer;
        }
        CmpCopyCompressedName(SearchName.Buffer,
                              SearchName.MaximumLength,
                              pcell->Name,
                              pcell->NameLength);
    } else {
        IsCompressed = FALSE;
        SearchName.Length = pcell->NameLength;
        SearchName.MaximumLength = pcell->NameLength;
        SearchName.Buffer = &(pcell->Name[0]);
    }

    pcell = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
    if( pcell == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        Result = FALSE;
        goto Exit;
    }

    ASSERT_CELL_DIRTY(Hive,ParentKey);

    //
    // release the cell here; as key is dirty/pinned
    //
    HvReleaseCell(Hive, ParentKey);

    Type = HvGetCellType(TargetKey);

    ASSERT(pcell->SubKeyCounts[Type] != 0);
    ASSERT(HvIsCellAllocated(Hive, pcell->SubKeyLists[Type]));

    LeafCell = pcell->SubKeyLists[Type];
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if( Leaf == NULL ) {
        //
        // we couldn't map the bin containing this cell
        //
        Result = FALSE;
        goto Exit;
    }

    CellToRelease1 = LeafCell;

    RootSelect = INVALID_INDEX; // only needed for the compiler W4 option

    if (Leaf->Signature == CM_KEY_INDEX_ROOT) {
        RootSelect = CmpFindSubKeyInRoot(Hive, Leaf, &SearchName, &Child);

        if( INVALID_INDEX & RootSelect ) {
            //
            // couldn't map view inside; bail out
            //
            Result = FALSE;
            goto Exit;
        }
        ASSERT(Child != FALSE);

        Root = Leaf;
        RootCell = LeafCell;
        LeafCell = Child;
        Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
        if( Leaf == NULL ) {
            //
            // we couldn't map the bin containing this cell
            //
            Result = FALSE;
            goto Exit;
        }
        CellToRelease2  = LeafCell;

    }

    ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF)   ||
           (Leaf->Signature == CM_KEY_FAST_LEAF)    ||
           (Leaf->Signature == CM_KEY_HASH_LEAF)
           );

    LeafSelect = CmpFindSubKeyInLeaf(Hive, Leaf, &SearchName, &Child);
    if( INVALID_INDEX & LeafSelect ) {
        //
        // couldn't map view
        // 
        Result = FALSE;
        goto Exit;
    }

    ASSERT(Child != HCELL_NIL);


    //
    // Leaf points to Index Leaf block
    // Child is Index Leaf block cell
    // LeafSelect is Index for List[]
    //
    pcell->SubKeyCounts[Type] -= 1;

    Leaf->Count -= 1;
    if (Leaf->Count == 0) {

        //
        // Empty Leaf, drop it.
        //
        HvFreeCell(Hive, LeafCell);

        if (Root != NULL) {

            Root->Count -= 1;
            if (Root->Count == 0) {

                //
                // Root is empty, free it too.
                //
                HvFreeCell(Hive, RootCell);
                pcell->SubKeyLists[Type] = HCELL_NIL;

            } else if (RootSelect < (ULONG)(Root->Count)) {

                //
                // Middle entry, squeeze root
                //
                RtlMoveMemory(
                    (PVOID)&(Root->List[RootSelect]),
                    (PVOID)&(Root->List[RootSelect+1]),
                    (Root->Count - RootSelect) * sizeof(HCELL_INDEX)
                    );
            }
            //
            // Else RootSelect == last entry, so decrementing count
            // was all we needed to do
            //

        } else {

            pcell->SubKeyLists[Type] = HCELL_NIL;

        }

    } else if (LeafSelect < (ULONG)(Leaf->Count)) {

        if (Leaf->Signature == CM_KEY_INDEX_LEAF) {
            RtlMoveMemory((PVOID)&(Leaf->List[LeafSelect]),
                          (PVOID)&(Leaf->List[LeafSelect+1]),
                          (Leaf->Count - LeafSelect) * sizeof(HCELL_INDEX));
        } else {
            FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
            RtlMoveMemory((PVOID)&(FastIndex->List[LeafSelect]),
                          (PVOID)&(FastIndex->List[LeafSelect+1]),
                          (FastIndex->Count - LeafSelect) * sizeof(CM_INDEX));
        }
    }
    //
    // Else LeafSelect == last entry, so decrementing count was enough
    //

    // things went OK
    Result = TRUE;

Exit:
    if( CellToRelease1 != HCELL_NIL ) {
        HvReleaseCell(Hive,CellToRelease1);
    }
    if( CellToRelease2 != HCELL_NIL ) {
        HvReleaseCell(Hive,CellToRelease2);
    }

    if ((IsCompressed) &&
        (SearchName.MaximumLength > sizeof(CompressedBuffer))) {
        ExFreePool(SearchName.Buffer);
    }
    return Result;
}

HCELL_INDEX
CmpDuplicateIndex(
    PHHIVE          Hive,
    HCELL_INDEX     IndexCell,
    ULONG           StorageType
    )
/*++

Routine Description:

    Duplicate an index, regardless of its type; Needed for NtRenameKey

Arguments:

    Hive - pointer to hive control structure for hive of interest

    IndexCell - the index to be duplicated

    StorageType - storagetype (Stable or Volatile)

Return Value:

    cellindex of a duplicate or HCELL_NIL

--*/
{

    PCM_KEY_INDEX   Index;
#if DBG
    PCM_KEY_INDEX   Leaf;
#endif
    ULONG           i;
    PCM_KEY_INDEX   NewIndex = NULL;
    HCELL_INDEX     NewIndexCell;
    HCELL_INDEX     LeafCell;
    HV_TRACK_CELL_REF   CellRef = {0};

    CM_PAGED_CODE();

    //
    // we have the lock exclusive or nobody is operating inside this hive
    //
    ASSERT_CM_EXCLUSIVE_HIVE_ACCESS(Hive);

    ASSERT( HvGetCellType(IndexCell) == StorageType );

    Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
    if( Index == NULL ) {
        //
        // we couldn't map a view for the bin containing this cell
        //
        return HCELL_NIL;
    }

    if( !HvTrackCellRef(&CellRef,Hive,IndexCell) ) {
        return HCELL_NIL;
    }

    if (Index->Signature == CM_KEY_INDEX_ROOT) {
        //
        // first duplicate IndexCell, zeroing out the new content
        //
        NewIndexCell = HvDuplicateCell(Hive,IndexCell,StorageType,FALSE);
        if( NewIndexCell == HCELL_NIL ) {
            HvReleaseFreeCellRefArray(&CellRef);
            return HCELL_NIL;
        }

        NewIndex = (PCM_KEY_INDEX)HvGetCell(Hive, NewIndexCell);
        if( NewIndex == NULL ) {
            //
            // we couldn't map a view for the bin containing this cell
            // this shouldn't happen as we just allocated this cell (i.e. is dirty/pinned into memory)
            //
            ASSERT( FALSE );
            goto ErrorExit;
        }
        if( !HvTrackCellRef(&CellRef,Hive,NewIndexCell) ) {
            goto ErrorExit;
        }

        //
        // we have a root index;
        //
        NewIndex->Signature = CM_KEY_INDEX_ROOT;
        NewIndex->Count = 0;

        //
        // copy first level.
        //
        for( i=0;i<Index->Count;i++) {
#if DBG
            Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, Index->List[i]);
            if( Leaf == NULL ) {
                //
                // we couldn't map the bin containing this cell
                //
                goto ErrorExit;
            }

            ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF)   ||
                   (Leaf->Signature == CM_KEY_FAST_LEAF)    ||
                   (Leaf->Signature == CM_KEY_HASH_LEAF)
                   );
            ASSERT(Leaf->Count != 0);
            HvReleaseCell(Hive, Index->List[i]);
#endif
            
            LeafCell = HvDuplicateCell(Hive,Index->List[i],StorageType,TRUE);
            if( LeafCell == HCELL_NIL ) {
                goto ErrorExit;
            }
            
            NewIndex->List[i] = LeafCell;
            NewIndex->Count++;
        }
        
        ASSERT( NewIndex->Count == Index->Count );

    } else {
        //
        // leaf index
        //
        ASSERT((Index->Signature == CM_KEY_INDEX_LEAF)  ||
               (Index->Signature == CM_KEY_FAST_LEAF)   ||
               (Index->Signature == CM_KEY_HASH_LEAF)
               );
        ASSERT(Index->Count != 0);

        //
        // first duplicate IndexCell, copying the old content
        //
        NewIndexCell = HvDuplicateCell(Hive,IndexCell,StorageType,TRUE);
    }

    HvReleaseFreeCellRefArray(&CellRef);
    return NewIndexCell;

ErrorExit:
    HvReleaseFreeCellRefArray(&CellRef);
    if( NewIndex != NULL ){
        // we can get here only if we are trying to duplicate an index_root
        ASSERT( NewIndex->Signature == CM_KEY_INDEX_ROOT );
       
        //
        // free the space we already allocated
        //
        for(i=0;i<NewIndex->Count;i++) {
            ASSERT(NewIndex->List[i] != 0 );
            HvFreeCell(Hive, NewIndex->List[i]);
        }
    }

    HvFreeCell(Hive, NewIndexCell);
    return HCELL_NIL;
}

BOOLEAN
CmpUpdateParentForEachSon(
    PHHIVE          Hive,
    HCELL_INDEX     Parent
    )
/*++

Routine Description:

    Walks the child's list (both stable and volatile and marks updates
    the parent link to Parent.

    First step is to mark all children dirty, and then to update the link.
    This way, if we fail part through, we leave everything in good order

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Parent - cell index of the cell who's son's to be updated.

Return Value:

    TRUE - successfully updated

--*/
{
    PCM_KEY_NODE    ParentNode;
    PCM_KEY_NODE    CurrentSon;
    HCELL_INDEX     Child;
    ULONG           Count;   
    ULONG           i;   

    CM_PAGED_CODE();

    //
    // we have the lock exclusive or nobody is operating inside this hive
    //
    ASSERT_CM_EXCLUSIVE_HIVE_ACCESS(Hive);

    //
    // grab the parent node; this was already marked as dirty, we shouldn't 
    // have any problem here;
    //
    ParentNode = (PCM_KEY_NODE)HvGetCell(Hive,Parent);
    if( ParentNode == NULL ) {
        //
        // cannot map view; this shouldn't happen as we just allocated 
        // this cell (i.e. it should be dirty/pinned into memory)
        //
        ASSERT( FALSE );
        return FALSE;
    }

    //
    // iterate through the child list (both stable and volatile), marking every
    // child dirty; this will pin the cell into memory and we will have no problems 
    // changing the parent later on
    //
    Count = ParentNode->SubKeyCounts[Stable] + ParentNode->SubKeyCounts[Volatile];
    for( i=0;i<Count;i++) {
        Child = CmpFindSubKeyByNumber(Hive,ParentNode,i);
        if( Child == HCELL_NIL ) {
            HvReleaseCell(Hive, Parent);
            return FALSE;
        }
        if(!HvMarkCellDirty(Hive,Child,FALSE)) {
            HvReleaseCell(Hive, Parent);
            return FALSE;
        }
    }

    //
    // second iteration, change the parent for each and every son
    //
    for( i=0;i<Count;i++) {
        Child = CmpFindSubKeyByNumber(Hive,ParentNode,i);

        //
        // sanity test: we marked this dirty few lines above!
        //
        ASSERT( Child != HCELL_NIL );

        CurrentSon = (PCM_KEY_NODE)HvGetCell(Hive,Child);

        //
        // sanity test: this cell should be pinned in memory by now
        //
        ASSERT( CurrentSon != NULL );

        //
        // change the parent
        //
        CurrentSon->Parent = Parent;
        // release the cell here; as the registry is locked exclusive (i.e. we don't care)
        HvReleaseCell(Hive, Child);
    }

    HvReleaseCell(Hive, Parent);
    return TRUE;
}

ULONG
CmpComputeHashKey(
    IN ULONG            HashStart,
    IN PUNICODE_STRING  Name
#if DBG
    ,IN BOOLEAN        AllowSeparators
#endif
    )
{
    ULONG                   Cnt;
    WCHAR                   *Cp;

    ASSERT((Name->Length == 0) || AllowSeparators || (Name->Buffer[0] != OBJ_NAME_PATH_SEPARATOR));
    //
    // Manually compute the hash to use.
    //

    Cp = Name->Buffer;
    for (Cnt=0; Cnt<Name->Length; Cnt += sizeof(WCHAR)) {
        ASSERT( AllowSeparators || (*Cp != OBJ_NAME_PATH_SEPARATOR) );
        HashStart = 37 * HashStart + (ULONG)CmUpcaseUnicodeChar(*Cp);
        ++Cp;
    }

    return HashStart;
}

ULONG
CmpComputeHashKeyForCompressedName(
                                    IN ULONG    HashStart,
                                    IN PWCHAR   Source,
                                    IN ULONG    SourceLength
                                    )
{
    ULONG   i;

    for (i=0;i<SourceLength;i++) {
        HashStart = 37*HashStart + (ULONG)CmUpcaseUnicodeChar((WCHAR)(((PUCHAR)Source)[i]));
    }

    return HashStart;
}

//
// HashIndex routines
//


HCELL_INDEX
CmpFindSubKeyByHash(
    PHHIVE                  Hive,
    PCM_KEY_FAST_INDEX      FastIndex,
    PUNICODE_STRING         SearchName
    )
/*++

Routine Description:

    Find the child cell (either subkey or value) specified by name.
    It searched in the index table ordered by the hash

Arguments:

    Hive - pointer to hive control structure for hive of interest

    Index - 

    SearchName - name of child of interest

Return Value:

    Cell of matching child key, or HCELL_NIL if none.

--*/
{
    USHORT      Current;
    ULONG       HashKey;
    LONG        Result;

    CM_PAGED_CODE();

    ASSERT( FastIndex->Signature == CM_KEY_HASH_LEAF );

    HashKey = CmpComputeHashKey(0,SearchName
#if DBG
                                , FALSE
#endif
    );

    for(Current = 0; Current < FastIndex->Count; Current++ ) {
        if( HashKey == FastIndex->List[Current].HashKey ) {
            //
            // HashKey matches; see if this is a real hit
            //

            Result = CmpDoCompareKeyName(Hive,SearchName,FastIndex->List[Current].Cell);
            if (Result == 0) {
                return FastIndex->List[Current].Cell;
            }

        }
    }

    return HCELL_NIL;
}

