NT4/private/ntos/config/cmsavres.c
2020-09-30 17:12:29 +02:00

1256 lines
31 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
cmsavres.c
Abstract:
This file contains code for SaveKey and RestoreKey.
Author:
Bryan M. Willman (bryanwi) 15-Jan-92
Revision History:
--*/
#include "cmp.h"
//
// defines how big the buffer we use for doing a savekey by copying the
// hive file should be.
//
#define CM_SAVEKEYBUFSIZE 0x10000
extern PCMHIVE CmpMasterHive;
extern BOOLEAN CmpProfileLoaded;
extern PUCHAR CmpStashBuffer;
PCMHIVE
CmpCreateTemporaryHive(
IN HANDLE FileHandle
);
VOID
CmpDestroyTemporaryHive(
PCMHIVE CmHive
);
NTSTATUS
CmpLoadHiveVolatile(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
IN HANDLE FileHandle
);
NTSTATUS
CmpRefreshHive(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock
);
NTSTATUS
CmpSaveKeyByFileCopy(
PCMHIVE Hive,
HANDLE FileHandle
);
ULONG
CmpRefreshWorkerRoutine(
PCM_KEY_CONTROL_BLOCK Current,
PVOID Context1,
PVOID Context2
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CmRestoreKey)
#pragma alloc_text(PAGE,CmpLoadHiveVolatile)
#pragma alloc_text(PAGE,CmpRefreshHive)
#pragma alloc_text(PAGE,CmSaveKey)
#pragma alloc_text(PAGE,CmpCreateTemporaryHive)
#pragma alloc_text(PAGE,CmpDestroyTemporaryHive)
#pragma alloc_text(PAGE,CmpRefreshWorkerRoutine)
#pragma alloc_text(PAGE,CmpSaveKeyByFileCopy)
#endif
NTSTATUS
CmRestoreKey(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
IN HANDLE FileHandle,
IN ULONG Flags
)
/*++
Routine Description:
This copies the data from an on-disk hive into the registry. The file
is not loaded into the registry, and the system will NOT be using
the source file after the call returns.
If the flag REG_WHOLE_HIVE_VOLATILE is not set, the given key is replaced
by the root of the hive file. The root's name is changed to the name
of the given key.
If the flag REG_WHOLE_HIVE_VOLATILE is set, a volatile hive is created,
the hive file is copied into it, and the resulting hive is linked to
the master hive. The given key must be in the master hive. (Usually
will be \Registry\User)
If the flag REG_REFRESH_HIVE is set (must be only flag) then the
the Hive will be restored to its state as of the last flush.
(The hive must be marked NOLAZY_FLUSH, and the caller must have
TCB privilege, and the handle must point to the root of the hive.
If the refresh fails, the hive will be corrupt, and the system
will bugcheck.)
Arguments:
Hive - supplies a pointer to the hive control structure for the hive
Cell - supplies index of node at root of tree to restore into
FileHandle - handle of the file to read from.
Return Value:
NTSTATUS - Result code from call, among the following:
<TBS>
--*/
{
NTSTATUS status;
PCELL_DATA ptar;
PCELL_DATA psrc;
PCMHIVE TmpCmHive;
HCELL_INDEX newroot;
HCELL_INDEX newcell;
HCELL_INDEX parent;
HCELL_INDEX list;
ULONG count;
ULONG i;
ULONG j;
LONG size;
LONG change; // must be signed
PHHIVE Hive;
HCELL_INDEX Cell;
HSTORAGE_TYPE Type;
ULONG NumberLeaves;
PHCELL_INDEX LeafArray;
PCM_KEY_INDEX Leaf;
PCM_KEY_FAST_INDEX FastLeaf;
PAGED_CODE();
CMLOG(CML_MAJOR, CMS_SAVRES) {
KdPrint(("CmRestoreKey:\n"));
KdPrint(("\tKCB=%08lx\n",KeyControlBlock));
KdPrint(("\tFileHandle=%08lx\n",FileHandle));
}
if (Flags & REG_REFRESH_HIVE) {
if ((Flags & ~REG_REFRESH_HIVE) != 0) {
//
// Refresh must be alone
//
return STATUS_INVALID_PARAMETER;
}
}
//
// If they want to do WHOLE_HIVE_VOLATILE, it's a completely different API.
//
if (Flags & REG_WHOLE_HIVE_VOLATILE) {
return(CmpLoadHiveVolatile(KeyControlBlock, FileHandle));
}
//
// If they want to do REFRESH_HIVE, that's a completely different api too.
//
if (Flags & REG_REFRESH_HIVE) {
CmpLockRegistryExclusive();
status = CmpRefreshHive(KeyControlBlock);
CmpUnlockRegistry();
return status;
}
Hive = KeyControlBlock->KeyHive;
Cell = KeyControlBlock->KeyCell;
//
// Disallow attempts to "restore" the master hive
//
if (Hive == &CmpMasterHive->Hive) {
return STATUS_ACCESS_DENIED;
}
CmpLockRegistryExclusive();
//
// Make sure this is the only handle open for this key, AND
// that key has not been deleted
//
if ((KeyControlBlock->RefCount != 1) || (KeyControlBlock->Delete)) {
CmpUnlockRegistry();
return(STATUS_CANNOT_DELETE);
}
DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
//
// Check for any open handles underneath the key we are restoring to.
//
if (CmpSearchForOpenSubKeys(KeyControlBlock)) {
//
// Cannot restore over a subtree with open handles in it.
//
CmpUnlockRegistry();
return(STATUS_CANNOT_DELETE);
}
ptar = HvGetCell(Hive, Cell);
//
// The subtree the caller wants does not exactly match a
// subtree. Make a temporary hive, load the file into it,
// tree copy the temporary to the active, and free the temporary.
//
//
// Create the temporary hive
//
if (!CmpInitializeHive(&TmpCmHive,
HINIT_FILE,
0,
HFILE_TYPE_PRIMARY,
NULL,
FileHandle,
NULL,
NULL,
NULL,
NULL)) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit1;
}
//
// Create a new target root, under which we will copy the new tree
//
if (ptar->u.KeyNode.Flags & KEY_HIVE_ENTRY) {
parent = HCELL_NIL; // root of hive, so parent is NIL
} else {
parent = ptar->u.KeyNode.Parent;
}
newroot = CmpCopyKeyPartial(
&(TmpCmHive->Hive),
TmpCmHive->Hive.BaseBlock->RootCell,
Hive,
parent,
TRUE
);
if (newroot == HCELL_NIL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit2;
}
//
// newroot has all the correct stuff, except that it has the
// source root's name, when it needs to have the target root's.
// So edit its name.
//
psrc = HvGetCell(Hive, Cell);
ptar = HvGetCell(Hive, newroot);
size = HvGetCellSize(Hive, ptar);
// psrc->old root
// ptar->new root
//
// make sure that new root has correct amount of space
// to hold name from old root
//
change = (LONG)psrc->u.KeyNode.NameLength -
(LONG)ptar->u.KeyNode.NameLength;
if (change != 0) {
newcell = HvReallocateCell(Hive, newroot, size+change);
if (newcell == HCELL_NIL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit2;
}
newroot = newcell;
ptar = HvGetCell(Hive, newroot);
}
status = STATUS_SUCCESS;
RtlMoveMemory(
(PVOID)&(ptar->u.KeyNode.Name[0]),
(PVOID)&(psrc->u.KeyNode.Name[0]),
psrc->u.KeyNode.NameLength
);
ptar->u.KeyNode.NameLength = psrc->u.KeyNode.NameLength;
if (psrc->u.KeyNode.Flags & KEY_COMP_NAME) {
ptar->u.KeyNode.Flags |= KEY_COMP_NAME;
} else {
ptar->u.KeyNode.Flags &= ~KEY_COMP_NAME;
}
//
// newroot is now ready to have subtree copied under it, do tree copy
//
if (CmpCopyTree(
&(TmpCmHive->Hive),
TmpCmHive->Hive.BaseBlock->RootCell,
Hive,
newroot
) == FALSE)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit2;
}
//
// The new root and the tree under it now look the way we want.
//
//
// Swap the new tree in for the old one.
//
ptar = HvGetCell(Hive, Cell);
parent = ptar->u.KeyNode.Parent;
if (ptar->u.KeyNode.Flags & KEY_HIVE_ENTRY) {
//
// root is actually the root of the hive. parent doesn't
// refer to it via a child list, but rather with an inter hive
// pointer. also, must update base block
//
ptar = HvGetCell( (&(CmpMasterHive->Hive)), parent);
ptar->u.KeyNode.u1.ChildHiveReference.KeyCell = newroot;
ptar = HvGetCell(Hive, newroot);
ptar->u.KeyNode.Parent = parent;
Hive->BaseBlock->RootCell = newroot;
} else {
//
// Notice that new root is *always* name of existing target,
// therefore, even in b-tree, old and new cell can share
// the same reference slot in the parent. So simply edit
// the new cell_index on the top of the old.
//
ptar = HvGetCell(Hive, parent);
Type = HvGetCellType(Cell);
list = ptar->u.KeyNode.SubKeyLists[Type];
count = ptar->u.KeyNode.SubKeyCounts[Type];
ptar = HvGetCell(Hive, list);
if (ptar->u.KeyIndex.Signature == CM_KEY_INDEX_ROOT) {
NumberLeaves = ptar->u.KeyIndex.Count;
LeafArray = &ptar->u.KeyIndex.List[0];
} else {
NumberLeaves = 1;
LeafArray = &list;
}
//
// Look in each leaf for the HCELL_INDEX we need to replace
//
for (i = 0; i < NumberLeaves; i++) {
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafArray[i]);
if (Leaf->Signature == CM_KEY_FAST_LEAF) {
FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
for (j=0; j < FastLeaf->Count; j++) {
if (FastLeaf->List[j].Cell == Cell) {
FastLeaf->List[j].Cell = newroot;
goto FoundCell;
}
}
} else {
for (j=0; j < Leaf->Count; j++) {
if (Leaf->List[j] == Cell) {
Leaf->List[j] = newroot;
goto FoundCell;
}
}
}
}
ASSERT(FALSE); // implies we didn't find it
// we should never get here
}
FoundCell:
//
// Fix up the key control block to point to the new root
//
KeyControlBlock->KeyCell = newroot;
KeyControlBlock->KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, newroot);
CmpReinsertKeyControlBlock(KeyControlBlock);
//
// Delete the old subtree and it's root cell
//
CmpDeleteTree(Hive, Cell);
CmpFreeKeyByCell(Hive, Cell, FALSE);
//
// Report the notify event
//
CmpReportNotify(KeyControlBlock->FullName,
KeyControlBlock->KeyHive,
KeyControlBlock->KeyCell,
REG_NOTIFY_CHANGE_NAME);
//
// Free the temporary hive
//
CmpDestroyTemporaryHive(TmpCmHive);
//
// We've given user chance to log on, so turn on quota
//
if (CmpProfileLoaded == FALSE) {
CmpProfileLoaded = TRUE;
CmpSetGlobalQuotaAllowed();
}
DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
CmpUnlockRegistry();
return status;
//
// Error exits
//
ErrorExit2:
CmpDestroyTemporaryHive(TmpCmHive);
ErrorExit1:
DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
CmpUnlockRegistry();
return status;
}
NTSTATUS
CmpLoadHiveVolatile(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
IN HANDLE FileHandle
)
/*++
Routine Description:
Creates a VOLATILE hive and loads it underneath the given Hive and Cell.
The data for the volatile hive is copied out of the given file. The
file is *NOT* in use by the registry when this returns.
Arguments:
Hive - Supplies the hive that the new hive is to be created under.
Currently this must be the Master Hive.
Cell - Supplies the HCELL_INDEX of the new hive's parent. (Usually
will by \Registry\User)
FileHandle - Supplies a handle to the hive file that will be copied
into the volatile hive.
Return Value:
NTSTATUS
--*/
{
PHHIVE Hive;
PCELL_DATA RootData;
PCMHIVE NewHive;
PCMHIVE TempHive;
HCELL_INDEX Cell;
HCELL_INDEX Root;
REGISTRY_COMMAND Command;
NTSTATUS Status;
UNICODE_STRING RootName;
UNICODE_STRING NewName;
USHORT NewNameLength;
PAGED_CODE();
CmpLockRegistryExclusive();
if (KeyControlBlock->Delete) {
CmpUnlockRegistry();
return(STATUS_KEY_DELETED);
}
Hive = KeyControlBlock->KeyHive;
Cell = KeyControlBlock->KeyCell;
//
// New hives can be created only under the master hive.
//
if (Hive != &CmpMasterHive->Hive) {
CmpUnlockRegistry();
return(STATUS_INVALID_PARAMETER);
}
//
// Create a temporary hive and load the file into it
//
if (!CmpInitializeHive(&TempHive,
HINIT_FILE,
0,
HFILE_TYPE_PRIMARY,
NULL,
FileHandle,
NULL,
NULL,
NULL,
NULL)) {
CmpUnlockRegistry();
return(STATUS_INSUFFICIENT_RESOURCES);
}
//
// Create the volatile hive.
//
if (!CmpInitializeHive(&NewHive,
HINIT_CREATE,
HIVE_VOLATILE,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL)) {
CmpDestroyTemporaryHive(TempHive);
CmpUnlockRegistry();
return(STATUS_INSUFFICIENT_RESOURCES);
}
//
// Create the target root
//
Root = CmpCopyKeyPartial(&TempHive->Hive,
TempHive->Hive.BaseBlock->RootCell,
&NewHive->Hive,
HCELL_NIL,
FALSE);
if (Root == HCELL_NIL) {
CmpDestroyTemporaryHive(TempHive);
CmpDestroyTemporaryHive(NewHive);
CmpUnlockRegistry();
return(STATUS_INSUFFICIENT_RESOURCES);
}
NewHive->Hive.BaseBlock->RootCell = Root;
//
// Copy the temporary hive into the volatile hive
//
if (!CmpCopyTree(&TempHive->Hive,
TempHive->Hive.BaseBlock->RootCell,
&NewHive->Hive,
Root))
{
CmpDestroyTemporaryHive(TempHive);
CmpDestroyTemporaryHive(NewHive);
CmpUnlockRegistry();
return(STATUS_INSUFFICIENT_RESOURCES);
}
//
// The volatile hive now has all the right stuff in all the right places,
// we just need to link it into the master hive.
//
RootData = HvGetCell(&NewHive->Hive,Root);
NewNameLength = KeyControlBlock->FullName.Length +
CmpHKeyNameLen(&RootData->u.KeyNode) +
sizeof(WCHAR);
NewName.Buffer = ExAllocatePool(PagedPool, NewNameLength);
if (NewName.Buffer == NULL) {
CmpDestroyTemporaryHive(TempHive);
CmpDestroyTemporaryHive(NewHive);
CmpUnlockRegistry();
return(STATUS_INSUFFICIENT_RESOURCES);
}
NewName.Length = NewName.MaximumLength = NewNameLength;
RtlCopyUnicodeString(&NewName, &KeyControlBlock->FullName);
RtlAppendUnicodeToString(&NewName, L"\\");
if (RootData->u.KeyNode.Flags & KEY_COMP_NAME) {
CmpCopyCompressedName(NewName.Buffer + (NewName.Length / sizeof(WCHAR)),
NewName.MaximumLength - NewName.Length,
RootData->u.KeyNode.Name,
CmpHKeyNameLen(&RootData->u.KeyNode));
NewName.Length += CmpHKeyNameLen(&RootData->u.KeyNode);
} else {
RootName.Buffer = RootData->u.KeyNode.Name;
RootName.Length = RootName.MaximumLength = RootData->u.KeyNode.NameLength;
RtlAppendUnicodeStringToString(&NewName,&RootName);
}
Status = CmpLinkHiveToMaster(&NewName,
NULL,
NewHive,
FALSE,
NULL);
if (NT_SUCCESS(Status)) {
Command.CmHive = NewHive;
Command.Command = REG_CMD_ADD_HIVE_LIST;
CmpWorkerCommand(&Command);
} else {
CmpDestroyTemporaryHive(NewHive);
}
CmpDestroyTemporaryHive(TempHive);
ExFreePool(NewName.Buffer);
if (NT_SUCCESS(Status)) {
//
// We've given user chance to log on, so turn on quota
//
if (CmpProfileLoaded == FALSE) {
CmpProfileLoaded = TRUE;
CmpSetGlobalQuotaAllowed();
}
}
CmpUnlockRegistry();
return(Status);
}
ULONG
CmpRefreshWorkerRoutine(
PCM_KEY_CONTROL_BLOCK Current,
PVOID Context1,
PVOID Context2
)
/*++
Routine Description:
Helper used by CmpRefreshHive when calling
CmpSearchKeyControlBlockTree.
If a match is found, the KCB is deleted and restart is returned.
Else, continue is returned.
Arguments:
Current - the kcb to examine
Context1 - the hive to match against
Context2 - nothing
Return Value:
if no match, return continue.
if match, return restart.
--*/
{
PAGED_CODE();
if (Current->KeyHive == (PHHIVE)Context1) {
//
// match. set deleted flag. restart search.
//
Current->Delete = TRUE;
CmpRemoveKeyControlBlock(Current);
Current->KeyHive = NULL;
Current->KeyCell = 0;
return KCB_WORKER_RESTART;
} else {
return KCB_WORKER_CONTINUE;
}
}
NTSTATUS
CmpRefreshHive(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock
)
/*++
Routine Description:
Backs out all changes to a hives since it was last flushed.
Used as a transaction abort by the security system.
Caller must have SeTcbPrivilege.
The target hive must have HIVE_NOLAZYFLUSH set.
KeyControlBlock must refer to the root of the hive (HIVE_ENTRY must
be set in the key.)
Any kcbs that point into this hive (and thus any handles open
against it) will be force to DELETED state. (If we do any work.)
All notifies pending against the hive will be flushed.
When we're done, only the tombstone kcbs, handles, and attached
notify blocks will be left.
WARNNOTE: Once reads have begun, if the operation fails, the hive
will be corrupt, so we will bugcheck.
Arguments:
KeyControlBlock - provides a reference to the root of the hive
we wish to refresh.
Return Value:
NTSTATUS
--*/
{
PHHIVE Hive;
HCELL_INDEX Cell;
PCELL_DATA pcell;
REGISTRY_COMMAND CommandArea;
PLIST_ENTRY ptr;
PCM_NOTIFY_BLOCK node;
PAGED_CODE();
//
// Check to see if the caller has the privilege to make this call.
//
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, KeGetPreviousMode())) {
return STATUS_PRIVILEGE_NOT_HELD;
}
if (KeyControlBlock->Delete) {
return(STATUS_KEY_DELETED);
}
Hive = KeyControlBlock->KeyHive;
Cell = KeyControlBlock->KeyCell;
//
// check to see if hive is of proper type
//
if ( ! (Hive->HiveFlags & HIVE_NOLAZYFLUSH)) {
return STATUS_INVALID_PARAMETER;
}
//
// punt if any volatile storage has been allocated
//
if (Hive->Storage[Volatile].Length != 0) {
return STATUS_UNSUCCESSFUL;
}
//
// check to see if call was applied to the root of the hive
//
pcell = HvGetCell(Hive, Cell);
if ( ! (pcell->u.KeyNode.Flags & KEY_HIVE_ENTRY)) {
return STATUS_INVALID_PARAMETER;
}
//
// Flush all NotifyBlocks attached to this hive
//
while (TRUE) {
//
// flush below will edit list, so restart at beginning each time
//
ptr = &(((PCMHIVE)Hive)->NotifyList);
if (ptr->Flink == NULL) {
break;
}
ptr = ptr->Flink;
node = CONTAINING_RECORD(ptr, CM_NOTIFY_BLOCK, HiveList);
ASSERT((node->KeyBody)->NotifyBlock == node);
CmpFlushNotify(node->KeyBody);
}
//
// Force all kcbs that refer to this hive to the deleted state.
//
CmpSearchKeyControlBlockTree(
CmpRefreshWorkerRoutine,
(PVOID)Hive,
NULL
);
//
// We cannot do the relevent I/O from this context, so send a
// command to the worker code.
//
CommandArea.Command = REG_CMD_REFRESH_HIVE;
CommandArea.Hive = Hive;
CmpWorkerCommand(&CommandArea);
//
// we're back (rather than bugchecked) so it worked
//
return STATUS_SUCCESS;
}
NTSTATUS
CmSaveKey(
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
IN HANDLE FileHandle
)
/*++
Routine Description:
Arguments:
KeyControlBlock - pointer to the KCB that describes the key
FileHandle - handle of the file to dump to.
Return Value:
NTSTATUS - Result code from call, among the following:
<TBS>
--*/
{
NTSTATUS status;
PCELL_DATA proot;
USHORT flags;
PCMHIVE TmpCmHive;
PCMHIVE CmHive;
HCELL_INDEX newroot;
PHHIVE Hive;
HCELL_INDEX Cell;
PAGED_CODE();
CMLOG(CML_MAJOR, CMS_SAVRES) {
KdPrint(("CmSaveKey:\n"));
KdPrint(("\tKCB=%08lx",KeyControlBlock));
KdPrint(("\tFileHandle=%08lx\n",FileHandle));
}
//
// Disallow attempts to "save" the master hive
//
Hive = KeyControlBlock->KeyHive;
Cell = KeyControlBlock->KeyCell;
if (Hive == &CmpMasterHive->Hive) {
return STATUS_ACCESS_DENIED;
}
CmpLockRegistryExclusive();
if (KeyControlBlock->Delete) {
CmpUnlockRegistry();
return STATUS_KEY_DELETED;
}
DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
if ( (Hive->HiveFlags & HIVE_NOLAZYFLUSH) &&
(Hive->DirtyCount != 0))
{
//
// It's a NOLAZY hive, and there's some dirty data, so writing
// out a snapshot of what's in memory will not give the caller
// consistent user data. Therefore, copy the on disk image
// instead of the memory image
//
//
// Note that this will generate weird results if the key
// being saved is not the root of the hive, since the
// resulting file will always be a copy of the entire hive, not
// just the subtree they asked for.
//
status = CmpSaveKeyByFileCopy((PCMHIVE)Hive, FileHandle);
CmpUnlockRegistry();
return status;
}
proot = HvGetCell(Hive, Cell);
flags = proot->u.KeyNode.Flags;
//
// Always try to copy the hive and write it out. This has the
// effect of compressing out unused free storage.
// If there isn't space, and the savekey is of the root of the
// hive, then just write it out directly. (i.e. don't fail on
// a whole hive restore just because we're out of memory.)
//
CMLOG(CML_FLOW, CMS_SAVRES) KdPrint(("\tSave of partial hive\n"));
//
// The subtree the caller wants does not exactly match a
// subtree. Make a temporary hive, tree copy the source
// to temp, write out the temporary, free the temporary.
//
//
// Create the temporary hive
//
TmpCmHive = CmpCreateTemporaryHive(FileHandle);
if (TmpCmHive == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorInsufficientResources;
}
//
// Create a root cell, mark it as such
//
newroot = CmpCopyKeyPartial(
Hive,
Cell,
&(TmpCmHive->Hive),
HCELL_NIL, // will force KEY_HIVE_ENTRY set
TRUE);
if (newroot == HCELL_NIL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorInsufficientResources;
}
TmpCmHive->Hive.BaseBlock->RootCell = newroot;
//
// Do a tree copy
//
if (CmpCopyTree(Hive, Cell, &(TmpCmHive->Hive), newroot) == FALSE) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorInsufficientResources;
}
//
// Write the file
//
TmpCmHive->FileHandles[HFILE_TYPE_EXTERNAL] = FileHandle;
status = HvWriteHive(&(TmpCmHive->Hive));
TmpCmHive->FileHandles[HFILE_TYPE_EXTERNAL] = NULL;
//
// Free the temporary hive
//
CmpDestroyTemporaryHive(TmpCmHive);
DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
CmpUnlockRegistry();
return status;
//
// Error exits
//
ErrorInsufficientResources:
if (TmpCmHive != NULL) {
CmpDestroyTemporaryHive(TmpCmHive);
}
if (flags & KEY_HIVE_ENTRY) {
//
// We failed, for want of space, trying to write out a
// tree that exactly matches a hive. Therefore,
// write it out as a raw dump.
//
CMLOG(CML_FLOW, CMS_SAVRES) KdPrint(("\tSave of full hive\n"));
CmHive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
ASSERT(CmHive->FileHandles[HFILE_TYPE_EXTERNAL] == NULL);
CmHive->FileHandles[HFILE_TYPE_EXTERNAL] = FileHandle;
status = HvWriteHive(Hive);
CmHive->FileHandles[HFILE_TYPE_EXTERNAL] = NULL;
} // else fall through to standard exit
DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
CmpUnlockRegistry();
return status;
}
NTSTATUS
CmpSaveKeyByFileCopy(
PCMHIVE CmHive,
HANDLE FileHandle
)
/*++
Routine Description:
Do special case of SaveKey by copying the hive file
Arguments:
CmHive - supplies a pointer to an HHive
FileHandle - open handle to target file
Return Value:
NTSTATUS - Result code from call, among the following:
--*/
{
PHBASE_BLOCK BaseBlock;
NTSTATUS status;
ULONG Offset;
ULONG Length;
ULONG Position;
REGISTRY_COMMAND CommandArea;
PUCHAR CopyBuffer;
ULONG BufferLength;
ULONG BytesToCopy;
PAGED_CODE();
//
// Attempt to allocate large buffer for copying stuff around. If
// we can't get one, just use the stash buffer.
//
BufferLength = CM_SAVEKEYBUFSIZE;
try {
CopyBuffer = ExAllocatePoolWithQuota(PagedPoolCacheAligned,
BufferLength);
} except(EXCEPTION_EXECUTE_HANDLER) {
CopyBuffer = NULL;
}
if (CopyBuffer == NULL) {
CopyBuffer = CmpStashBuffer;
BufferLength = HBLOCK_SIZE;
}
//
// Read the base block, step the sequence number, and write it out
//
status = STATUS_REGISTRY_IO_FAILED;
CmHive->FileHandles[HFILE_TYPE_EXTERNAL] = FileHandle;
Offset = 0;
CommandArea.Command = REG_CMD_HIVE_READ;
CommandArea.CmHive = CmHive;
CommandArea.FileType = HFILE_TYPE_PRIMARY;
CommandArea.Offset = &Offset;
CommandArea.Buffer = CopyBuffer;
CommandArea.FileSize = HBLOCK_SIZE;
CmpWorkerCommand(&CommandArea);
if (! NT_SUCCESS(CommandArea.Status)) {
goto ErrorExit;
}
BaseBlock = (PHBASE_BLOCK)CopyBuffer;
Length = BaseBlock->Length;
BaseBlock->Sequence1++;
Offset = 0;
if ( ! CmpFileWrite((PHHIVE)CmHive, HFILE_TYPE_EXTERNAL, &Offset,
CopyBuffer, HBLOCK_SIZE))
{
goto ErrorExit;
}
//
// Flush the external, so header will show corrupt until we're done
//
if (CmpFileFlush((PHHIVE)CmHive, HFILE_TYPE_EXTERNAL)) {
status = STATUS_SUCCESS;
}
//
// For span of data, read from master and write to external
//
for (Position = 0; Position < Length; Position += BytesToCopy) {
Offset = Position + HBLOCK_SIZE;
BytesToCopy = Length-Position;
if (BytesToCopy > BufferLength) {
BytesToCopy = BufferLength;
}
CommandArea.Command = REG_CMD_HIVE_READ;
CommandArea.CmHive = CmHive;
CommandArea.FileType = HFILE_TYPE_PRIMARY;
CommandArea.Offset = &Offset;
CommandArea.Buffer = CopyBuffer;
CommandArea.FileSize = BytesToCopy;
CmpWorkerCommand(&CommandArea);
if (! NT_SUCCESS(CommandArea.Status)) {
goto ErrorExit;
}
Offset = Position + HBLOCK_SIZE;
if ( ! CmpFileWrite((PHHIVE)CmHive, HFILE_TYPE_EXTERNAL, &Offset,
CopyBuffer, BytesToCopy))
{
goto ErrorExit;
}
}
//
// Flush the external, so data is there before we update the header
//
if (CmpFileFlush((PHHIVE)CmHive, HFILE_TYPE_EXTERNAL)) {
status = STATUS_SUCCESS;
}
//
// Reread the base block, sync the seq #, rewrite it.
// (Brute force, but means no memory alloc - always works)
//
Offset = 0;
CommandArea.Command = REG_CMD_HIVE_READ;
CommandArea.CmHive = CmHive;
CommandArea.FileType = HFILE_TYPE_PRIMARY;
CommandArea.Offset = &Offset;
CommandArea.Buffer = CopyBuffer;
CommandArea.FileSize = HBLOCK_SIZE;
CmpWorkerCommand(&CommandArea);
if (! NT_SUCCESS(CommandArea.Status)) {
goto ErrorExit;
}
BaseBlock->Sequence1++; // it got trampled when we reread it
BaseBlock->Sequence2++;
Offset = 0;
if ( ! CmpFileWrite((PHHIVE)CmHive, HFILE_TYPE_EXTERNAL, &Offset,
CopyBuffer, HBLOCK_SIZE))
{
goto ErrorExit;
}
//
// Flush the external, and we are done
//
if (CmpFileFlush((PHHIVE)CmHive, HFILE_TYPE_EXTERNAL)) {
status = STATUS_SUCCESS;
}
ErrorExit:
if (CopyBuffer != CmpStashBuffer) {
ExFreePool(CopyBuffer);
}
CmHive->FileHandles[HFILE_TYPE_EXTERNAL] = NULL;
return status;
}
PCMHIVE
CmpCreateTemporaryHive(
IN HANDLE FileHandle
)
/*++
Routine Description:
Allocates and inits a temporary hive.
Arguments:
FileHandle - Supplies the handle of the file to back the hive.
Return Value:
Pointer to CmHive.
If NULL the operation failed.
--*/
{
PCMHIVE TempHive;
PAGED_CODE();
//
// NOTE: Hive will get put on CmpHiveListHead list.
// Make sure CmpDestroyTemporaryHive gets called to remove it.
//
if (CmpInitializeHive(&TempHive,
HINIT_CREATE,
HIVE_VOLATILE,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL)) {
return(TempHive);
} else {
return(NULL);
}
}
VOID
CmpDestroyTemporaryHive(
PCMHIVE CmHive
)
/*++
Routine Description:
Frees all the pieces of a hive.
Arguments:
CmHive - CM level hive structure to free
Return Value:
None.
--*/
{
PAGED_CODE();
CMLOG(CML_MINOR, CMS_SAVRES) {
KdPrint(("CmpDestroyTemporaryHive:\n"));
KdPrint(("\tCmHive=%08lx\n", CmHive));
}
if (CmHive == NULL) {
return;
}
//
// NOTE: Hive is on CmpHiveListHead list.
// Remove it.
//
RemoveEntryList(&CmHive->HiveList);
HvFreeHive(&(CmHive->Hive));
CmpFree(CmHive, sizeof(CMHIVE));
return;
}