windows-nt/Source/XPSP1/NT/base/ntsetup/pnpsif/restore.c

1529 lines
52 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
restore.c
Abstract:
This module contains the following Plug and Play registry merge-restore
routines:
AsrRestorePlugPlayRegistryData
Author:
Jim Cavalaris (jamesca) 3-07-2000
Environment:
User-mode only.
Revision History:
07-March-2000 jamesca
Creation and initial implementation.
--*/
//
// includes
//
#include "precomp.h"
#include "util.h"
#include "debug.h"
#include <regstr.h>
#include <cfgmgr32.h>
//
// private memory allocation definitions
//
#define MyMalloc(size) LocalAlloc(0, size);
#define MyFree(entry) LocalFree(entry);
#define MyRealloc(entry,size) LocalReAlloc(entry, size, 0);
//
// global variables to store the root keys we're working on.
//
// (we really shouldn't have to do this, but it's the easiest way for us to
// reach into one set of keys while recursing through the other set.)
//
HKEY PnpSifRestoreSourceEnumKeyHandle = NULL;
HKEY PnpSifRestoreTargetEnumKeyHandle = NULL;
HKEY PnpSifRestoreSourceClassKeyHandle = NULL;
HKEY PnpSifRestoreTargetClassKeyHandle = NULL;
//
// private definitions
//
// INVALID_FLAGS macro from pnp\inc\cfgmgrp.h
#define INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
// private flags for internal RestoreSpecialRegistryData routine
#define PNPSIF_RESTORE_TYPE_DEFAULT (0x00000000)
#define PNPSIF_RESTORE_TYPE_ENUM (0x00000001)
#define PNPSIF_RESTORE_TYPE_CLASS (0x00000002)
#define PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION (0x00000010)
#define PNPSIF_RESTORE_BITS (0x00000013)
// Enum level definitions describing the subkey types for the specified handles.
#define ENUM_LEVEL_ENUMERATORS (0x0000003)
#define ENUM_LEVEL_DEVICES (0x0000002)
#define ENUM_LEVEL_INSTANCES (0x0000001)
// Class level definitions describing the subkey types for the specified handles.
#define CLASS_LEVEL_CLASSGUIDS (0x0000002)
#define CLASS_LEVEL_DRVINSTS (0x0000001)
//
// private prototypes
//
BOOL
RestoreEnumKey(
IN HKEY hSourceEnumKey,
IN HKEY hTargetEnumKey
);
BOOL
RestoreClassKey(
IN HKEY hSourceClassKey,
IN HKEY hTargetClassKey
);
BOOL
RestoreSpecialRegistryData(
IN HKEY hSourceKey,
IN HKEY hTargetKey,
IN ULONG ulLevel,
IN OUT PVOID pContext,
IN ULONG ulFlags
);
BOOL
MyReplaceKey(
IN HKEY hSourceKey,
IN HKEY hTargetKey
);
BOOL
IsString4DigitOrdinal(
IN LPTSTR pszSubkeyName
);
BOOL
IsDeviceConfigured(
IN HKEY hInstanceKey
);
BOOL
ReplaceClassKeyForInstance(
IN HKEY hSourceInstanceKey,
IN HKEY hSourceRootClassKey,
IN HKEY hTargetRootClassKey
);
//
// routines
//
BOOL
AsrRestorePlugPlayRegistryData(
IN HKEY SourceSystemKey,
IN HKEY TargetSystemKey,
IN DWORD Flags,
IN PVOID Reserved
)
/*++
Routine Description:
This routine restores plug and play data from the the specified source
SYSTEM key to the specified target SYSTEM key, merging intermediate subkeys
and values as appropriate.
Arguments:
SourceSystemKey - Handle to the HKLM\SYSTEM key within the "source"
registry, whose data is to be "merged" into the
corresponding SYSTEM key of the "target" registry, as
specified by TargetSystemKey.
TargetSystemKey - Handle to the HKLM\SYSTEM key within the "target"
registry, that is to receive additional data from the
corresponding SYSTEM key of the "source" registry, as
specified by SourceSystemKey.
Flags - Not used, must be zero.
Reserved - Reserved for future use, must be NULL.
Return Value:
TRUE if successful, FALSE otherwise. Upon failure, additional information
can be retrieved by calling GetLastError().
Notes:
This routine was written specifically to assist in the restoration of the
Plug-and-Play specific registry keys that cannot simply be restored from
backup. It is intended to be called within the context of a
backup-and-restore application's "restore" phase.
During a backup-and-restore application's "restore" phase, the registry that
has been backed-up onto backup medium is to become the new system registry.
Certain Plug and Play values and registry keys, are actually copied into the
backed-up registry from the current registry. Rather than using the backup
registry, or the current registry exclusively, Plug and Play data contained
in these keys should be merged from the current registry into the backup
registry in a way that is appropriate for each key. The backup registry can
then safely replace the current registry as the system registry.
In the context of a backup-and-restore application's "restore" phase, the
"Source" registry key is the one contained in the current running system
registry. The "Target" registry is that which has been backed-up, and will
become the system registry, upon reboot.
The calling process must have both the SE_BACKUP_NAME and SE_RESTORE_NAME
privileges.
--*/
{
LONG result = ERROR_SUCCESS;
HKEY hSystemSelectKey = NULL;
TCHAR pszRegKeySelect[] = TEXT("Select");
TCHAR pszRegValueCurrent[] = TEXT("Current");
TCHAR szBuffer[128];
DWORD dwType, dwSize;
DWORD dwSourceCCS, dwTargetCCS;
//
// Make sure the user didn't pass us anything in the Reserved parameter.
//
if (Reserved) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// Make sure that the user supplied us with valid flags.
//
if(INVALID_FLAGS(Flags, 0x0)) {
SetLastError(ERROR_INVALID_FLAGS);
return FALSE;
}
//
// Determine the CurrentControlSet for the source.
//
result = RegOpenKeyEx(SourceSystemKey,
pszRegKeySelect,
0,
KEY_READ,
&hSystemSelectKey);
if (result != ERROR_SUCCESS) {
hSystemSelectKey = NULL;
goto Clean0;
}
dwSize = sizeof(DWORD);
result = RegQueryValueEx(hSystemSelectKey,
pszRegValueCurrent,
0,
&dwType,
(LPBYTE)&dwSourceCCS,
&dwSize);
RegCloseKey(hSystemSelectKey);
hSystemSelectKey = NULL;
if ((result != ERROR_SUCCESS) ||
(dwType != REG_DWORD)) {
goto Clean0;
}
//
// Determine the CurrentControlSet for the target.
//
result = RegOpenKeyEx(TargetSystemKey,
pszRegKeySelect,
0,
KEY_READ,
&hSystemSelectKey);
if (result != ERROR_SUCCESS) {
hSystemSelectKey = NULL;
goto Clean0;
}
dwSize = sizeof(DWORD);
result = RegQueryValueEx(hSystemSelectKey,
pszRegValueCurrent,
0,
&dwType,
(LPBYTE)&dwTargetCCS,
&dwSize);
RegCloseKey(hSystemSelectKey);
hSystemSelectKey = NULL;
if ((result != ERROR_SUCCESS) ||
(dwType != REG_DWORD)) {
goto Clean0;
}
//
// Open the source CurrentControlSet\Enum key.
//
wsprintf(szBuffer, TEXT("ControlSet%03d\\Enum"), dwSourceCCS);
result = RegOpenKeyEx(SourceSystemKey,
szBuffer,
0,
KEY_READ, // only need to read from the source
&PnpSifRestoreSourceEnumKeyHandle);
if (result != ERROR_SUCCESS) {
goto Clean0;
}
//
// Open the target CurrentControlSet\Enum key.
//
wsprintf(szBuffer, TEXT("ControlSet%03d\\Enum"), dwTargetCCS);
result = RegOpenKeyEx(TargetSystemKey,
szBuffer,
0,
KEY_ALL_ACCESS, // need full access to the target
&PnpSifRestoreTargetEnumKeyHandle);
if (result != ERROR_SUCCESS) {
goto Clean0;
}
//
// Open the source CurrentControlSet\Control\Class key.
//
wsprintf(szBuffer, TEXT("ControlSet%03d\\Control\\Class"), dwSourceCCS);
result = RegOpenKeyEx(SourceSystemKey,
szBuffer,
0,
KEY_READ, // only need to read from the source
&PnpSifRestoreSourceClassKeyHandle);
if (result != ERROR_SUCCESS) {
goto Clean0;
}
//
// Open the target CurrentControlSet\Control\Class key.
//
wsprintf(szBuffer, TEXT("ControlSet%03d\\Control\\Class"), dwTargetCCS);
result = RegOpenKeyEx(TargetSystemKey,
szBuffer,
0,
KEY_ALL_ACCESS, // need full access to the target
&PnpSifRestoreTargetClassKeyHandle);
if (result != ERROR_SUCCESS) {
goto Clean0;
}
//
// NOTE: We restore the Enum branch first, then restore the Class branch.
// The code that restores these keys depends on it, so do not change this!!
//
// This order makes sense, because relevant Class keys correspond to exactly
// one Enum instance key (but an Enum key may or may not have a Class key),
// so Class keys are only really meaningful in the context of the instance
// key they belong to. This behavior should NOT need to change.
//
//
// Do the merge-restore for the Enum keys, ignore any errors.
//
if (!RestoreEnumKey(PnpSifRestoreSourceEnumKeyHandle,
PnpSifRestoreTargetEnumKeyHandle)) {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RestoreEnumKey failed with error == 0x%08lx\n"),
GetLastError()));
}
//
// Do the merge-restore for the Class keys, ignore any errors.
//
if (!RestoreClassKey(PnpSifRestoreSourceClassKeyHandle,
PnpSifRestoreTargetClassKeyHandle)) {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RestoreClassKey failed with error == 0x%08lx\n"),
GetLastError()));
}
Clean0:
//
// Close the open handles.
//
if (PnpSifRestoreSourceEnumKeyHandle) {
RegCloseKey(PnpSifRestoreSourceEnumKeyHandle);
PnpSifRestoreSourceEnumKeyHandle = NULL;
}
if (PnpSifRestoreTargetEnumKeyHandle) {
RegCloseKey(PnpSifRestoreTargetEnumKeyHandle);
PnpSifRestoreTargetEnumKeyHandle = NULL;
}
if (PnpSifRestoreSourceClassKeyHandle) {
RegCloseKey(PnpSifRestoreSourceClassKeyHandle);
PnpSifRestoreSourceClassKeyHandle = NULL;
}
if (PnpSifRestoreTargetClassKeyHandle) {
RegCloseKey(PnpSifRestoreTargetClassKeyHandle);
PnpSifRestoreTargetClassKeyHandle = NULL;
}
if (result != ERROR_SUCCESS) {
SetLastError(result);
}
return (result == ERROR_SUCCESS);
} // AsrRestorePlugPlayRegistryData()
//
// private worker routines for AsrRestorePlugPlayRegistryData
//
BOOL
RestoreEnumKey(
IN HKEY hSourceEnumKey,
IN HKEY hTargetEnumKey
)
/*++
Routine Description:
Restores new device instances in the source (current) Enum key to the target
Enum key, located in the backup registry to be restored. By doing this, the
Enum key from the backup set can safely replace the current Enum key.
All intermediate values from the source (current) registry are restored to
the target (backup), to account for the device instance hash values located
at the root of the Enum key, that have been updated during setup.
During an ASR backup and restore operation, the hash values in the source
(current) registry are propogated to the current registry during textmode
setup, as they are stored the asrpnp.sif file. Since hash values may be
modified by setup, the values in the source (current) registry will be more
relevant than those in the target (backup), so source values should always
be preseverd.
Arguments:
hSourceEnumKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Enum key within
the "source" registry, whose data is to be "merged" into
the corresponding SYSTEM key of the "target" registry, as
specified by hTargetEnumKey.
hTargetEnumKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Enum key within
the "target" registry, that is to receive additional data
from the corresponding SYSTEM key of the "source" registry,
as specified by hSourceEnumKey.
Return Value:
TRUE if successful, FALSE otherwise. Upon failure, additional information
can be retrieved by calling GetLastError().
--*/
{
BOOL bIsRootEnumerated = FALSE;
return RestoreSpecialRegistryData(hSourceEnumKey,
hTargetEnumKey,
ENUM_LEVEL_ENUMERATORS, // (0x0000003)
(PVOID)&bIsRootEnumerated,
PNPSIF_RESTORE_TYPE_ENUM |
PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION
);
} // RestoreEnumKey
BOOL
RestoreClassKey(
IN HKEY hSourceClassKey,
IN HKEY hTargetClassKey
)
/*++
Routine Description:
Restores new elements of the source Class key to the target Class key,
located in the backup registry to be restored.
Intermediate values from the source registry are coied to the the target
registry only when they do not already exist in the target. Otherwise, the
target values are preseved.
Arguments:
hSourceClassKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Control\Class
key within the "source" registry, whose data is to be
"merged" into the corresponding SYSTEM key of the "target"
registry, as specified by hTargetClassKey.
hTargetClassKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Control\Class
key within the "target" registry, that is to receive
additional data from the corresponding SYSTEM key of the
"source" registry, as specified by hSourceClassKey.
Return Value:
TRUE if successful, FALSE otherwise. Upon failure, additional information
can be retrieved by calling GetLastError().
--*/
{
return RestoreSpecialRegistryData(hSourceClassKey,
hTargetClassKey,
CLASS_LEVEL_CLASSGUIDS, // (0x00000002)
(PVOID)NULL,
PNPSIF_RESTORE_TYPE_CLASS
);
} // RestoreClassKey
BOOL
RestoreSpecialRegistryData(
IN HKEY hSourceKey,
IN HKEY hTargetKey,
IN ULONG ulLevel,
IN OUT PVOID pContext,
IN ULONG ulFlags
)
/*++
Routine Description:
This routine restores the specified source key to the specified target key,
merging intermediate subkeys and values. Values in the subkeys located
above the specified depth level are merged from the source into the target
(with collisions handled according to the specified flags). Subkeys and
values at and below the level specified are merged from the source key to
the target key, with subkeys from the sources replacing any corresponding
subkeys in the target.
Arguments:
hSourceKey - Handle to a key within the source registry, whose data is to be
"merged" into the corresponding key of the target registry, as
specified by hTargetKey.
hTargetKey - Handle to a key within the target registry, that is to receive
data from the corresponding key of the source registry, as
specified by hSourceKey.
ulLevel - Specifies the subkey depth at which "replacement" will
take place. For subkeys above that depth, data in the target
registry will be preserved if present, and copied otherwise.
For keys at the specified level, data from the specified target
key will be replaced by the source key.
pContext - Specifies caller-supplied context for the operation that is
specific to the type of subkeys being restored (see ulFlags
below), and the specified ulLevel parameter.
ulFlags - Supplies flags specifying options for the restoration of the
registry keys. May be one of the following values:
PNPSIF_RESTORE_TYPE_ENUM:
Specifies that the subkeys being restored are subkeys of
the SYSTEM\CurrentControlSet\Enum branch. Device hardware
settings can be inspected at the appropriate ulLevel.
For Enum branch subkeys, the ulLevel parameter describes
also the type of the subkeys contained under the hSourceKey
and hTargetKey keys. May be one of:
ENUM_LEVEL_ENUMERATORS
ENUM_LEVEL_DEVICES
ENUM_LEVEL_INSTANCES
PNPSIF_RESTORE_TYPE_CLASS:
Specifies that the subkeys being restored are subkeys of
the SYSTEM\CurrentControlSet\Control\Class branch. Setup
class or device software settings can be inspected at the
appropriate ulLevel.
For Class branch subkeys, the ulLevel parameter also
describes the type of the subkeys contained under the
hSourceKey and hTargetKey keys. May be one of:
CLASS_LEVEL_CLASSGUIDS
CLASS_LEVEL_DRVINSTS
PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION:
When a collision occurs while merging values between the
source and target at intermediate levels, replace the
target value with that from the source (the default
behavior is to preserve existing target values above
'ulLevel' in depth from the supplied keys; below 'ulLevel',
only source key values will be present.)
NOTE: it's probably worth noting that a corresponding flag for
replace-target-keys-on-collision is not at all necessary, since
such a behavior can already be implemented with the ulLevel
parameter. (i.e. the ulLevel parameter actually specifies the
level at which all source keys will replace target keys; to
specify that this should ALWAYS be done is the same as
specifying ulLevel == 0)
Return Value:
TRUE if successful, FALSE otherwise. Upon failure, additional information
can be retrieved by calling GetLastError().
Notes:
This routine was written specifically to assist in the restoration of the
Plug-and-Play specific registry keys that cannot simply be restored from
backup. It is intended to be called within the context of a
backup-and-restore application's "restore" phase.
During a backup-and-restore application's "restore" phase, the registry that
has been backed-up onto backup medium is to become the new system registry.
Certain Plug and Play values and registry keys, are actually copied into the
backed-up registry from the current registry. Rather than using the backup
registry, or the current registry exclusively, Plug and Play data contained
in these keys should be merged from the current registry into the backup
registry in a way that is appropriate for each key. The backup registry can
then safely replace the current registry as the system registry.
In the context of a backup-and-restore application's "restore" phase, the
"Source" registry key is the one contained in the current running system
registry. The "Target" registry is that which has been backed-up, and will
become the system registry, upon reboot.
The different restore behaviors required for each of the different sets of
keys can be specified with the appropriate ulLevel, and ulFlags.
--*/
{
LONG result = ERROR_SUCCESS;
DWORD dwIndex = 0;
DWORD dwSubkeyCount, dwMaxSubkeyNameLength;
DWORD dwValueCount, dwMaxValueNameLength, dwMaxValueDataLength;
DWORD dwDisposition;
LPTSTR pszSubkeyName = NULL, pszValueName = NULL;
LPBYTE pValueData = NULL;
BOOL bPossibleRedundantInstanceKeys = FALSE;
//
// Validate parameters
//
if (INVALID_FLAGS(ulFlags, PNPSIF_RESTORE_BITS)) {
SetLastError(ERROR_INVALID_FLAGS);
return FALSE;
}
if ((hTargetKey == NULL) ||
(hTargetKey == INVALID_HANDLE_VALUE) ||
(hSourceKey == NULL) ||
(hSourceKey == INVALID_HANDLE_VALUE)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (ulLevel == 0) {
//
// This is the level at which we should stop merging, so just replace
// hTargetKey with hSourceKey, and we're done.
//
return MyReplaceKey(hSourceKey, hTargetKey);
} else {
//
// At levels above the replacement level, perform a non-destructive
// merge of intermediate subkeys and values; that is, only copy keys and
// values from the source to the target if they do not already exist at
// the target. Otherwise, leave the target keys and values alone, and
// keep traversing subkeys as necessary.
//
//
// Find out information about the hSourceKey values and subkeys.
//
result = RegQueryInfoKey(hSourceKey,
NULL,
NULL,
NULL,
&dwSubkeyCount,
&dwMaxSubkeyNameLength,
NULL,
&dwValueCount,
&dwMaxValueNameLength,
&dwMaxValueDataLength,
NULL,
NULL);
if (result != ERROR_SUCCESS) {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegQueryInfoKey returned error == 0x%08lx\n"),
result));
goto Clean0;
}
//
// Allocate space to fold the largest hSourceKey subkey, value and data.
//
dwMaxSubkeyNameLength++;
pszSubkeyName = MyMalloc(dwMaxSubkeyNameLength * sizeof(TCHAR));
if (pszSubkeyName == NULL) {
result = ERROR_NOT_ENOUGH_MEMORY;
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: MyMalloc failed allocating subkey name string, error == 0x%08lx\n"),
result));
goto Clean0;
}
dwMaxValueNameLength++;
pszValueName = MyMalloc(dwMaxValueNameLength * sizeof(TCHAR));
if (pszValueName == NULL) {
result = ERROR_NOT_ENOUGH_MEMORY;
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: MyMalloc failed allocating value name string, error == 0x%08lx\n"),
result));
goto Clean0;
}
pValueData = MyMalloc(dwMaxValueDataLength * sizeof(TCHAR));
if (pValueData == NULL) {
result = ERROR_NOT_ENOUGH_MEMORY;
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: MyMalloc failed allocating value data buffer, error == 0x%08lx\n"),
result));
goto Clean0;
}
//
// Enumerate all hSourceKey values.
//
for (dwIndex = 0; dwIndex < dwValueCount; dwIndex++) {
DWORD dwValueNameLength = dwMaxValueNameLength;
DWORD dwValueDataLength = dwMaxValueDataLength;
DWORD dwType;
result = RegEnumValue(hSourceKey,
dwIndex,
pszValueName,
&dwValueNameLength,
0,
&dwType,
pValueData,
&dwValueDataLength);
if (result != ERROR_SUCCESS) {
//
// Error enumerating values - whatchya gonna do?
// Just move on and try to merge subkeys the best we can?
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegEnumValue returned error == 0x%08lx\n"),
result));
goto EnumSubkeys;
}
DBGTRACE( DBGF_INFO,
(TEXT("PNPSIF: Enumerated value %d == '%ws' on hSourceKey.\n"),
dwIndex, pszValueName));
//
// Query to see if this value exists in the hTargetKey
//
result = RegQueryValueEx(hTargetKey,
pszValueName,
0,
NULL,
NULL,
NULL);
if ((result == ERROR_SUCCESS) &&
!(ulFlags & PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION)) {
//
// The enumerated value already exists under the target key, and
// we are NOT supposed to replace it.
//
DBGTRACE( DBGF_INFO,
(TEXT("PNPSIF: Value '%ws' already exists on hTargetKey.\n"),
pszValueName));
} else if ((result == ERROR_FILE_NOT_FOUND) ||
(ulFlags & PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION)){
//
// The enumerated value doesn't exist under the target key, or
// it does and we're supposed to replace it.
//
result = RegSetValueEx(hTargetKey,
pszValueName,
0,
dwType,
pValueData,
dwValueDataLength);
if (result != ERROR_SUCCESS) {
//
// Error setting value - whatchya gonna do?
// Just move on to the next enumerated value.
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegSetValueEx failed setting value '%ws', error == 0x%08lx\n"),
pszValueName, result));
}
} else {
//
// RegQueryValueEx returned some other error - weird?
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegQueryValueEx failed for value '%ws', error == 0x%08lx\n"),
pszValueName, result));
}
}
EnumSubkeys:
//
// Perform special processing of the device instance subkeys.
//
if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
(ARGUMENT_PRESENT(pContext)) &&
(ulLevel == ENUM_LEVEL_INSTANCES)) {
if (*((PBOOL)pContext)) {
//
// For root enumerated devices, check for the possibility that
// redundant instance keys of a device might have been created
// in the current registry, in which case we should ignore them
// when migrating to the target registry.
//
//
// The possibility of redundant root-enumerated device
// instance keys exists when:
//
// - the device is root-enumerated
//
// - instances of this device already exist in the target hive
//
// a particular device instance is redundant if it is a newly
// created instance in the target registry for a device where
// other instances already exist.
//
DWORD dwTargetSubkeyCount = 0;
if (RegQueryInfoKey(hTargetKey,
NULL,
NULL,
NULL,
&dwTargetSubkeyCount,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL) != ERROR_SUCCESS) {
dwTargetSubkeyCount = 0;
}
bPossibleRedundantInstanceKeys = (dwTargetSubkeyCount > 0);
}
}
//
// Enumerate all hSourceKey subkeys.
//
for (dwIndex = 0; dwIndex < dwSubkeyCount; dwIndex++) {
HKEY hTargetSubkey = NULL, hSourceSubkey = NULL;
DWORD dwSubkeyNameLength = dwMaxSubkeyNameLength;
result = RegEnumKeyEx(hSourceKey,
dwIndex,
pszSubkeyName,
&dwSubkeyNameLength,
0,
NULL,
NULL,
NULL);
if (result != ERROR_SUCCESS) {
//
// Error enumerating subkeys - whatchya gonna do?
// There is nothing left to do, so just exit.
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegEnumKeyEx returned error == 0x%08lx\n"),
result));
goto Clean0;
}
DBGTRACE( DBGF_INFO,
(TEXT("PNPSIF: enumerated subkey %d == '%ws' on hSourceKey.\n"),
dwIndex, pszSubkeyName));
//
// Perform special processing of the Enum subkeys.
//
if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
(ARGUMENT_PRESENT(pContext)) &&
(ulLevel == ENUM_LEVEL_ENUMERATORS)) {
//
// At the start of the recursive enumeration for each
// enumerator subkey, reset our (global) BOOL context
// variable to keep track of whether subsequent device
// subkeys represent "ROOT" enumerated devices.
//
PBOOL pbIsRootEnumerated = (PBOOL)pContext;
*pbIsRootEnumerated =
(BOOL)(lstrcmpi((LPTSTR)pszSubkeyName,
REGSTR_KEY_ROOTENUM) == 0);
}
//
// Open this subkey in hSourceKey
//
result = RegOpenKeyEx(hSourceKey,
pszSubkeyName,
0,
KEY_READ,
&hSourceSubkey);
if (result == ERROR_SUCCESS) {
//
// Attempt to open this subkey in the hTargetKey
//
result = RegOpenKeyEx(hTargetKey,
pszSubkeyName,
0,
KEY_READ,
&hTargetSubkey);
if ((result != ERROR_SUCCESS) &&
(bPossibleRedundantInstanceKeys) &&
(IsString4DigitOrdinal(pszSubkeyName))) {
//
// The subkey is a root-enumerated instance key with a
// 4-digit ordinal name (most-likely autogenerated), where
// we may have redundant keys.
//
// If this key doesn't exist in the target registry, it was
// likely a redundant, autogenerated instance created during
// setup because a previous autogenerated key (i.e. one we
// just migrated) already existed, we just won't migrate it.
//
ASSERT(ulFlags & PNPSIF_RESTORE_TYPE_ENUM);
ASSERT(ulLevel == ENUM_LEVEL_INSTANCES);
ASSERT((ARGUMENT_PRESENT(pContext)) && (*((PBOOL)pContext)));
} else {
if (result == ERROR_SUCCESS) {
//
// We successfully opened the target subkey above (we
// just weren't supposed to delete it) so we should have
// a valid handle.
//
ASSERT(hTargetSubkey != NULL);
dwDisposition = REG_OPENED_EXISTING_KEY;
} else {
//
// Create the target subkey.
//
ASSERT(hTargetSubkey == NULL);
result = RegCreateKeyEx(hTargetKey,
pszSubkeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hTargetSubkey,
&dwDisposition);
if (result == ERROR_SUCCESS) {
ASSERT(dwDisposition == REG_CREATED_NEW_KEY);
}
}
if (result == ERROR_SUCCESS) {
//
// Check if the key had already existed.
//
if (dwDisposition == REG_CREATED_NEW_KEY) {
//
// We just created this key in the target to replace it
// with the corresponding key in the source.
//
if (!MyReplaceKey(hSourceSubkey, hTargetSubkey)) {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
GetLastError()));
}
} else if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
(ulLevel == ENUM_LEVEL_INSTANCES) &&
(!IsDeviceConfigured(hTargetSubkey)) &&
(IsDeviceConfigured(hSourceSubkey))) {
//
// If we are enumerating instances, check if the
// instance keys in the target and source are
// properly configured.
//
//
// If it is not configured in the target, but is
// configured in the source - then we DO want to
// replace the target instance key, because it might
// be some sort of critical device - like a boot
// device. Even if it's not critical, if the device
// has not been seen since before the last upgrade
// *prior* to backup, we can't really have much
// confidence in those settings anyways. If neither
// the target or source are configured, we may as
// well just keep the target, like we do for
// everything else.
//
if (MyReplaceKey(hSourceSubkey, hTargetSubkey)) {
//
// If we successfully replaced the target instance
// key for this device with the source, then also
// replace the corresponding Class key for this
// instance from the target with the Class key from
// the source.
//
// NOTE: this works because we always restore the
// Enum branch before retoring the Class branch, so
// the Class keys haven't been touched yet.
//
if (!ReplaceClassKeyForInstance(hSourceSubkey,
PnpSifRestoreSourceClassKeyHandle,
PnpSifRestoreTargetClassKeyHandle)) {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: ReplaceClassKeyForInstance failed with error == 0x%08lx\n"),
GetLastError()));
}
} else {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
GetLastError()));
}
} else if ((ulLevel-1) != 0) {
//
// We're still above the replacement level, and the key
// previously existed in the target, so we're not
// supposed to replace it. Since the next level is NOT
// the replacement level, just follow the keys down
// recursively.
//
ASSERT(dwDisposition == REG_OPENED_EXISTING_KEY);
if (!RestoreSpecialRegistryData(hSourceSubkey,
hTargetSubkey,
ulLevel-1,
(PVOID)pContext,
ulFlags)) {
//
// Error handling the subkeys - whatcha gonna do?
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RestoreSpecialRegistryData failed for subkey %ws at level %d, error == 0x%08lx\n"),
pszSubkeyName, ulLevel-1,
GetLastError()));
}
} else {
//
// The key already exists, so don't replace it. Also,
// the next level is the replacement level, so don't
// recurse either (else we'd replace it). Basically,
// just do nothing here.
//
ASSERT(ulLevel == 1);
ASSERT(dwDisposition == REG_OPENED_EXISTING_KEY);
}
//
// Close the open target subkey.
//
RegCloseKey(hTargetSubkey);
} else {
//
// could not open/create subkey in taget registry.
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegCreateKey failed to create target subkey %s with error == 0x%08lx\n"),
pszSubkeyName, result));
}
}
//
// Close the enumerated hSourceKey subkey.
//
RegCloseKey(hSourceSubkey);
} else {
//
// Could not open the enumerated hSourceKey subkey.
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegOpenKey failed to open existing subkey %s with error == 0x%08lx\n"),
pszSubkeyName, result));
}
} // for (dwIndex = 0; dwIndex < dwSubkeyCount; dwIndex++)
} // (ulLevel != 0)
Clean0:
//
// Free any allocated buffers
//
if (pszSubkeyName != NULL) {
MyFree(pszSubkeyName);
}
if (pszValueName != NULL) {
MyFree(pszValueName);
}
if (pValueData != NULL) {
MyFree(pValueData);
}
if (result != ERROR_SUCCESS) {
SetLastError(result);
}
return (result == ERROR_SUCCESS);
} // RestoreSpecialRegistryData
BOOL
MyReplaceKey(
IN HKEY hSourceKey,
IN HKEY hTargetKey
)
/*++
Routine Description:
This routine replaces the target registry key with the source registry key.
This is done by performing a RegSaveKey on the source registry key to a
temporary file, and restoring that file to the target registry key. All
data contained below the target registry key is lost. The source registry
key is not modified by this routine.
Arguments:
hSourceKey - Handle to the key that is the source of the restore operation.
All values and subkeys of the source key will be restored on
top of the target key.
hTargetKey - Handle to the key that is the target of the restore operation.
All values and subkeys of the source key will be restored on
top of this key, and all existing values and data underneath
this key will be lost.
Return Value:
TRUE if successful, FALSE otherwise. Upon failure, additional information
can be retrieved by calling GetLastError().
Notes:
Since this routine uses the RegSaveKey and RegRestoreKey registry APIs, it
is expected that the calling process have both the SE_BACKUP_NAME and
SE_RESTORE_NAME privileges.
--*/
{
LONG result = ERROR_SUCCESS;
TCHAR szTempFilePath[MAX_PATH];
TCHAR szTempFileName[MAX_PATH];
DWORD dwTemp;
//
// Use the temporary directory to store the saved registry key.
//
dwTemp = GetTempPath(MAX_PATH, szTempFilePath);
if ((dwTemp == 0) || (dwTemp > MAX_PATH)) {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: GetTempPath failed, current directory will be specified.\n")));
// current path specified with trailing '\', as GetTempPath would have.
lstrcpy(szTempFileName, TEXT(".\\"));
}
//
// Assign the saved registry key a temporary, unique filename.
//
if (!GetTempFileName(szTempFilePath,
TEXT("PNP"),
0, // make sure it's unique
szTempFileName)) {
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: GetTempFileName failed with error == 0x%08lx, using hardcoded temp file name!\n"),
GetLastError()));
lstrcpy(szTempFileName, szTempFilePath);
lstrcat(szTempFileName, TEXT("~pnpreg.tmp"));
}
DBGTRACE( DBGF_INFO,
(TEXT("PNPSIF: Using temporary filename: %ws\n"),
szTempFileName));
//
// A side effect of requesting a "unique" filename from GetTempFileName is
// that it automatically creates the file. Unfortunately, RegSaveKey will
// fail if the specified file already exists, so delete the file now.
//
if(pSifUtilFileExists(szTempFileName,NULL)) {
DBGTRACE( DBGF_INFO,
(TEXT("PNPSIF: Temporary file %s exists, deleting.\n"),
szTempFileName));
SetFileAttributes(szTempFileName, FILE_ATTRIBUTE_NORMAL);
DeleteFile(szTempFileName);
}
//
// Save the source key to a file using the temporary file name.
// (calling process must have the SE_BACKUP_NAME privilege)
//
result = RegSaveKey(hSourceKey,
szTempFileName,
NULL);
if (result == ERROR_SUCCESS) {
//
// Restore the file to the target key.
// (calling process must have the SE_RESTORE_NAME privilege)
//
result = RegRestoreKey(hTargetKey,
szTempFileName,
REG_FORCE_RESTORE);
if (result != ERROR_SUCCESS) {
//
// Failed to restore the file to the target key!
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegRestoreKey from %ws failed with error == 0x%08lx\n"),
szTempFileName, result));
} else {
DBGTRACE( DBGF_INFO,
(TEXT("PNPSIF: Key replacement successful.\n")));
}
//
// Delete the temporary file we created, now that we're done with it.
//
DBGTRACE( DBGF_INFO,
(TEXT("PNPSIF: Deleting temporary file %s.\n"),
szTempFileName));
ASSERT(pSifUtilFileExists(szTempFileName,NULL));
SetFileAttributes(szTempFileName, FILE_ATTRIBUTE_NORMAL);
DeleteFile(szTempFileName);
} else {
//
// Failed to save the source key.
//
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: RegSaveKey to %ws failed with error == 0x%08lx\n"),
szTempFileName, result));
}
if (result != ERROR_SUCCESS) {
SetLastError(result);
}
return (result == ERROR_SUCCESS);
} // MyReplaceKey
BOOL
IsString4DigitOrdinal(
IN LPTSTR pszSubkeyName
)
/*++
Routine Description:
This routine checks if a subkey name has the form of a 4-digit decimal
ordinal (e.g. "0000", "0001", ... , "9999"), that are typically given to
auto-generated root-enumerated device instance ids - i.e. TEXT("%04u").
Arguments:
pszSubkeyName - Subkey name to check.
Return Value:
TRUE if the string has the form of a 4-digit ordinal string, FALSE
otherwise.
--*/
{
LPTSTR p;
ULONG ulTotalLength = 0;
if ((!ARGUMENT_PRESENT(pszSubkeyName)) ||
(pszSubkeyName[0] == TEXT('\0'))) {
return FALSE;
}
for (p = pszSubkeyName; *p; p++) {
//
// Count the caharcters in the string, and make sure its not longer than
// 4 characters.
//
ulTotalLength++;
if (ulTotalLength > 4) {
return FALSE;
}
//
// Check if the character is non-numeric, non-decimal.
//
if ((*p < TEXT('0')) || (*p > TEXT('9'))) {
return FALSE;
}
}
if (ulTotalLength != 4) {
return FALSE;
}
return TRUE;
} // IsString4DigitOrdinal
BOOL
IsDeviceConfigured(
IN HKEY hInstanceKey
)
/*++
Routine Description:
This routine determines whether the device instance specified by the
supplied registry key is configured or not. If a device has configflags, and
CONFIGFLAG_REINSTALL or CONFIGFLAG_FAILEDINSTALL are not set then the
device is considered configured.
Arguments:
hInstanceKey - Handle to a device instance registry key.
Return Value:
TRUE if successful, FALSE otherwise. Upon failure, additional information
can be retrieved by calling GetLastError().
--*/
{
BOOL bDeviceConfigured = FALSE;
DWORD dwSize, dwType, dwConfigFlags;
if ((hInstanceKey == NULL) ||
(hInstanceKey == INVALID_HANDLE_VALUE)) {
return FALSE;
}
dwSize = sizeof(dwConfigFlags);
if ((RegQueryValueEx(hInstanceKey,
REGSTR_VAL_CONFIGFLAGS,
0,
&dwType,
(LPBYTE)&dwConfigFlags,
&dwSize) == ERROR_SUCCESS) &&
(dwType == REG_DWORD) &&
!(dwConfigFlags & CONFIGFLAG_REINSTALL) &&
!(dwConfigFlags & CONFIGFLAG_FAILEDINSTALL)) {
bDeviceConfigured = TRUE;
}
return bDeviceConfigured;
} // IsDeviceConfigured
BOOL
ReplaceClassKeyForInstance(
IN HKEY hSourceInstanceKey,
IN HKEY hSourceRootClassKey,
IN HKEY hTargetRootClassKey
)
/*++
Routine Description:
This routine replaces the class key corresponding to the specified device
instance key (as specified by the "Driver" value in the instance key) in the
target hive with the class key from the source.
Arguments:
hSourceInstanceKey - Handle to a device instance registry key in the source
hive.
hSourceRootClassKey - Handle to the root of the Class branch in the source
hive - the same hive as the specified instance key.
hTargetRootClassKey - Handle to the root of the Class branch in the target
hive.
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
LONG result = ERROR_SUCCESS;
TCHAR szDriverKeyName[MAX_GUID_STRING_LEN + 5]; // "{ClassGUID}\XXXX"
DWORD dwSize, dwType, dwDisposition;
HKEY hSourceClassSubkey = NULL, hTargetClassSubkey = NULL;
if ((hSourceInstanceKey == NULL) ||
(hSourceInstanceKey == INVALID_HANDLE_VALUE) ||
(hSourceRootClassKey == NULL) ||
(hSourceRootClassKey == INVALID_HANDLE_VALUE) ||
(hTargetRootClassKey == NULL) ||
(hTargetRootClassKey == INVALID_HANDLE_VALUE)) {
return FALSE;
}
//
// Read the REGSTR_VAL_DRIVER REG_SZ "Driver" value from the instance key.
//
szDriverKeyName[0] = TEXT('\0');
dwSize = sizeof(szDriverKeyName);
result = RegQueryValueEx(hSourceInstanceKey,
REGSTR_VAL_DRIVER,
0,
&dwType,
(LPBYTE)szDriverKeyName,
&dwSize);
if (result == ERROR_FILE_NOT_FOUND) {
//
// No "Driver" value, so there's no class key to migrate, which is fine.
//
result = ERROR_SUCCESS;
goto Clean0;
} else if ((result != ERROR_SUCCESS) ||
(dwType != REG_SZ)) {
//
// Any other error is a failure to read the value.
//
goto Clean0;
}
//
// Open the "Driver" key in the source Class branch.
//
result = RegOpenKeyEx(hSourceRootClassKey,
szDriverKeyName,
0,
KEY_READ,
&hSourceClassSubkey);
if (result != ERROR_SUCCESS) {
//
// The instance key had a "Driver" value, so a corresponding key
// *should* exist. If we couldn't open it, it's from some failure
// besides that.
//
return FALSE;
}
//
// Open / create the corresponding key in the target Class branch.
//
result = RegCreateKeyEx(hTargetRootClassKey,
szDriverKeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hTargetClassSubkey,
&dwDisposition);
if (result != ERROR_SUCCESS) {
goto Clean0;
}
//
// Replace the target class subkey with the source.
//
if (!MyReplaceKey(hSourceClassSubkey, hTargetClassSubkey)) {
result = GetLastError();
DBGTRACE( DBGF_ERRORS,
(TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
result));
}
Clean0:
if (hTargetClassSubkey) {
RegCloseKey(hTargetClassSubkey);
}
if (hSourceClassSubkey) {
RegCloseKey(hSourceClassSubkey);
}
if (result != ERROR_SUCCESS) {
SetLastError(result);
}
return (result == ERROR_SUCCESS);
} // ReplaceClassKeyForInstance