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