Windows2000/private/ntos/config/cmtrecpy.c
2020-09-30 17:12:32 +02:00

1925 lines
49 KiB
C

/*++
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;
CMLOG(CML_MAJOR, CMS_SAVRES) {
KdPrint(("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;
result = CmpCopySyncTree2(
CmpCopyStack,
CMP_INITIAL_STACK_SIZE,
0,
SourceHive,
TargetHive,
CopyVolatile,
CopyType
);
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;
ULONG SyncTreeCopyStackStart;
WCHAR *NameBuffer = NULL;
// A merge is a particular case of a sync !!!
BOOLEAN TreeSync = (CopyType == Sync || CopyType == Merge)?TRUE:FALSE;
CMLOG(CML_MINOR, CMS_SAVRES) {
KdPrint(("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, 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);
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);
// 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
KdPrint(("CONFIG: SubKey Deletion from Source Cell #%lu.\n",
Frame->SourceCell));
#endif
// Delete what should be deleted from TargetCell
CmpSyncSubKeysAfterDelete(CmpSourceHive,
SourceCell,
CmpTargetHive,
TargetCell,
NameBuffer);
}
}
break;
}
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);
CmpInitializeKeyNameString(SourceChildCell,
&KeyName,
NameBuffer);
// Try to find the current child's counterpart in
// in the target tree using the child's name.
NewSubKey = CmpFindSubKeyByName(CmpTargetHive,
(PCM_KEY_NODE)HvGetCell(CmpTargetHive,
Frame->TargetCell),
&KeyName);
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);
// 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 synchornized with the current subkey.
#if DEBUG_TREE_SYNC
KdPrint(("CONFIG: Target Refresh.\n"));
KdPrint(("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)) {
goto CopyEnd;
}
} else {
if(!CmpSyncKeyValues(CmpSourceHive, SourceChild, SourceChildCell,
CmpTargetHive, NewSubKey, TargetChildCell)) {
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.
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
KdPrint(("CONFIG: New SubKey.\n"));
KdPrint(("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;
}
NewSubKey = CmpCopyKeyPartial(
CmpSourceHive,
SourceChild,
CmpTargetHive,
Frame->TargetCell,
TRUE
);
if (NewSubKey == HCELL_NIL) {
goto CopyEnd;
}
if ( ! CmpAddSubKey(
CmpTargetHive,
Frame->TargetCell,
NewSubKey
)
) {
goto CopyEnd;
}
// 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;
PCELL_DATA psrckey;
PCM_KEY_NODE ptarkey;
PCELL_DATA psrclist;
PCELL_DATA ptarlist;
PCELL_DATA psrcsecurity;
HCELL_INDEX security;
HCELL_INDEX class;
ULONG classlength;
ULONG count;
ULONG Type;
CMLOG(CML_MINOR, CMS_SAVRES) {
KdPrint(("CmpCopyKeyPartial:\n"));
KdPrint(("\tSHive=%08lx SCell=%08lx\n",SourceHive,SourceKeyCell));
KdPrint(("\tTHive=%08lx\n",TargetHive));
}
// 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);
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;
}
// 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);
ptarkey->Class = newclass;
ptarkey->Security = HCELL_NIL;
ptarkey->SubKeyLists[Stable] = HCELL_NIL;
ptarkey->SubKeyLists[Volatile] = HCELL_NIL;
ptarkey->SubKeyCounts[Stable] = 0;
ptarkey->SubKeyCounts[Volatile] = 0;
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
psrcsecurity = HvGetCell(SourceHive, security);
status = CmpAssignSecurityDescriptor(TargetHive,
newkey,
ptarkey,
&(psrcsecurity->u.KeySecurity.Descriptor));
if (!NT_SUCCESS(status)) {
goto DoFinally;
}
// Set up the value list
count = psrckey->u.KeyNode.ValueList.Count;
if ((count == 0) || (CopyValues == FALSE)) {
ptarkey->ValueList.List = HCELL_NIL;
ptarkey->ValueList.Count = 0;
success = TRUE;
} else {
psrclist = HvGetCell(SourceHive, psrckey->u.KeyNode.ValueList.List);
newlist = HvAllocateCell(
TargetHive,
count * sizeof(HCELL_INDEX),
Type
);
if (newlist == HCELL_NIL) {
goto DoFinally;
}
ptarkey->ValueList.List = newlist;
ptarlist = HvGetCell(TargetHive, newlist);
// Copy the values
for (i = 0; i < count; i++) {
newvalue = CmpCopyValue(
SourceHive,
psrclist->u.KeyList[i],
TargetHive,
Type
);
if (newvalue != HCELL_NIL) {
ptarlist->u.KeyList[i] = newvalue;
} else {
for (; i > 0; i--) {
HvFreeCell(
TargetHive,
ptarlist->u.KeyList[i - 1]
);
}
goto DoFinally;
}
}
success = TRUE;
}
DoFinally:
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;
CMLOG(CML_MINOR, CMS_SAVRES) {
KdPrint(("CmpCopyValue:\n"));
KdPrint(("\tSHive=%08lx SCell=%08lx\n",SourceHive,SourceValueCell));
KdPrint(("\tTargetHive=%08lx\n",TargetHive));
}
// get source data
pvalue = HvGetCell(SourceHive, SourceValueCell);
small = CmpIsHKeyValueSmall(datalength, pvalue->u.KeyValue.DataLength);
olddata = pvalue->u.KeyValue.Data;
// Copy body
newvalue = CmpCopyCell(SourceHive, SourceValueCell, TargetHive, Type);
if (newvalue == HCELL_NIL) {
return HCELL_NIL;
}
// Copy data (if any)
if (datalength > 0) {
if (datalength > CM_KEY_VALUE_SMALL) {
// there's data, and it's "big", so do standard copy
newdata = CmpCopyCell(SourceHive, olddata, TargetHive, Type);
if (newdata == HCELL_NIL) {
HvFreeCell(TargetHive, newvalue);
return HCELL_NIL;
}
pvalue = HvGetCell(TargetHive, newvalue);
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
pvalue = HvGetCell(SourceHive, pvalue->u.KeyValue.Data);
tempdata = *((PULONG)pvalue);
}
pvalue = HvGetCell(TargetHive, newvalue);
pvalue->u.KeyValue.Data = tempdata;
pvalue->u.KeyValue.DataLength =
datalength + CM_KEY_VALUE_SPECIAL_SIZE;
}
}
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;
CMLOG(CML_MINOR, CMS_SAVRES) {
KdPrint(("CmpCopyCell:\n"));
KdPrint(("\tSourceHive=%08lx SourceCell=%08lx\n",SourceHive,SourceCell));
KdPrint(("\tTargetHive=%08lx\n",TargetHive));
}
psource = HvGetCell(SourceHive, SourceCell);
size = HvGetCellSize(SourceHive, psource);
newcell = HvAllocateCell(TargetHive, size, Type);
if (newcell == HCELL_NIL) {
return HCELL_NIL;
}
ptarget = HvGetCell(TargetHive, newcell);
RtlCopyMemory(ptarget, psource, size);
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;
// 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);
// Free each value
for (i = 0; i < Node->ValueList.Count; i++) {
CmpFreeValue(Hive, plist->u.KeyList[i]);
}
// 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
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.
--*/
{
NTSTATUS status;
BOOLEAN success = FALSE;
PCELL_DATA psrclist, ptarlist;
HCELL_INDEX newvalue, newlist = HCELL_NIL;
ULONG i, count, Type;
PCM_KEY_VALUE poldvalue;
WCHAR *NameBuffer = NULL;
UNICODE_STRING ValueName;
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, 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);
newlist = HvReallocateCell(
TargetHive,
TargetKeyNode->ValueList.List,
(TargetKeyNode->ValueList.Count + count) * sizeof(HCELL_INDEX)
);
// Growing up may fail
if (newlist == HCELL_NIL) {
goto EndValueMerge;
}
TargetKeyNode->ValueList.List = newlist;
ptarlist = HvGetCell(TargetHive, newlist);
// Copy the values
for (i = 0; i < count; i++) {
poldvalue = (PCM_KEY_VALUE)HvGetCell(SourceHive, psrclist->u.KeyList[i]);
// get the name
CmpInitializeValueNameString(poldvalue,&ValueName,NameBuffer);
// check if this particular values doesn't exist in the target node already
if(HCELL_NIL == CmpFindNameInList(TargetHive,&(TargetKeyNode->ValueList),&ValueName,NULL,NULL)) {
// No, it doesn't, so add it
newvalue = CmpCopyValue(
SourceHive,
psrclist->u.KeyList[i],
TargetHive,
Type
);
if (newvalue != HCELL_NIL) {
ptarlist->u.KeyList[TargetKeyNode->ValueList.Count++] = newvalue;
} else {
// Delete all the copied values on an error.
for (; i > 0; i--) {
HvFreeCell(
TargetHive,
ptarlist->u.KeyList[--TargetKeyNode->ValueList.Count]
);
}
goto EndValueMerge;
}
}
}
// adjust the Value list to the new count.
// This call shouldn't fail (the new size is smaller or in the worst case equal to the old one)
newlist = HvReallocateCell(
TargetHive,
TargetKeyNode->ValueList.List,
TargetKeyNode->ValueList.Count * sizeof(HCELL_INDEX)
);
ASSERT(newlist != HCELL_NIL);
TargetKeyNode->ValueList.List = newlist;
success = TRUE;
}
EndValueMerge:
if (NameBuffer) ExFreePool(NameBuffer);
if (success == FALSE) {
// Clean-up on failure
// Revert to the original size
if (newlist != HCELL_NIL) {
newlist = HvReallocateCell(
TargetHive,
TargetKeyNode->ValueList.List,
TargetKeyNode->ValueList.Count * sizeof(HCELL_INDEX)
);
ASSERT(newlist != HCELL_NIL);
TargetKeyNode->ValueList.List = newlist;
}
}
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, ptarlist, psrcsecurity;
HCELL_INDEX newvalue, newlist = HCELL_NIL, newclass = HCELL_NIL, newsecurity = HCELL_NIL;
ULONG i, count, Type;
// First, free the target key's values, sd, and class info.
if(!CmpFreeKeyValues(TargetHive, TargetKeyCell, TargetKeyNode))
return FALSE;
// 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;
TargetKeyNode->Security = HCELL_NIL;
// Allocate and assign security
psrcsecurity = HvGetCell(SourceHive, SourceKeyNode->Security);
status = CmpAssignSecurityDescriptor(TargetHive,
TargetKeyCell,
TargetKeyNode,
&(psrcsecurity->u.KeySecurity.Descriptor));
if (!NT_SUCCESS(status)) {
goto EndValueSync;
}
// Set up the value list
count = SourceKeyNode->ValueList.Count;
if (count == 0) {
// No values in source, no list needed.
TargetKeyNode->ValueList.List = HCELL_NIL;
TargetKeyNode->ValueList.Count = 0;
success = TRUE;
} else {
// Allocate the value list for target
psrclist = HvGetCell(SourceHive, SourceKeyNode->ValueList.List);
newlist = HvAllocateCell(
TargetHive,
count * sizeof(HCELL_INDEX),
Type
);
if (newlist == HCELL_NIL) {
goto EndValueSync;
}
TargetKeyNode->ValueList.List = newlist;
ptarlist = HvGetCell(TargetHive, newlist);
// Copy the values
for (i = 0; i < count; i++) {
newvalue = CmpCopyValue(
SourceHive,
psrclist->u.KeyList[i],
TargetHive,
Type
);
if (newvalue != HCELL_NIL) {
ptarlist->u.KeyList[i] = newvalue;
} else {
// Delete all the copied values on an error.
for (; i > 0; i--) {
HvFreeCell(
TargetHive,
ptarlist->u.KeyList[i - 1]
);
}
goto EndValueSync;
}
}
TargetKeyNode->ValueList.Count = count;
success = TRUE;
}
EndValueSync:
if (success == FALSE) {
// Clean-up on failure
if (newlist != HCELL_NIL) {
HvFreeCell(TargetHive, newlist);
}
if (newsecurity != HCELL_NIL) {
HvFreeCell(TargetHive, newsecurity);
}
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 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,
MAX_KEY_NAME_LENGTH,
Cell->Name,
Cell->NameLength);
// Use the decompression buffer as the string buffer
KeyName->Buffer = NameBuffer;
KeyName->MaximumLength = 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 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,
MAX_KEY_VALUE_NAME_LENGTH,
Cell->Name,
Cell->NameLength);
// Use the decompression buffer as the string buffer
ValueName->Buffer = NameBuffer;
ValueName->MaximumLength = 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 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);
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
KdPrint(("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
KdPrint(("CONFIG: Delete TREE performed.\n"));
#endif
}
// 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++;
}
}
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, realsize;
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 (! (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);
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 (!CmpIsHKeyValueSmall(realsize, pvalue->u.KeyValue.DataLength)) {
if (! HvMarkCellDirty(Hive, pvalue->u.KeyValue.Data)) {
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;
// Map in the target
ptarget = HvGetCell(Hive, Cell);
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;
}