/*++ Copyright (c) 1992 Microsoft Corporation Module Name: cmtrecpy.c Abstract: This file contains code for CmpCopyTree, misc copy utility routines. Author: Bryan M. Willman (bryanwi) 15-Jan-92 Revision History: Elliot Shmukler (t-ellios) 24-Aug-1998 Added support for synchronizing two trees. --*/ #include "cmp.h" // // Set this to true to enable tree sync debug outputs // #define DEBUG_TREE_SYNC FALSE // // stack used for directing nesting of tree copy. gets us off // the kernel stack and thus allows for VERY deep nesting // #define CMP_INITIAL_STACK_SIZE 1024 // ENTRIES typedef struct { HCELL_INDEX SourceCell; HCELL_INDEX TargetCell; ULONG i; } CMP_COPY_STACK_ENTRY, *PCMP_COPY_STACK_ENTRY; BOOLEAN CmpCopySyncTree2( PCMP_COPY_STACK_ENTRY CmpCopyStack, ULONG CmpCopyStackSize, ULONG CmpCopyStackTop, PHHIVE CmpSourceHive, PHHIVE CmpTargetHive, BOOLEAN CopyVolatile, CMP_COPY_TYPE CopyType ); BOOLEAN CmpFreeKeyValues( PHHIVE Hive, HCELL_INDEX Cell, PCM_KEY_NODE Node ); BOOLEAN CmpSyncKeyValues( PHHIVE SourceHive, HCELL_INDEX SourceKeyCell, PCM_KEY_NODE SourceKeyNode, PHHIVE TargetHive, HCELL_INDEX TargetKeyCell, PCM_KEY_NODE TargetKeyNode ); BOOLEAN CmpMergeKeyValues( PHHIVE SourceHive, HCELL_INDEX SourceKeyCell, PCM_KEY_NODE SourceKeyNode, PHHIVE TargetHive, HCELL_INDEX TargetKeyCell, PCM_KEY_NODE TargetKeyNode ); BOOLEAN CmpSyncSubKeysAfterDelete( PHHIVE SourceHive, PCM_KEY_NODE SourceCell, PHHIVE TargetHive, PCM_KEY_NODE TargetCell, WCHAR *NameBuffer); BOOLEAN CmpMarkKeyValuesDirty( PHHIVE Hive, HCELL_INDEX Cell, PCM_KEY_NODE Node ); BOOLEAN CmpMarkKeyParentDirty( PHHIVE Hive, HCELL_INDEX Cell ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,CmpCopySyncTree) #pragma alloc_text(PAGE,CmpCopySyncTree2) #pragma alloc_text(PAGE,CmpCopyKeyPartial) #pragma alloc_text(PAGE,CmpCopyValue) #pragma alloc_text(PAGE,CmpCopyCell) #pragma alloc_text(PAGE,CmpFreeKeyValues) #pragma alloc_text(PAGE,CmpSyncKeyValues) #pragma alloc_text(PAGE,CmpMergeKeyValues) #pragma alloc_text(PAGE,CmpInitializeKeyNameString) #pragma alloc_text(PAGE,CmpInitializeValueNameString) #pragma alloc_text(PAGE,CmpSyncSubKeysAfterDelete) #pragma alloc_text(PAGE,CmpMarkKeyValuesDirty) #pragma alloc_text(PAGE,CmpMarkKeyParentDirty) #endif // // Routine to actually call to do a tree copy (or sync) // BOOLEAN CmpCopySyncTree( PHHIVE SourceHive, HCELL_INDEX SourceCell, PHHIVE TargetHive, HCELL_INDEX TargetCell, BOOLEAN CopyVolatile, CMP_COPY_TYPE CopyType ) /*++ Routine Description: This routine can perform two distinct (yet similar) tasks: a tree copy or a tree synchronization (sync). Which task is performed is determined by the TreeSync parameter. For both operations: -------------------- The source root key and target root key must exist in advance. These root nodes and their value entries will NOT be copied/synced. NOTE: Volatile keys are only copied/synced if the CopyVolatile parameter is set to true. For a tree copy: ---------------- A tree is copied from source to destination. The subkeys of the source root key and the full trees under those subkeys will be copied to a new tree at target root key. NOTE: If this call fails part way through, it will NOT undo any successfully completed key copies, thus a partial tree copy CAN occur. For a tree sync: ---------------- The target tree is synchronized with the source tree. It is assumed that for a certain period of the time the target tree has remained unmodified while modifications may have been made to the source tree. During a sync, any such modifications to the source tree are made to the target tree. Thus, at the end of a successful sync, the target tree is identical to the source tree. Since only things that have changed in the source tree are modified in the target tree, a sync operation is far more efficient than the delete/copy operations necessary to accomplish the same results. NOTE: It is assumed that no open handles are held on any target tree keys. Registry in-memory data structures may be corrupted if this is not true. Arguments: SourceHive - pointer to hive control structure for source SourceCell - index of cell at root of tree to copy/sync TargetHive - pointer to hive control structure for target TargetCell - pointer to cell at root of target tree CopyVolatile - indicates whether volatile keys should be copied/synced. CopyType - indicates the type of the copy operation: Copy - A copy is requested Sync - A sync is requested Merge - A merge is requested i.e.: 1. the target nodes that are not present on the source tree are not deleted. 2. the target nodes that are present in the source tree gets overrided no matter what the LastWriteTime value is. Return Value: BOOLEAN - Result code from call, among the following: TRUE - it worked FALSE - the tree copy/sync was not completed (though more than 0 keys may have been copied/synced) --*/ { BOOLEAN result; PCMP_COPY_STACK_ENTRY CmpCopyStack; PRELEASE_CELL_ROUTINE TargetReleaseCellRoutine; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"CmpCopyTree:\n")); CmpCopyStack = ExAllocatePool( PagedPool, sizeof(CMP_COPY_STACK_ENTRY)*CMP_INITIAL_STACK_SIZE ); if (CmpCopyStack == NULL) { return FALSE; } CmpCopyStack[0].SourceCell = SourceCell; CmpCopyStack[0].TargetCell = TargetCell; //ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); // // since the registry is locked exclusively here, we don't need to lock/release cells // while copying the trees; So, we just set the release routines to NULL and restore after // the copy is complete; this saves some pain // TargetReleaseCellRoutine = TargetHive->ReleaseCellRoutine; TargetHive->ReleaseCellRoutine = NULL; result = CmpCopySyncTree2( CmpCopyStack, CMP_INITIAL_STACK_SIZE, 0, SourceHive, TargetHive, CopyVolatile, CopyType ); TargetHive->ReleaseCellRoutine = TargetReleaseCellRoutine; ExFreePool(CmpCopyStack); return result; } // // Helper // BOOLEAN CmpCopySyncTree2( PCMP_COPY_STACK_ENTRY CmpCopyStack, ULONG CmpCopyStackSize, ULONG CmpCopyStackTop, PHHIVE CmpSourceHive, PHHIVE CmpTargetHive, BOOLEAN CopyVolatile, CMP_COPY_TYPE CopyType ) /*++ Routine Description: This is a helper routine for CmpCopySyncTree. It accomplishes the functionality described by that routine in a "virtually" recursive manner which frees this routine from the limitations of the Kernel stack. This routine should not be called directly. Use CmpCopySyncTree!. Arguments: (All of these are "virtual globals") CmpCopyStack - "global" pointer to stack for frames CmpCopyStackSize - alloced size of stack CmpCopyStackTop - current top CmpSourceHive, CmpTargetHive - source and target hives CopyVolatile, CopyType - same as CmpCopySyncTree. Return Value: BOOLEAN - Result code from call, among the following: TRUE - it worked FALSE - the tree copy/sync was not completed (though more than 0 keys may have been copied/synced) --*/ { PCMP_COPY_STACK_ENTRY Frame; HCELL_INDEX SourceChild; HCELL_INDEX NewSubKey; BOOLEAN Ret = FALSE, SyncNeedsTreeCopy = FALSE; UNICODE_STRING KeyName; PCM_KEY_NODE SourceChildCell, TargetChildCell; PCM_KEY_NODE SourceCell, TargetCell, TempNode; ULONG SyncTreeCopyStackStart = CmpCopyStackTop - 1; WCHAR *NameBuffer = NULL; // A merge is a particular case of a sync !!! BOOLEAN TreeSync = (CopyType == Sync || CopyType == Merge)?TRUE:FALSE; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"CmpCopyTree2:\n")); if (TreeSync) { // // The sync operation involves some work with key names, // so we must allocate a buffer used for key name decompression. // NameBuffer = ExAllocatePool(PagedPool, REG_MAX_KEY_NAME_LENGTH); if(!NameBuffer) return FALSE; } // // outer loop, apply to entire tree, emulate recursion here // jump to here is a virtual call // Outer: while (TRUE) { Frame = &(CmpCopyStack[CmpCopyStackTop]); Frame->i = 0; // // inner loop, applies to one key // jump to here is a virtual return // Inner: while (TRUE) { SourceCell = (PCM_KEY_NODE)HvGetCell(CmpSourceHive, Frame->SourceCell); if( SourceCell == NULL ) { // // we couldn't map the bin containing this cell // goto CopyEnd; } SourceChild = CmpFindSubKeyByNumber(CmpSourceHive, SourceCell, Frame->i); (Frame->i)++; if ((SourceChild == HCELL_NIL) || (!CopyVolatile && (HvGetCellType(SourceChild) == Volatile))) { // // we've stepped through all the children (or we are only // interested in stable children and have just stepped through // the stable children and into the volatile ones) // if(TreeSync && (CopyType != Merge)) { // // If we are here during a sync, that means most of sync operations // applied to the current SourceCell have been completed. // That is, we have: // 1) Synchronized SourceCell's values with its counterpart in the // target tree. // 2) Synchronized any new SourceCell subkeys (subkeys present // in SourceCell but not its counterpart) by creating // and copying them to the proper place in the target tree. // // What this means is that SourceCell's counterpart in the target tree // (TargetCell) now has at least as many subkeys as SourceCell. // // This implies that if TargetCell now has more subkeys that SourceCell // than some subkeys of TargetCell are not present in the source tree // (probably because those keys were deleted from the source tree // during the period between the previous sync and now). // // If such keys exist, then they must be delete them from TargetCell // in order to complete the sync. We do this below. // TargetCell = (PCM_KEY_NODE)HvGetCell(CmpTargetHive, Frame->TargetCell); if( TargetCell == NULL ) { // // we couldn't map the bin containing this cell // HvReleaseCell(CmpSourceHive, Frame->SourceCell); goto CopyEnd; } // // Does TargetCell have more subkeys than SourceCell? // if((TargetCell->SubKeyCounts[Stable] + TargetCell->SubKeyCounts[Volatile]) > (SourceCell->SubKeyCounts[Stable] + // We only count the volatile keys if we are actually // syncing them. Note, however, that we always use // the volatile counts in TargetCell since we may // be syncing to a volatile tree where all keys are volatile. (CopyVolatile ? SourceCell->SubKeyCounts[Volatile] : 0))) { #if DEBUG_TREE_SYNC CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CONFIG: SubKey Deletion from Source Cell #%lu.\n", Frame->SourceCell)); #endif // // Delete what should be deleted from TargetCell // CmpSyncSubKeysAfterDelete(CmpSourceHive, SourceCell, CmpTargetHive, TargetCell, NameBuffer); } // // release target cell as we don't need it anymore // HvReleaseCell(CmpTargetHive, Frame->TargetCell); } // // release the source cell // HvReleaseCell(CmpSourceHive, Frame->SourceCell); break; } else { // // release the source cell // HvReleaseCell(CmpSourceHive, Frame->SourceCell); } if (TreeSync) { // // For a sync, we want to check if the current child (subkey) // of SourceCell is also a child of TargetCell - i.e. if // the subkey in question has a counterpart in the target tree. // // There is no guarantee that the counterpart's index number // will be the same so we must perform this check using // the subkey name. // // // Get the name of the current child // SourceChildCell = (PCM_KEY_NODE)HvGetCell(CmpSourceHive, SourceChild); if( SourceChildCell == NULL ) { // // we couldn't map the bin containing this cell // goto CopyEnd; } CmpInitializeKeyNameString(SourceChildCell, &KeyName, NameBuffer); // // Try to find the current child's counterpart in // in the target tree using the child's name. // TempNode = (PCM_KEY_NODE)HvGetCell(CmpTargetHive,Frame->TargetCell); if( TempNode == NULL ) { // // we couldn't map the bin containing this cell // HvReleaseCell(CmpSourceHive,SourceChild); goto CopyEnd; } NewSubKey = CmpFindSubKeyByName(CmpTargetHive, TempNode, &KeyName); // release the temporary node HvReleaseCell(CmpTargetHive,Frame->TargetCell); if (NewSubKey != HCELL_NIL) { // // Found it, the current child (subkey) has a counterpart // in the target tree. Thus, we just need to check if // the counterpart's values are out of date and should // be updated. // TargetChildCell = (PCM_KEY_NODE)HvGetCell(CmpTargetHive, NewSubKey); if( TargetChildCell == NULL ) { // // we couldn't map the bin containing this cell // HvReleaseCell(CmpSourceHive,SourceChild); goto CopyEnd; } // // Check if the current subkey has been modified // more recently than its target tree counterpart. // When we are doing a tree merge, always override the target. // if ( (CopyType == Merge) || ((TargetChildCell->LastWriteTime.QuadPart) < (SourceChildCell->LastWriteTime.QuadPart))) { // // The counterpart is out of date. Its values // must be synchronized with the current subkey. // #if DEBUG_TREE_SYNC CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CONFIG: Target Refresh.\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CONFIG: Source Cell %lu = %.*S\n", SourceChild, KeyName.Length / sizeof(WCHAR), KeyName.Buffer)); #endif // // Sync up the key's values, sd, & class // if(CopyType == Merge) { if(!CmpMergeKeyValues(CmpSourceHive, SourceChild, SourceChildCell, CmpTargetHive, NewSubKey, TargetChildCell)) { HvReleaseCell(CmpSourceHive,SourceChild); HvReleaseCell(CmpTargetHive,NewSubKey); goto CopyEnd; } } else { if(!CmpSyncKeyValues(CmpSourceHive, SourceChild, SourceChildCell, CmpTargetHive, NewSubKey, TargetChildCell)) { HvReleaseCell(CmpSourceHive,SourceChild); HvReleaseCell(CmpTargetHive,NewSubKey); goto CopyEnd; } } // // Sync the timestamps so that we don't do this again. // TargetChildCell->LastWriteTime.QuadPart = SourceChildCell->LastWriteTime.QuadPart; } // // If we are here, then the current subkey's target // tree counterpart has been synchronized (or did not need // to be). Transfer control to the code that will apply // this function "recursively" to the current subkey in order // to continue the sync. // HvReleaseCell(CmpSourceHive,SourceChild); HvReleaseCell(CmpTargetHive,NewSubKey); goto NewKeyCreated; } // // If we are here, it means that the current child (subkey) // does not have a counterpart in the target tree. This means // we have encountered a new subkey in the source tree and must // create it in the target tree. // // The standard copy code below will create this subkey. However, // we must also make sure that the tree under this subkey is properly // copied from source to target. The most efficient way of doing // this is to temporarily forget that we are in a sync operation // and merely perform a copy until the desired result is achieved. // #if DEBUG_TREE_SYNC CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CONFIG: New SubKey.\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CONFIG: Source Cell %lu = %.*S\n", SourceChild, KeyName.Length / sizeof(WCHAR), KeyName.Buffer)); #endif // // Indicate that we will just copy and not sync for a while // SyncNeedsTreeCopy = TRUE; // // release this cell as we don't need it anymore // HvReleaseCell(CmpSourceHive,SourceChild); } NewSubKey = CmpCopyKeyPartial( CmpSourceHive, SourceChild, CmpTargetHive, Frame->TargetCell, TRUE ); if (NewSubKey == HCELL_NIL) { goto CopyEnd; } if ( ! CmpAddSubKey( CmpTargetHive, Frame->TargetCell, NewSubKey ) ) { goto CopyEnd; } // // Sync up the MaxNameLen field in the parent // { PCM_KEY_NODE TargetNodeParent, TargetNodeSon; USHORT NameLength; TargetNodeParent = (PCM_KEY_NODE)HvGetCell(CmpTargetHive,Frame->TargetCell); if( TargetNodeParent == NULL ) { goto CopyEnd; } // // make sure it's dirty as we are going to alter it. // if (! HvMarkCellDirty(CmpTargetHive,Frame->TargetCell)) { HvReleaseCell(CmpTargetHive,Frame->TargetCell); goto CopyEnd; } TargetNodeSon = (PCM_KEY_NODE)HvGetCell(CmpTargetHive,NewSubKey); if( TargetNodeSon == NULL ) { HvReleaseCell(CmpTargetHive,Frame->TargetCell); goto CopyEnd; } NameLength = TargetNodeSon->NameLength; if( TargetNodeSon->Flags & KEY_COMP_NAME ) { NameLength *= sizeof(WCHAR); } if (TargetNodeParent->MaxNameLen < NameLength) { TargetNodeParent->MaxNameLen = NameLength; } HvReleaseCell(CmpTargetHive,NewSubKey); HvReleaseCell(CmpTargetHive,Frame->TargetCell); } // // Check if the sync operation determined that this // subtree should be copied // if(TreeSync && SyncNeedsTreeCopy) { // // We have just created a new key in the target tree // with the above code. However, since this is a sync, // the parent of that new key has not been created by our // code and thus may not have been modified at all before // the creation of the new key. But this parent now // has a new child, and must therefore be marked as dirty. // if (! CmpMarkKeyParentDirty(CmpTargetHive, NewSubKey)) { goto CopyEnd; } // // Record the stack level where we start the copy // (and temporarily abandon the sync) // so that we can return to the sync operation when this // stack level is reached again (i.e. when the tree // under the current subkey is fully copied) // SyncTreeCopyStackStart = CmpCopyStackTop; // // Pretend that this is not a sync in order // to simply start copying // TreeSync = FALSE; } NewKeyCreated: // // We succeeded in copying/syncing the subkey, apply // ourselves to it // CmpCopyStackTop++; if (CmpCopyStackTop >= CmpCopyStackSize) { // // if we're here, it means that the tree // we're trying to copy is more than 1024 // COMPONENTS deep (from 2048 to 256k bytes) // we could grow the stack, but this is pretty // severe, so return FALSE and fail the copy // goto CopyEnd; } CmpCopyStack[CmpCopyStackTop].SourceCell = SourceChild; CmpCopyStack[CmpCopyStackTop].TargetCell = NewSubKey; goto Outer; } // Inner: while if (CmpCopyStackTop == 0) { Ret = TRUE; goto CopyEnd; } CmpCopyStackTop--; Frame = &(CmpCopyStack[CmpCopyStackTop]); // // We have just completed working at a certain stack level. // This is a good time to check if we need to resume a temporarily // suspended sync operation. // if(SyncNeedsTreeCopy && (CmpCopyStackTop == SyncTreeCopyStackStart)) { // // We've been copying a tree for a sync. But now, that tree is fully // copied. So, let's resume the sync once again. // TreeSync = TRUE; SyncNeedsTreeCopy = FALSE; } goto Inner; } // Outer: while CopyEnd: if (NameBuffer) ExFreePool(NameBuffer); return Ret; } HCELL_INDEX CmpCopyKeyPartial( PHHIVE SourceHive, HCELL_INDEX SourceKeyCell, PHHIVE TargetHive, HCELL_INDEX Parent, BOOLEAN CopyValues ) /*++ Routine Description: Copy a key body and all of its values, but NOT its subkeylist or subkey entries. SubKeyList.Count will be set to 0. Arguments: SourceHive - pointer to hive control structure for source SourceKeyCell - value entry being copied TargetHive - pointer to hive control structure for target Parent - parent value to set into newly created key body CopyValues - if FALSE value entries will not be copied, if TRUE, they will Return Value: HCELL_INDEX - Cell of body of new key entry, or HCELL_NIL if some error. --*/ { NTSTATUS status; HCELL_INDEX newkey = HCELL_NIL; HCELL_INDEX newclass = HCELL_NIL; HCELL_INDEX newsecurity = HCELL_NIL; HCELL_INDEX newlist = HCELL_NIL; HCELL_INDEX newvalue; BOOLEAN success = FALSE; ULONG i,Index; PCELL_DATA psrckey = NULL; PCM_KEY_NODE ptarkey = NULL; PCELL_DATA psrclist = NULL; PCELL_DATA ptarlist; HCELL_INDEX security; HCELL_INDEX class; ULONG classlength; ULONG count; ULONG Type; PCM_KEY_VALUE pvalue; #if DBG WCHAR *NameBuffer = NULL; UNICODE_STRING ValueName; HCELL_INDEX child; #endif PSECURITY_DESCRIPTOR SrcSecurityDescriptor; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"CmpCopyKeyPartial:\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"\tSHive=%p SCell=%08lx\n",SourceHive,SourceKeyCell)); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"\tTHive=%p\n",TargetHive)); #if DBG NameBuffer = ExAllocatePool(PagedPool, REG_MAX_KEY_VALUE_NAME_LENGTH); if(!NameBuffer) { return HCELL_NIL; } #endif //DBG // // get description of source // if (Parent == HCELL_NIL) { // // This is a root node we are creating, so don't make it volatile. // Type = Stable; } else { Type = HvGetCellType(Parent); } psrckey = HvGetCell(SourceHive, SourceKeyCell); if( psrckey == NULL ) { // // we couldn't map the bin containing this cell // goto DoFinally; } security = psrckey->u.KeyNode.Security; class = psrckey->u.KeyNode.Class; classlength = psrckey->u.KeyNode.ClassLength; // // Allocate and copy the body // newkey = CmpCopyCell(SourceHive, SourceKeyCell, TargetHive, Type); if (newkey == HCELL_NIL) { goto DoFinally; } // // this cell was just allocated so it should be pinned; it's OK to release it here // as the view is not going anywhere // ASSERT_CELL_DIRTY(TargetHive, newkey); // // Allocate and copy class // if (classlength > 0) { newclass = CmpCopyCell(SourceHive, class, TargetHive, Type); if (newclass == HCELL_NIL) { goto DoFinally; } } // // Fill in the target body // ptarkey = (PCM_KEY_NODE)HvGetCell(TargetHive, newkey); if( ptarkey == NULL ) { // // we couldn't map the bin containing this cell // goto DoFinally; } ptarkey->Class = newclass; ptarkey->Security = HCELL_NIL; ptarkey->SubKeyLists[Stable] = HCELL_NIL; ptarkey->SubKeyCounts[Stable] = 0; ptarkey->SubKeyCounts[Volatile] = 0; ptarkey->SubKeyLists[Volatile] = HCELL_NIL; ptarkey->Parent = Parent; ptarkey->Flags = (psrckey->u.KeyNode.Flags & KEY_COMP_NAME); if (Parent == HCELL_NIL) { ptarkey->Flags |= KEY_HIVE_ENTRY + KEY_NO_DELETE; } // // Allocate and copy security // // Use the hash Luke !!! // if( CmpFindSecurityCellCacheIndex ((PCMHIVE)SourceHive,security,&Index) == FALSE ) { goto DoFinally; } SrcSecurityDescriptor = &(((PCMHIVE)SourceHive)->SecurityCache[Index].CachedSecurity->Descriptor); status = CmpAssignSecurityDescriptor(TargetHive, newkey, ptarkey, SrcSecurityDescriptor); if (!NT_SUCCESS(status)) { goto DoFinally; } // // Set up the value list // count = psrckey->u.KeyNode.ValueList.Count; // // initialize an Empty ValueList // ptarkey->ValueList.List = HCELL_NIL; ptarkey->ValueList.Count = 0; if ((count == 0) || (CopyValues == FALSE)) { success = TRUE; } else { psrclist = HvGetCell(SourceHive, psrckey->u.KeyNode.ValueList.List); if( psrclist == NULL ) { // // we couldn't map the bin containing this cell // goto DoFinally; } // // Copy the values // for (i = 0; i < count; i++) { newvalue = CmpCopyValue( SourceHive, psrclist->u.KeyList[i], TargetHive, Type ); if (newvalue == HCELL_NIL) { // // for cleanup purposes // newlist = ptarkey->ValueList.List; goto DoFinally; } pvalue = (PCM_KEY_VALUE)HvGetCell(TargetHive, newvalue); if( pvalue == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just allocated the cell // (i.e. the bin containing it should be PINNED into memory by now ) // ASSERT( FALSE ); // // for cleanup purposes // newlist = ptarkey->ValueList.List; goto DoFinally; } // // this cell was just allocated so it should be pinned; it's OK to release it here // as the view is not going anywhere // ASSERT_CELL_DIRTY(TargetHive, newvalue); HvReleaseCell(TargetHive, newvalue); #if DBG // // get the name // CmpInitializeValueNameString(pvalue,&ValueName,NameBuffer); // // find out the index where we should insert this // this is a special treatment for the case when we copy form and old hive (not sorted) // into a new format one (sorted) // if( CmpFindNameInList(TargetHive,&(ptarkey->ValueList),&ValueName,&Index,&child) == FALSE ) { // // we couldn't map a view inside the above call // // // for cleanup purposes // newlist = ptarkey->ValueList.List; goto DoFinally; } // // the value is not present in the list; we're about to add it! // ASSERT( child == HCELL_NIL ); // // sanity validation : insert at the end // ASSERT( Index == i ); #endif //DBG if( !NT_SUCCESS( CmpAddValueToList(TargetHive,newvalue,i,Type,&(ptarkey->ValueList)) ) ) { // // for cleanup purposes // newlist = ptarkey->ValueList.List; if( newlist != HCELL_NIL ) { ASSERT( i > 0 ); // // free already copied values // ptarlist = HvGetCell(TargetHive, newlist); if( ptarlist == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't fail as we just allocated this cell // (i.e. the bin should be PINNED into memory at this point) // ASSERT( FALSE ); goto DoFinally; } // // this cell was just allocated so it should be pinned; it's OK to release it here // as the view is not going anywhere // ASSERT_CELL_DIRTY(TargetHive, newlist); HvReleaseCell(TargetHive, newlist); for (; i > 0; i--) { HvFreeCell( TargetHive, ptarlist->u.KeyList[i - 1] ); } } else { ASSERT( i == 0 ); } goto DoFinally; } } success = TRUE; } DoFinally: #if DBG ASSERT( NameBuffer != NULL ); ExFreePool(NameBuffer); #endif //DBG if( psrclist != NULL ) { ASSERT(psrckey!= NULL ); HvReleaseCell(SourceHive, psrckey->u.KeyNode.ValueList.List); } if( psrckey != NULL ) { HvReleaseCell(SourceHive, SourceKeyCell); } if( ptarkey != NULL ) { ASSERT( newkey != HCELL_NIL ); HvReleaseCell(TargetHive, newkey); } if (success == FALSE) { if (newlist != HCELL_NIL) { HvFreeCell(TargetHive, newlist); } if (newsecurity != HCELL_NIL) { HvFreeCell(TargetHive, newsecurity); } if (newclass != HCELL_NIL) { HvFreeCell(TargetHive, newclass); } if (newkey != HCELL_NIL) { HvFreeCell(TargetHive, newkey); } return HCELL_NIL; } else { return newkey; } } HCELL_INDEX CmpCopyValue( PHHIVE SourceHive, HCELL_INDEX SourceValueCell, PHHIVE TargetHive, HSTORAGE_TYPE Type ) /*++ Routine Description: Copy a value entry. Copies the body of a value entry and the data. Returns cell of new value entry. Arguments: SourceHive - pointer to hive control structure for source SourceValueCell - value entry being copied TargetHive - pointer to hive control structure for target Type - storage type to allocate for target (stable or volatile) Return Value: HCELL_INDEX - Cell of body of new value entry, or HCELL_NIL if some error. --*/ { HCELL_INDEX newvalue; HCELL_INDEX newdata; PCELL_DATA pvalue; ULONG datalength; HCELL_INDEX olddata; ULONG tempdata; BOOLEAN small; HCELL_INDEX CellToRelease = HCELL_NIL; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"CmpCopyValue:\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"\tSHive=%p SCell=%08lx\n",SourceHive,SourceValueCell)); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"\tTargetHive=%p\n",TargetHive)); ASSERT( TargetHive->ReleaseCellRoutine == NULL ); // // get source data // pvalue = HvGetCell(SourceHive, SourceValueCell); if( pvalue == NULL ) { // // we couldn't map the bin containing this cell // return HCELL_NIL; } small = CmpIsHKeyValueSmall(datalength, pvalue->u.KeyValue.DataLength); olddata = pvalue->u.KeyValue.Data; // // Copy body // newvalue = CmpCopyCell(SourceHive, SourceValueCell, TargetHive, Type); if (newvalue == HCELL_NIL) { HvReleaseCell(SourceHive, SourceValueCell); return HCELL_NIL; } // // Copy data (if any) // if (datalength > 0) { if (datalength > CM_KEY_VALUE_SMALL) { if( (CmpIsHKeyValueBig(SourceHive,datalength) == TRUE) || (CmpIsHKeyValueBig(TargetHive,datalength) == TRUE) ) { PCELL_DATA Buffer; BOOLEAN BufferAllocated; HCELL_INDEX CellToRelease2 = HCELL_NIL; // // get the data from source, regardless of the size // if( CmpGetValueData(SourceHive,&(pvalue->u.KeyValue),&datalength,&Buffer,&BufferAllocated,&CellToRelease2) == FALSE ) { // // insufficient resources; return NULL // ASSERT( BufferAllocated == FALSE ); ASSERT( Buffer == NULL ); HvFreeCell(TargetHive, newvalue); HvReleaseCell(SourceHive, SourceValueCell); return HCELL_NIL; } // // we ignore celltorelease because we have specifically set the releae routine to NULL // // // allocate a new value data in the target hive (regardless of the size) // and copy the data onto it. // if( !NT_SUCCESS(CmpSetValueDataNew(TargetHive,Buffer,datalength,Type,newvalue,&newdata)) ) { // // We have bombed out loading user data, clean up and exit. // if( BufferAllocated == TRUE ) { ExFreePool( Buffer ); } HvFreeCell(TargetHive, newvalue); HvReleaseCell(SourceHive, SourceValueCell); if( CellToRelease2 != HCELL_NIL ) { HvReleaseCell(SourceHive, CellToRelease2); } return HCELL_NIL; } // // free the source buffer // if( BufferAllocated == TRUE ) { ExFreePool( Buffer ); } if( CellToRelease2 != HCELL_NIL ) { HvReleaseCell(SourceHive, CellToRelease2); } } else { // // there's data, normal size, or none of the hives support // bigdata cells, so do standard copy // newdata = CmpCopyCell(SourceHive, olddata, TargetHive, Type); } if (newdata == HCELL_NIL) { HvFreeCell(TargetHive, newvalue); HvReleaseCell(SourceHive, SourceValueCell); return HCELL_NIL; } pvalue = HvGetCell(TargetHive, newvalue); if( pvalue == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just allocated the cell // (i.e. it should be PINNED into memory at this point) // ASSERT( FALSE ); HvFreeCell(TargetHive, newvalue); HvReleaseCell(SourceHive, SourceValueCell); CmpFreeValueData(TargetHive,newdata,datalength); return HCELL_NIL; } pvalue->u.KeyValue.Data = newdata; pvalue->u.KeyValue.DataLength = datalength; } else { // // the data is small, but may be stored in either large or // small format for historical reasons // if (small) { // // data is already small, so just do a body to body copy // tempdata = pvalue->u.KeyValue.Data; } else { // // data is stored externally in old cell, will be internal in new // CellToRelease = pvalue->u.KeyValue.Data; pvalue = HvGetCell(SourceHive, pvalue->u.KeyValue.Data); if( pvalue == NULL ) { // // we couldn't map the bin containing this cell // HvFreeCell(TargetHive, newvalue); HvReleaseCell(SourceHive, SourceValueCell); return HCELL_NIL; } tempdata = *((PULONG)pvalue); } pvalue = HvGetCell(TargetHive, newvalue); if( pvalue == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just allocated the cell // (i.e. it should be PINNED into memory at this point) // ASSERT( FALSE ); HvFreeCell(TargetHive, newvalue); HvReleaseCell(SourceHive, SourceValueCell); if( CellToRelease != HCELL_NIL ) { HvReleaseCell(SourceHive, CellToRelease); } return HCELL_NIL; } pvalue->u.KeyValue.Data = tempdata; pvalue->u.KeyValue.DataLength = datalength + CM_KEY_VALUE_SPECIAL_SIZE; if( CellToRelease != HCELL_NIL ) { HvReleaseCell(SourceHive, CellToRelease); } } } HvReleaseCell(SourceHive, SourceValueCell); return newvalue; } HCELL_INDEX CmpCopyCell( PHHIVE SourceHive, HCELL_INDEX SourceCell, PHHIVE TargetHive, HSTORAGE_TYPE Type ) /*++ Routine Description: Copy SourceHive.SourceCell to TargetHive.TargetCell. Arguments: SourceHive - pointer to hive control structure for source SourceCell - index of cell to copy from TargetHive - pointer to hive control structure for target Type - storage type (stable or volatile) of new cell Return Value: HCELL_INDEX of new cell, or HCELL_NIL if failure. --*/ { PVOID psource; PVOID ptarget; ULONG size; HCELL_INDEX newcell; CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"CmpCopyCell:\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"\tSourceHive=%p SourceCell=%08lx\n",SourceHive,SourceCell)); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SAVRES,"\tTargetHive=%p\n",TargetHive)); psource = HvGetCell(SourceHive, SourceCell); if( psource == NULL ) { // // we couldn't map the bin containing this cell // return HCELL_NIL; } size = HvGetCellSize(SourceHive, psource); newcell = HvAllocateCell(TargetHive, size, Type,HCELL_NIL); if (newcell == HCELL_NIL) { HvReleaseCell(SourceHive, SourceCell); return HCELL_NIL; } ptarget = HvGetCell(TargetHive, newcell); if( ptarget == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just allocated the cell // (i.e. it should be PINNED into memory at this point) // ASSERT( FALSE ); HvFreeCell(TargetHive, newcell); HvReleaseCell(SourceHive, SourceCell); return HCELL_NIL; } RtlCopyMemory(ptarget, psource, size); HvReleaseCell(SourceHive, SourceCell); HvReleaseCell(TargetHive, newcell); return newcell; } BOOLEAN CmpFreeKeyValues( PHHIVE Hive, HCELL_INDEX Cell, PCM_KEY_NODE Node ) /*++ Routine Description: Free the cells associated with the value entries, the security descriptor, and the class of a particular key. Arguments: Hive - The hive of the key in question Cell - The cell of the key in question Node - The key body of the key in question Return Value: TRUE if successful, FALSE otherwise. --*/ { PCELL_DATA plist; ULONG i; ASSERT( Hive->ReleaseCellRoutine == NULL ); // // Mark all the value-related cells dirty // if (! CmpMarkKeyValuesDirty(Hive, Cell, Node)) { return FALSE; } // // Link nodes don't have things that we need to free // if (!(Node->Flags & KEY_HIVE_EXIT)) { // // First, free the value entries // if (Node->ValueList.Count > 0) { // Get value list plist = HvGetCell(Hive, Node->ValueList.List); if( plist == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just marked the cell dirty // (i.e. it should be PINNED into memory at this point) // ASSERT( FALSE ); return FALSE; } // Free each value for (i = 0; i < Node->ValueList.Count; i++) { if( CmpFreeValue(Hive, plist->u.KeyList[i]) == FALSE ) { // // we couldn't map view inside call above // this shouldn't happen as we just marked the values dirty // (i.e. they should be PINNED into memory at this point) // ASSERT( FALSE ); return FALSE; } } // Free the value list HvFreeCell(Hive, Node->ValueList.List); } // // Make this key value-less // Node->ValueList.List = HCELL_NIL; Node->ValueList.Count = 0; // // Free the security descriptor // // we need to postpone that until we are sure we got the new one //CmpFreeSecurityDescriptor(Hive, Cell); // // Free the Class information // if (Node->ClassLength > 0) { HvFreeCell(Hive, Node->Class); Node->Class = HCELL_NIL; Node->ClassLength = 0; } } return TRUE; } BOOLEAN CmpMergeKeyValues( PHHIVE SourceHive, HCELL_INDEX SourceKeyCell, PCM_KEY_NODE SourceKeyNode, PHHIVE TargetHive, HCELL_INDEX TargetKeyCell, PCM_KEY_NODE TargetKeyNode ) /*++ Routine Description: Merges the values from the two key-nodes provided. Rules for the merge: 1. The target values are not touched! 2. Only values from the source that are not present in the target are taken into account by this routine. They are added to the target node value list "as they are". Arguments: SourceHive - Hive of the source key SourceKeyCell - The source key's cell SourceKeyNode - The source key's body TargetHive - Hive of the target key TargetKeyCell - The target key's cell TargetKeyNode - The target key's body Return Value: TRUE of successful, FALSE otherwise. --*/ { BOOLEAN success = FALSE; PCELL_DATA psrclist; HCELL_INDEX newvalue, newlist = HCELL_NIL,child; ULONG i, count, Type, ChildIndex; PCM_KEY_VALUE poldvalue; WCHAR *NameBuffer = NULL; UNICODE_STRING ValueName; ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); ASSERT( SourceHive->ReleaseCellRoutine == NULL ); ASSERT( TargetHive->ReleaseCellRoutine == NULL ); if(TargetKeyNode->MaxValueNameLen < SourceKeyNode->MaxValueNameLen) { TargetKeyNode->MaxValueNameLen = SourceKeyNode->MaxValueNameLen; } if(TargetKeyNode->MaxValueDataLen < SourceKeyNode->MaxValueDataLen) { TargetKeyNode->MaxValueDataLen = SourceKeyNode->MaxValueDataLen; } if(TargetKeyNode->ValueList.Count == 0) { // // No Values in Target, do a sync // return CmpSyncKeyValues(SourceHive, SourceKeyCell, SourceKeyNode, TargetHive, TargetKeyCell, TargetKeyNode); } // // Set up the value list // count = SourceKeyNode->ValueList.Count; if (count == 0) { // No values in source, no update to the list needed. success = TRUE; } else { NameBuffer = ExAllocatePool(PagedPool, REG_MAX_KEY_VALUE_NAME_LENGTH); if(!NameBuffer) return FALSE; // // The type of the new cells will be the same as that // of the target cell. // Type = HvGetCellType(TargetKeyCell); // // Reallocate the value list for target to fit the new size // Worst case: all values from the source node will be added // to the target node // psrclist = HvGetCell(SourceHive, SourceKeyNode->ValueList.List); if( psrclist == NULL ) { // // we couldn't map the bin containing this cell // newlist = HCELL_NIL; goto EndValueMerge; } // // Copy the values // for (i = 0; i < count; i++) { poldvalue = (PCM_KEY_VALUE)HvGetCell(SourceHive, psrclist->u.KeyList[i]); if( poldvalue == NULL ) { // // we couldn't map the bin containing this cell // // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; goto EndValueMerge; } // // get the name // CmpInitializeValueNameString(poldvalue,&ValueName,NameBuffer); // // check if this particular values doesn't exist in the target node already // if( CmpFindNameInList(TargetHive,&(TargetKeyNode->ValueList),&ValueName,&ChildIndex,&child) == FALSE ) { // // we couldn't map a view inside the above call // // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; goto EndValueMerge; } if( child == HCELL_NIL ) { // // sanity validation : insert at the end // ASSERT( ChildIndex == TargetKeyNode->ValueList.Count ); // // No, it doesn't, so add it // newvalue = CmpCopyValue( SourceHive, psrclist->u.KeyList[i], TargetHive, Type ); if (newvalue == HCELL_NIL) { // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; goto EndValueMerge; } if( !NT_SUCCESS( CmpAddValueToList(TargetHive,newvalue,ChildIndex,Type,&(TargetKeyNode->ValueList)) ) ) { // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; goto EndValueMerge; } } } success = TRUE; } EndValueMerge: if (NameBuffer) ExFreePool(NameBuffer); if (success == FALSE) { // Clean-up on failure // Revert to the original size // // unfortunatelly we cannot do that anymore as we have sorted the list // } return success; } BOOLEAN CmpSyncKeyValues( PHHIVE SourceHive, HCELL_INDEX SourceKeyCell, PCM_KEY_NODE SourceKeyNode, PHHIVE TargetHive, HCELL_INDEX TargetKeyCell, PCM_KEY_NODE TargetKeyNode ) /*++ Routine Description: Synchronizes the value entries, security descriptor, and class of a target key with that of a source key - ensuring that the keys are identical with respect to the synchronized information. Arguments: SourceHive - Hive of the source key SourceKeyCell - The source key's cell SourceKeyNode - The source key's body TargetHive - Hive of the target key TargetKeyCell - The target key's cell TargetKeyNode - The target key's body Return Value: TRUE of successful, FALSE otherwise. --*/ { NTSTATUS status; BOOLEAN success = FALSE; PCELL_DATA psrclist = NULL, ptarlist; HCELL_INDEX newvalue, newlist = HCELL_NIL, newclass = HCELL_NIL; ULONG i, count, Type, Index; PCM_KEY_VALUE pvalue; #if DBG WCHAR *NameBuffer = NULL; UNICODE_STRING ValueName; HCELL_INDEX child; #endif //DBG PSECURITY_DESCRIPTOR SrcSecurityDescriptor; HCELL_INDEX OldSecurity,NewSecurity; UNREFERENCED_PARAMETER (SourceKeyCell); // // nobody is operating on the target hive // ASSERT( TargetHive->ReleaseCellRoutine == NULL ); // // First, free the target key's values, sd, and class info. // if(!CmpFreeKeyValues(TargetHive, TargetKeyCell, TargetKeyNode)) return FALSE; #if DBG NameBuffer = ExAllocatePool(PagedPool, REG_MAX_KEY_VALUE_NAME_LENGTH); if(!NameBuffer) { return FALSE; } #endif //DBG // // Now, copy the values, class, & sd from the source cell // // // The type of the new cells will be the same as that // of the target cell. // Type = HvGetCellType(TargetKeyCell); // // Allocate and copy class // if ((SourceKeyNode->ClassLength > 0) && (SourceKeyNode->Class != HCELL_NIL)) { newclass = CmpCopyCell(SourceHive, SourceKeyNode->Class, TargetHive, Type); if (newclass == HCELL_NIL) { goto EndValueSync; } // only if class is valid. Otherwise remains 0 (set by CmpFreeKeyValues) TargetKeyNode->ClassLength = SourceKeyNode->ClassLength; } // // Associate the new class with the target key // and prepare and security descriptor assignment. // TargetKeyNode->Class = newclass; // // Allocate and assign security // // // Use the hash Luke !!! // if( CmpFindSecurityCellCacheIndex ((PCMHIVE)SourceHive,SourceKeyNode->Security,&Index) == FALSE ) { goto EndValueSync; } SrcSecurityDescriptor = &(((PCMHIVE)SourceHive)->SecurityCache[Index].CachedSecurity->Descriptor); // // store it for later in case of error recovery // OldSecurity = TargetKeyNode->Security; TargetKeyNode->Security = HCELL_NIL; status = CmpAssignSecurityDescriptor(TargetHive, TargetKeyCell, TargetKeyNode, SrcSecurityDescriptor); if (!NT_SUCCESS(status)) { TargetKeyNode->Security = OldSecurity; goto EndValueSync; } NewSecurity = TargetKeyNode->Security; TargetKeyNode->Security = OldSecurity; if ((TargetKeyNode->Flags & KEY_HIVE_ENTRY) && ( NewSecurity != OldSecurity) ) { // // we need to play it safe here so we don't blow away the security list for entire hive. // PCM_KEY_SECURITY NewSec; PCM_KEY_SECURITY OldSec; PCM_KEY_SECURITY LastSec; HCELL_INDEX LastSecCell; NewSec = (PCM_KEY_SECURITY)HvGetCell(TargetHive,NewSecurity); if( NewSec == NULL ) { // // could not map view // goto EndValueSync; } OldSec = (PCM_KEY_SECURITY)HvGetCell(TargetHive,OldSecurity); if( OldSec == NULL ) { // // could not map view // HvReleaseCell(TargetHive,NewSecurity); goto EndValueSync; } LastSecCell = OldSec->Blink; LastSec = (PCM_KEY_SECURITY)HvGetCell(TargetHive,LastSecCell); if( LastSec == NULL ) { // // could not map view // HvReleaseCell(TargetHive,OldSecurity); HvReleaseCell(TargetHive,NewSecurity); goto EndValueSync; } if( !HvMarkCellDirty(TargetHive,OldSecurity) || !HvMarkCellDirty(TargetHive,LastSecCell) ) { // // no log space // HvReleaseCell(TargetHive,LastSecCell); HvReleaseCell(TargetHive,OldSecurity); HvReleaseCell(TargetHive,NewSecurity); goto EndValueSync; } // // link old list to new security // NewSec->Flink = OldSecurity; NewSec->Blink = LastSecCell; OldSec->Blink = NewSecurity; LastSec->Flink = NewSecurity; HvReleaseCell(TargetHive,LastSecCell); HvReleaseCell(TargetHive,OldSecurity); HvReleaseCell(TargetHive,NewSecurity); } // // we need to play it safe here, to make sure we never end up having a key // with a NIL security cell // CmpFreeSecurityDescriptor(TargetHive, TargetKeyCell); TargetKeyNode->Security = NewSecurity; // // Set up the value list // count = SourceKeyNode->ValueList.Count; // // target ValueList is an emptylist; we shall add values to it. // TargetKeyNode->ValueList.List = HCELL_NIL; TargetKeyNode->ValueList.Count = 0; // // after sync we'll have the values from source // TargetKeyNode->MaxValueNameLen = SourceKeyNode->MaxValueNameLen; TargetKeyNode->MaxValueDataLen = SourceKeyNode->MaxValueDataLen; if (count == 0) { // No values in source, no list needed. success = TRUE; } else { // // Do not allocate space for ValueList; CmpAddValueToList will do it // psrclist = HvGetCell(SourceHive, SourceKeyNode->ValueList.List); if( psrclist == NULL ) { // // we couldn't map the bin containing this cell // goto EndValueSync; } // // Copy the values // for (i = 0; i < count; i++) { newvalue = CmpCopyValue( SourceHive, psrclist->u.KeyList[i], TargetHive, Type ); if (newvalue == HCELL_NIL) { // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; goto EndValueSync; } pvalue = (PCM_KEY_VALUE)HvGetCell(TargetHive, newvalue); if( pvalue == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just allocated the cell // (i.e. the bin containing it should be PINNED into memory by now ) // ASSERT( FALSE ); // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; goto EndValueSync; } #if DBG // // get the name // CmpInitializeValueNameString(pvalue,&ValueName,NameBuffer); // // find out the index where we should insert this // this is a special treatment for the case when we copy form and old hive (not sorted) // into a new format one (sorted) // if( CmpFindNameInList(TargetHive,&(TargetKeyNode->ValueList),&ValueName,&Index,&child) == FALSE ) { // // we couldn't map a view inside the above call // // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; goto EndValueSync; } // // the value is not present in the list; we're about to add it! // ASSERT( child == HCELL_NIL ); // // sanity validation : insert at the end // ASSERT( Index == i ); #endif //DBG if( !NT_SUCCESS( CmpAddValueToList(TargetHive,newvalue,i,Type,&(TargetKeyNode->ValueList)) ) ) { // // for cleanup purposes // newlist = TargetKeyNode->ValueList.List; if( newlist != HCELL_NIL ) { // // Delete all the copied values on an error. // ptarlist = HvGetCell(TargetHive, newlist); if( ptarlist == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't fail as we just allocated this cell // (i.e. the bin should be PINNED into memory at this point) // ASSERT( FALSE ); goto EndValueSync; } for (; i > 0; i--) { HvFreeCell( TargetHive, ptarlist->u.KeyList[i - 1] ); } } goto EndValueSync; } } success = TRUE; } EndValueSync: #if DBG ASSERT( NameBuffer != NULL ); ExFreePool(NameBuffer); #endif //DBG if( psrclist != NULL ) { HvReleaseCell(SourceHive, SourceKeyNode->ValueList.List); } if (success == FALSE) { // Clean-up on failure if (newlist != HCELL_NIL) { HvFreeCell(TargetHive, newlist); } if (newclass != HCELL_NIL) { HvFreeCell(TargetHive, newclass); } } return success; } VOID CmpInitializeKeyNameString(PCM_KEY_NODE Cell, PUNICODE_STRING KeyName, WCHAR *NameBuffer ) /*++ Routine Description: Initializes a UNICODE_STRING with the name of a given key. N.B. The initialized string's buffer is not meant to be modified. Arguments: Cell - The body of the key in question KeyName - The UNICODE_STRING to initialize NameBuffer - A buffer REG_MAX_KEY_NAME_LENGTH bytes in size that will possibly be used as the UNICODE_STRING's buffer. Return Value: NONE. --*/ { // is the name stored in compressed form? if(Cell->Flags & KEY_COMP_NAME) { // Name is compressed. // Get the uncompressed length. KeyName->Length = CmpCompressedNameSize(Cell->Name, Cell->NameLength); // Decompress the name into a buffer. CmpCopyCompressedName(NameBuffer, REG_MAX_KEY_NAME_LENGTH, Cell->Name, Cell->NameLength); // // Use the decompression buffer as the string buffer // KeyName->Buffer = NameBuffer; KeyName->MaximumLength = REG_MAX_KEY_NAME_LENGTH; } else { // // Name is not compressed. Just use the name string // from the key buffer as the string buffer. // KeyName->Length = Cell->NameLength; KeyName->Buffer = Cell->Name; KeyName->MaximumLength = (USHORT)Cell->MaxNameLen; } } VOID CmpInitializeValueNameString(PCM_KEY_VALUE Cell, PUNICODE_STRING ValueName, WCHAR *NameBuffer ) /* Routine Description: Initializes a UNICODE_STRING with the name of a given value key. N.B. The initialized string's buffer is not meant to be modified. Arguments: Cell - The value key in question ValueName - The UNICODE_STRING to initialize NameBuffer - A buffer REG_MAX_KEY_NAME_LENGTH bytes in size that will possibly be used as the UNICODE_STRING's buffer. Return Value: NONE. */ { // is the name stored in compressed form? if(Cell->Flags & VALUE_COMP_NAME) { // Name is compressed. // Get the uncompressed length. ValueName->Length = CmpCompressedNameSize(Cell->Name, Cell->NameLength); // Decompress the name into a buffer. CmpCopyCompressedName(NameBuffer, REG_MAX_KEY_VALUE_NAME_LENGTH, Cell->Name, Cell->NameLength); // // Use the decompression buffer as the string buffer // ValueName->Buffer = NameBuffer; ValueName->MaximumLength = REG_MAX_KEY_VALUE_NAME_LENGTH; } else { // // Name is not compressed. Just use the name string // from the ValueName buffer as the string buffer. // ValueName->Length = Cell->NameLength; ValueName->Buffer = Cell->Name; ValueName->MaximumLength = ValueName->Length; } } BOOLEAN CmpSyncSubKeysAfterDelete(PHHIVE SourceHive, PCM_KEY_NODE SourceCell, PHHIVE TargetHive, PCM_KEY_NODE TargetCell, WCHAR *NameBuffer) /*++ Routine Description: This routine makes sure that any subkeys present in the target key but not present in the source key are deleted from the target key along with any trees under those subkeys. This routine is useful for synchronizing key deletion changes in a source cell with a target cell. It is used in this way from CmpCopySyncTree. NOTE: It is assumed that no open handles are held for the keys being deleted. If this is not so, registry in-memory data structures may become corrupted. Arguments: SourceHive - The hive of the source key SourceCell - The body of the source key TargetHive - The hive of the target key TargetCell - The body of the target key NameBuffer - A buffer REG_MAX_KEY_NAME_LENGTH bytes in size Return Value: TRUE if successful, FALSE otherwise. --*/ { HCELL_INDEX TargetSubKey, SourceSubKey; ULONG i = 0; PCM_KEY_NODE SubKeyCell; UNICODE_STRING SubKeyName; // // Run through all of the target cell's subkeys // while((TargetSubKey = CmpFindSubKeyByNumber( TargetHive, TargetCell, i)) != HCELL_NIL) { // // Check if the current subkey has a counterpart // subkey of the source cell. // (Note that we use similar techniques as in the code // of CmpCopySyncTree2) // SubKeyCell = (PCM_KEY_NODE)HvGetCell(TargetHive, TargetSubKey); if( SubKeyCell == NULL ) { // // we couldn't map the bin containing this cell // return FALSE; } CmpInitializeKeyNameString(SubKeyCell, &SubKeyName, NameBuffer); SourceSubKey = CmpFindSubKeyByName(SourceHive, SourceCell, &SubKeyName); if(SourceSubKey == HCELL_NIL) { // // The current subkey has no counterpart, // it must therefore be deleted from the target cell. // #if DEBUG_TREE_SYNC CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CONFIG: SubKey Deletion of %.*S\n", SubKeyName.Length / sizeof(WCHAR), SubKeyName.Buffer)); #endif if(SubKeyCell->SubKeyCounts[Stable] + SubKeyCell->SubKeyCounts[Volatile]) { // The subkey we are deleting has subkeys - use delete tree to get rid of them CmpDeleteTree(TargetHive, TargetSubKey); #if DEBUG_TREE_SYNC CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CONFIG: Delete TREE performed.\n")); #endif } // // release this cell as we don't need it anymore // HvReleaseCell(TargetHive, TargetSubKey); // The subkey we are deleting is now a leaf (or has always been one), // just delete it. if(!NT_SUCCESS(CmpFreeKeyByCell(TargetHive, TargetSubKey, TRUE))) { return FALSE; } // // We have deleted a subkey, so *i* does not need to get incremented // here because it now refers to the next subkey. // } else { // // Counterpart found. No deletion necessary. Move on to the next subkey // i++; // // release this cell as we don't need it anymore // HvReleaseCell(TargetHive, TargetSubKey); } } return TRUE; } BOOLEAN CmpMarkKeyValuesDirty( PHHIVE Hive, HCELL_INDEX Cell, PCM_KEY_NODE Node ) /*++ Routine Description: Marks the cells associated with a key's value entries, security descriptor, and class information as dirty. Arguments: Hive - The hive of the key in question Cell - The cell of the key in question Node - The body of the key in question Return Value: TRUE if successful, FALSE otherwise. A failure probably indicates that no log space was available. --*/ { PCELL_DATA plist, security, pvalue; ULONG i; ASSERT( Hive->ReleaseCellRoutine == NULL ); if (Node->Flags & KEY_HIVE_EXIT) { // // If this is a link node, we are done. Link nodes never have // classes, values, subkeys, or security descriptors. Since // they always reside in the master hive, they're always volatile. // return(TRUE); } // // mark cell itself // if (! HvMarkCellDirty(Hive, Cell)) { return FALSE; } // // Mark the class // if (Node->Class != HCELL_NIL) { if (! HvMarkCellDirty(Hive, Node->Class)) { return FALSE; } } // // Mark security // if (Node->Security != HCELL_NIL) { if (! HvMarkCellDirty(Hive, Node->Security)) { return FALSE; } security = HvGetCell(Hive, Node->Security); if( security == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just marked the cell dirty // (dirty == PINNED in memory). // ASSERT( FALSE ); return FALSE; } if (! (HvMarkCellDirty(Hive, security->u.KeySecurity.Flink) && HvMarkCellDirty(Hive, security->u.KeySecurity.Blink))) { return FALSE; } } // // Mark the value entries and their data // if (Node->ValueList.Count > 0) { // Value list if (! HvMarkCellDirty(Hive, Node->ValueList.List)) { return FALSE; } plist = HvGetCell(Hive, Node->ValueList.List); if( plist == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just marked the cell dirty // (dirty == PINNED in memory). // ASSERT( FALSE ); return FALSE; } for (i = 0; i < Node->ValueList.Count; i++) { if (! HvMarkCellDirty(Hive, plist->u.KeyList[i])) { return FALSE; } pvalue = HvGetCell(Hive, plist->u.KeyList[i]); if( pvalue == NULL ) { // // we couldn't map the bin containing this cell // this shouldn't happen as we just marked the cell dirty // (dirty == PINNED in memory). // ASSERT( FALSE ); return FALSE; } if( !CmpMarkValueDataDirty(Hive,&(pvalue->u.KeyValue)) ) { return FALSE; } } } return TRUE; } BOOLEAN CmpMarkKeyParentDirty( PHHIVE Hive, HCELL_INDEX Cell ) /*++ Routine Description: Marks the parent of a given key and the parent's subkey list as dirty. Arguments: Hive - The hive of the key in question. Cell - The cell of the key in question. Return Value: TRUE if successful, FALSE otherwise. A failure probably indicates that no log space was available. --*/ { PCELL_DATA ptarget; ASSERT_CM_EXCLUSIVE_HIVE_ACCESS(Hive); // // Map in the target // ptarget = HvGetCell(Hive, Cell); if( ptarget == NULL ) { // // we couldn't map the bin containing this cell // return FALSE; } if (ptarget->u.KeyNode.Flags & KEY_HIVE_ENTRY) { // // if this is an entry node, we are done. our parent will // be in the master hive (and thus volatile) // return TRUE; } // // Mark the parent's Subkey list // if (! CmpMarkIndexDirty(Hive, ptarget->u.KeyNode.Parent, Cell)) { return FALSE; } // // Mark the parent // if (! HvMarkCellDirty(Hive, ptarget->u.KeyNode.Parent)) { return FALSE; } return TRUE; }