windows-nt/Source/XPSP1/NT/base/ntsetup/textmode/kernel/spupgcfg.c
2020-09-26 16:20:57 +08:00

3685 lines
130 KiB
C
Raw Permalink 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:
Spupgcfg.c
Abstract:
Configuration routines for the upgrade case
Author:
Sunil Pai (sunilp) 18-Nov-1993
Revision History:
--*/
#include "spprecmp.h"
#include <initguid.h>
#include <devguid.h>
#pragma hdrstop
NTSTATUS
SppResetLastKnownGood(
IN HANDLE hKeySystem
);
BOOLEAN
SppEnsureHardwareProfileIsPresent(
IN HANDLE hKeyCCSet
);
VOID
SppSetGuimodeUpgradePath(
IN HANDLE hKeySoftwareHive,
IN HANDLE hKeyControlSet
);
NTSTATUS
SppMigratePrinterKeys(
IN HANDLE hControlSet,
IN HANDLE hDestSoftwareHive
);
VOID
SppClearMigratedInstanceValues(
IN HANDLE hKeyCCSet
);
VOID
SppClearMigratedInstanceValuesCallback(
IN HANDLE SetupInstanceKeyHandle,
IN HANDLE UpgradeInstanceKeyHandle OPTIONAL,
IN BOOLEAN RootEnumerated,
IN OUT PVOID Context
);
//
// Callback routine for SppMigrateDeviceParentId
//
typedef BOOL (*PSPP_DEVICE_MIGRATION_CALLBACK_ROUTINE) (
IN HANDLE InstanceKeyHandle,
IN HANDLE DriverKeyHandle
);
VOID
SppMigrateDeviceParentId(
IN HANDLE hKeyCCSet,
IN PWSTR DeviceId,
IN PSPP_DEVICE_MIGRATION_CALLBACK_ROUTINE DeviceMigrationCallbackRoutine
);
VOID
SppMigrateDeviceParentIdCallback(
IN HANDLE SetupInstanceKeyHandle,
IN HANDLE UpgradeInstanceKeyHandle OPTIONAL,
IN BOOLEAN RootEnumerated,
IN OUT PVOID Context
);
BOOL
SppParallelClassCallback(
IN HANDLE InstanceKeyHandle,
IN HANDLE DriverKeyHandle
);
typedef struct _GENERIC_BUFFER_CONTEXT {
PUCHAR Buffer;
ULONG BufferSize;
} GENERIC_BUFFER_CONTEXT, *PGENERIC_BUFFER_CONTEXT;
typedef struct _DEVICE_MIGRATION_CONTEXT {
PUCHAR Buffer;
ULONG BufferSize;
ULONG UniqueParentID;
PWSTR ParentIdPrefix;
HANDLE hKeyCCSet;
PSPP_DEVICE_MIGRATION_CALLBACK_ROUTINE DeviceMigrationCallbackRoutine;
} DEVICE_MIGRATION_CONTEXT, *PDEVICE_MIGRATION_CONTEXT;
//
// Device classe(s) for root device(s) that need to be deleted on upgrade
//
RootDevnodeSectionNamesType UpgRootDeviceClassesToDelete[] =
{
{ L"RootDeviceClassesToDelete", RootDevnodeSectionNamesType_ALL, 0x0000, 0xffff },
{ L"RootDeviceClassesToDelete.NT4", RootDevnodeSectionNamesType_NTUPG, 0x0000, 0x04ff },
{ NULL, 0, 0, 0 }
};
NTSTATUS
SpUpgradeNTRegistry(
IN PVOID SifHandle,
IN HANDLE *HiveRootKeys,
IN LPCWSTR SetupSourceDevicePath,
IN LPCWSTR DirectoryOnSourceDevice,
IN HANDLE hKeyCCSet
)
/*++
Routine Description:
This routine does all the NT registry modifications needed on an upgrade.
This includes the following:
- Disabling network services
- Running the addreg/delreg sections specified in txtsetup.sif
- Deleting various root-enumerated devnode keys specified in txtsetup.sif
Arguments:
SifHandle - supplies handle to txtsetup.sif.
HiveRootKeys - supplies array of handles of root keys in the hives
of the system being upgraded.
hKeyCCSet: Handle to the root of the control set in the system
being upgraded.
Return Value:
Status is returned.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
BOOLEAN b;
//
// Disable the network stuff
//
Status = SpDisableNetwork(SifHandle,HiveRootKeys[SetupHiveSoftware],hKeyCCSet);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: warning: SpDisableNetworkFailed (%lx)\n",Status));
}
//
// Migrate the parallel class device parent id value to all parallel
// devices.
//
SppMigrateDeviceParentId(hKeyCCSet,
L"Root\\PARALLELCLASS\\0000",
SppParallelClassCallback);
//
// Delete legacy root-enumerated devnode keys out of Enum tree.
//
SpDeleteRootDevnodeKeys(SifHandle,
hKeyCCSet,
L"RootDevicesToDelete",
UpgRootDeviceClassesToDelete);
//
// Clean the "Migrated" values from device instance keys in the setup
// and upgrade registries, as appropriate.
//
SppClearMigratedInstanceValues(hKeyCCSet);
//
// If the user doesn't have any hardware profiles defined (i.e., we're upgrading
// from a pre-NT4 system), then create them one.
//
b = SppEnsureHardwareProfileIsPresent(hKeyCCSet);
if(!b) {
return STATUS_UNSUCCESSFUL;
}
Status = SppMigratePrinterKeys( hKeyCCSet,
HiveRootKeys[SetupHiveSoftware] );
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: warning: SppMigratePrinterKeys() failed. Status = %lx \n",Status));
}
//
// Perform the general and wide-ranging hive upgrade.
//
b = SpHivesFromInfs(
SifHandle,
L"HiveInfs.Upgrade",
SetupSourceDevicePath,
DirectoryOnSourceDevice,
HiveRootKeys[SetupHiveSystem],
HiveRootKeys[SetupHiveSoftware],
HiveRootKeys[SetupHiveDefault],
HiveRootKeys[SetupHiveUserdiff]
);
if(!b) {
return(STATUS_UNSUCCESSFUL);
}
SppSetGuimodeUpgradePath(HiveRootKeys[SetupHiveSystem],hKeyCCSet);
//
// Set 'LastKnownGood' the same as 'Current'
// Ignore the error in case of failure, since this will
// not affect the installation process
//
Status = SppResetLastKnownGood(HiveRootKeys[SetupHiveSystem]);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: warning: SppResetLastKnownGood() failed. Status = (%lx)\n",Status));
return(Status);
}
return(STATUS_SUCCESS);
}
NTSTATUS
SppDeleteKeyRecursive(
HANDLE hKeyRoot,
PWSTR Key,
BOOLEAN ThisKeyToo
)
/*++
Routine Description:
Routine to recursively delete all subkeys under the given
key, including the key given.
Arguments:
hKeyRoot: Handle to root relative to which the key to be deleted is
specified.
Key: Root relative path of the key which is to be recursively deleted.
ThisKeyToo: Whether after deletion of all subkeys, this key itself is to
be deleted.
Return Value:
Status is returned.
--*/
{
ULONG ResultLength;
PKEY_BASIC_INFORMATION KeyInfo;
NTSTATUS Status;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Obja;
PWSTR SubkeyName;
HANDLE hKey;
//
// Initialize
//
KeyInfo = (PKEY_BASIC_INFORMATION)TemporaryBuffer;
//
// Open the key
//
INIT_OBJA(&Obja,&UnicodeString,Key);
Obja.RootDirectory = hKeyRoot;
Status = ZwOpenKey(&hKey,KEY_ALL_ACCESS,&Obja);
if( !NT_SUCCESS(Status) ) {
return(Status);
}
//
// Enumerate all subkeys of the current key. if any exist they should
// be deleted first. since deleting the subkey affects the subkey
// index, we always enumerate on subkeyindex 0
//
while(1) {
Status = ZwEnumerateKey(
hKey,
0,
KeyBasicInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&ResultLength
);
if(!NT_SUCCESS(Status)) {
break;
}
//
// Zero-terminate the subkey name just in case.
//
KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Make a duplicate of the subkey name because the name is
// in TemporaryBuffer, which might get clobbered by recursive
// calls to this routine.
//
SubkeyName = SpDupStringW(KeyInfo->Name);
Status = SppDeleteKeyRecursive( hKey, SubkeyName, TRUE);
SpMemFree(SubkeyName);
if(!NT_SUCCESS(Status)) {
break;
}
}
ZwClose(hKey);
//
// Check the status, if the status is anything other than
// STATUS_NO_MORE_ENTRIES we failed in deleting some subkey,
// so we cannot delete this key too
//
if( Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
if(!NT_SUCCESS(Status)) {
return(Status);
}
//
// else delete the current key if asked to do so
//
if( ThisKeyToo ) {
Status = SpDeleteKey(hKeyRoot, Key);
}
return(Status);
}
NTSTATUS
SppCopyKeyRecursive(
HANDLE hKeyRootSrc,
HANDLE hKeyRootDst,
PWSTR SrcKeyPath, OPTIONAL
PWSTR DstKeyPath, OPTIONAL
BOOLEAN CopyAlways,
BOOLEAN ApplyACLsAlways
)
/*++
Routine Description:
This routine recursively copies a src key to a destination key. Any new
keys that are created will receive the same security that is present on
the source key.
Arguments:
hKeyRootSrc: Handle to root src key
hKeyRootDst: Handle to root dst key
SrcKeyPath: src root key relative path to the subkey which needs to be
recursively copied. if this is null hKeyRootSrc is the key
from which the recursive copy is to be done.
DstKeyPath: dst root key relative path to the subkey which needs to be
recursively copied. if this is null hKeyRootDst is the key
from which the recursive copy is to be done.
CopyAlways: If FALSE, this routine doesn't copy values which are already
there on the target tree.
Return Value:
Status is returned.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjaSrc, ObjaDst;
UNICODE_STRING UnicodeStringSrc, UnicodeStringDst, UnicodeStringValue;
HANDLE hKeySrc=NULL,hKeyDst=NULL;
ULONG ResultLength, Index;
PWSTR SubkeyName,ValueName;
PSECURITY_DESCRIPTOR Security = NULL;
PKEY_BASIC_INFORMATION KeyInfo;
PKEY_VALUE_FULL_INFORMATION ValueInfo;
//
// Get a handle to the source key
//
if(SrcKeyPath == NULL) {
hKeySrc = hKeyRootSrc;
}
else {
//
// Open the Src key
//
INIT_OBJA(&ObjaSrc,&UnicodeStringSrc,SrcKeyPath);
ObjaSrc.RootDirectory = hKeyRootSrc;
Status = ZwOpenKey(&hKeySrc,KEY_READ,&ObjaSrc);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to open key %ws in the source hive (%lx)\n",SrcKeyPath,Status));
return(Status);
}
}
//
// Get a handle to the destination key
//
if(DstKeyPath == NULL) {
hKeyDst = hKeyRootDst;
} else {
//
// First, get the security descriptor from the source key so we can create
// the destination key with the correct ACL.
//
Status = ZwQuerySecurityObject(hKeySrc,
DACL_SECURITY_INFORMATION,
NULL,
0,
&ResultLength
);
if(Status==STATUS_BUFFER_TOO_SMALL) {
Security=SpMemAlloc(ResultLength);
Status = ZwQuerySecurityObject(hKeySrc,
DACL_SECURITY_INFORMATION,
Security,
ResultLength,
&ResultLength);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to query security for key %ws in the source hive (%lx)\n",
SrcKeyPath,
Status)
);
SpMemFree(Security);
Security=NULL;
}
} else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to query security size for key %ws in the source hive (%lx)\n",
SrcKeyPath,
Status)
);
Security=NULL;
}
//
// Attempt to open (not create) the destination key first. If we can't
// open the key because it doesn't exist, then we'll create it and apply
// the security present on the source key.
//
INIT_OBJA(&ObjaDst,&UnicodeStringDst,DstKeyPath);
ObjaDst.RootDirectory = hKeyRootDst;
Status = ZwOpenKey(&hKeyDst,KEY_ALL_ACCESS,&ObjaDst);
if(!NT_SUCCESS(Status)) {
//
// Assume that failure was because the key didn't exist. Now try creating
// the key.
ObjaDst.SecurityDescriptor = Security;
Status = ZwCreateKey(
&hKeyDst,
KEY_ALL_ACCESS,
&ObjaDst,
0,
NULL,
REG_OPTION_NON_VOLATILE,
NULL
);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to create key %ws(%lx)\n",DstKeyPath, Status));
if(SrcKeyPath != NULL) {
ZwClose(hKeySrc);
}
if(Security) {
SpMemFree(Security);
}
return(Status);
}
} else if (ApplyACLsAlways) {
Status = ZwSetSecurityObject(
hKeyDst,
DACL_SECURITY_INFORMATION,
Security );
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to copy ACL to existing key %ws(%lx)\n",DstKeyPath, Status));
}
}
//
// Free security descriptor buffer before checking return status from ZwCreateKey.
//
if(Security) {
SpMemFree(Security);
}
}
//
// Enumerate all keys in the source key and recursively create
// all the subkeys
//
KeyInfo = (PKEY_BASIC_INFORMATION)TemporaryBuffer;
for( Index=0;;Index++ ) {
Status = ZwEnumerateKey(
hKeySrc,
Index,
KeyBasicInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&ResultLength
);
if(!NT_SUCCESS(Status)) {
if(Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
else {
if(SrcKeyPath!=NULL) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to enumerate subkeys in key %ws(%lx)\n",SrcKeyPath, Status));
}
else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to enumerate subkeys in root key(%lx)\n", Status));
}
}
break;
}
//
// Zero-terminate the subkey name just in case.
//
KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Make a duplicate of the subkey name because the name is
// in TemporaryBuffer, which might get clobbered by recursive
// calls to this routine.
//
SubkeyName = SpDupStringW(KeyInfo->Name);
Status = SppCopyKeyRecursive(
hKeySrc,
hKeyDst,
SubkeyName,
SubkeyName,
CopyAlways,
ApplyACLsAlways
);
SpMemFree(SubkeyName);
}
//
// Process any errors if found
//
if(!NT_SUCCESS(Status)) {
if(SrcKeyPath != NULL) {
ZwClose(hKeySrc);
}
if(DstKeyPath != NULL) {
ZwClose(hKeyDst);
}
return(Status);
}
//
// Enumerate all values in the source key and create all the values
// in the destination key
//
ValueInfo = (PKEY_VALUE_FULL_INFORMATION)TemporaryBuffer;
for( Index=0;;Index++ ) {
Status = ZwEnumerateValueKey(
hKeySrc,
Index,
KeyValueFullInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&ResultLength
);
if(!NT_SUCCESS(Status)) {
if(Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
else {
if(SrcKeyPath!=NULL) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to enumerate values in key %ws(%lx)\n",SrcKeyPath, Status));
}
else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to enumerate values in root key(%lx)\n", Status));
}
}
break;
}
//
// Process the value found and create the value in the destination
// key
//
ValueName = (PWSTR)SpMemAlloc(ValueInfo->NameLength + sizeof(WCHAR));
ASSERT(ValueName);
wcsncpy(ValueName, ValueInfo->Name, (ValueInfo->NameLength)/sizeof(WCHAR));
ValueName[(ValueInfo->NameLength)/sizeof(WCHAR)] = 0;
RtlInitUnicodeString(&UnicodeStringValue,ValueName);
//
// If it is a conditional copy, we need to check if the value already
// exists in the destination, in which case we shouldn't set the value
//
if( !CopyAlways ) {
ULONG Length;
PKEY_VALUE_BASIC_INFORMATION DestValueBasicInfo;
Length = sizeof(KEY_VALUE_BASIC_INFORMATION) + ValueInfo->NameLength + sizeof(WCHAR) + MAX_PATH;
DestValueBasicInfo = (PKEY_VALUE_BASIC_INFORMATION)SpMemAlloc(Length);
ASSERT(DestValueBasicInfo);
Status = ZwQueryValueKey(
hKeyDst,
&UnicodeStringValue,
KeyValueBasicInformation,
DestValueBasicInfo,
Length,
&ResultLength
);
SpMemFree((PVOID)DestValueBasicInfo);
if(NT_SUCCESS(Status)) {
//
// Value exists, we shouldn't change the value
//
SpMemFree(ValueName);
continue;
}
if( Status!=STATUS_OBJECT_NAME_NOT_FOUND && Status!=STATUS_OBJECT_PATH_NOT_FOUND) {
if(DstKeyPath) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to query value %ws in key %ws(%lx)\n",ValueName,DstKeyPath, Status));
}
else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to query value %ws in root key(%lx)\n",ValueName, Status));
}
SpMemFree(ValueName);
break;
}
}
Status = ZwSetValueKey(
hKeyDst,
&UnicodeStringValue,
ValueInfo->TitleIndex,
ValueInfo->Type,
(PBYTE)ValueInfo + ValueInfo->DataOffset,
ValueInfo->DataLength
);
if(!NT_SUCCESS(Status)) {
if(DstKeyPath) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to set value %ws in key %ws(%lx)\n",ValueName,DstKeyPath, Status));
}
else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to set value %ws(%lx)\n",ValueName, Status));
}
SpMemFree(ValueName);
break;
}
SpMemFree(ValueName);
}
//
// cleanup
//
if(SrcKeyPath != NULL) {
ZwClose(hKeySrc);
}
if(DstKeyPath != NULL) {
ZwClose(hKeyDst);
}
return(Status);
}
NTSTATUS
SppResetLastKnownGood(
IN HANDLE hKeySystem
)
{
NTSTATUS Status;
ULONG ResultLength;
DWORD Value;
//
// Make the appropriate change
//
Status = SpGetValueKey(
hKeySystem,
L"Select",
L"Current",
sizeof(TemporaryBuffer),
(PCHAR)TemporaryBuffer,
&ResultLength
);
//
// TemporaryBuffer is 32kb long, and it should be big enough
// for the data.
//
ASSERT( Status != STATUS_BUFFER_OVERFLOW );
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to read value from registry. KeyName = Select, ValueName = Current, Status = (%lx)\n",Status));
return( Status );
}
Value = *(DWORD *)(((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->Data);
Status = SpOpenSetValueAndClose( hKeySystem,
L"Select",
L"LastKnownGood",
REG_DWORD,
&Value,
sizeof( ULONG ) );
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to write value to registry. KeyName = Select, ValueName = LastKnownGood, Status = (%lx)\n",Status));
}
//
// We need also to reset the value 'Failed'. Otherwise, the Service Control
// Manager will display a popup indicating the LastKnownGood CCSet was
// used.
//
Value = 0;
Status = SpOpenSetValueAndClose( hKeySystem,
L"Select",
L"Failed",
REG_DWORD,
&Value,
sizeof( ULONG ) );
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to write value to registry. KeyName = Select, ValueName = Failed, Status = (%lx)\n",Status));
}
return( Status );
}
VOID
SpDeleteRootDevnodeKeys(
IN PVOID SifHandle,
IN HANDLE hKeyCCSet,
IN PWSTR DevicesToDelete,
IN RootDevnodeSectionNamesType *DeviceClassesToDelete
)
/*++
Routine Description:
This routine deletes some root-enumerated devnode registry keys
based on criteria specified in txtsetup.sif. The following sections
are processed:
[RootDevicesToDelete] - this section lists device IDs under
HKLM\System\CurrentControlSet\Enum\Root that
should be deleted (including their subkeys).
[RootDeviceClassesToDelete] - this section lists device class GUIDs
whose root-enumerated members are to be
deleted.
For each device instance key to be deleted, we also delete the corresponding
Driver key under HKLM\System\CurrentControlSet\Control\Class, if specified.
We also do two additional operations to clean-up in certain cases where we
may encounter junk deposited in the registry from NT4:
1. Delete any root-enumerated devnode keys that have a nonzero (or
ill-formed) "Phantom" value, indicating that they're a "private
phantom".
2. Delete any Control subkeys we may find--since these are supposed to
always be volatile, we _should_ never see these, but we've seen
cases where OEM preinstalls 'seed' the hives with device instance
keys including this subkey, and the results are disastrous (i.e.,
we bugcheck when we encounter a PDO address we'd squirreled away in
the key on a previous boot thinking it was nonvolatile, hence would
disappear upon reboot).
Arguments:
SifHandle: Supplies handle to txtsetup.sif.
hKeyCCSet: Handle to the root of the control set in the system
being upgraded.
DevicesToDelete: Section name containing the root device names that need
to be deleted.
DeviceClassesToDelete: Specifies the classes of root devices that need to
be deleted.
Return Value:
none.
--*/
{
HANDLE hRootKey, hDeviceKey, hInstanceKey, hClassKey;
HANDLE hSetupRootKey, hSetupDeviceKey, hSetupInstanceKey, hSetupClassKey;
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING UnicodeString, guidString;
ULONG LineIndex, DeviceKeyIndex, ResultLength, InstanceKeyIndex;
PKEY_BASIC_INFORMATION DeviceKeyInfo, InstanceKeyInfo;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
PUCHAR p, q;
BOOLEAN InstanceKeysEnumerated;
PWSTR DeviceId, ClassGuidToDelete;
PUCHAR MyScratchBuffer;
ULONG MyScratchBufferSize, drvInst;
BOOLEAN DeleteInstanceKey;
int SectionIndex;
DWORD OsFlags = 0;
DWORD OsVersion = 0;
DWORD MangledVersion;
PWSTR Value;
//
// Determine OSFlags & OSVersion for going through various sections
//
Value = SpGetSectionKeyIndex(WinntSifHandle,
SIF_DATA, WINNT_D_NTUPGRADE_W, 0);
if(Value && _wcsicmp(Value, WINNT_A_YES_W)==0) {
//
// It's an NT upgrade
//
OsFlags |= RootDevnodeSectionNamesType_NTUPG;
}
if(!OsFlags) {
Value = SpGetSectionKeyIndex(WinntSifHandle,
SIF_DATA, WINNT_D_WIN95UPGRADE_W, 0);
if (Value && _wcsicmp(Value, WINNT_A_YES_W)==0) {
//
// It's a Win9x upgrade
//
OsFlags |= RootDevnodeSectionNamesType_W9xUPG;
}
if(!OsFlags) {
//
// in all other cases assume clean
//
OsFlags = RootDevnodeSectionNamesType_CLEAN;
}
}
Value = SpGetSectionKeyIndex(WinntSifHandle,
SIF_DATA, WINNT_D_WIN32_VER_W, 0);
if(Value) {
//
// version is bbbbllhh - build/low/high
//
MangledVersion = (DWORD)SpStringToLong( Value, NULL, 16 );
//
// NTRAID#453953 2001/08/09-JamieHun OsVersion checking is wrong
// this should be RtlUshortByteSwap((USHORT)MangledVersion) & 0xffff;
// Win2k -> 0005 WinXP -> 0105
// so we always run ".NT4" below
//
OsVersion = MAKEWORD(LOBYTE(LOWORD(MangledVersion)),HIBYTE(LOWORD(MangledVersion)));
} else {
OsVersion = 0;
}
//
// open CCS\Enum\Root in the registry being upgraded.
//
INIT_OBJA(&Obja, &UnicodeString, L"Enum\\Root");
Obja.RootDirectory = hKeyCCSet;
Status = ZwOpenKey(&hRootKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to open upgrade Enum\\Root for devnode deletion. Status = %lx \n",
Status));
return;
}
//
// Open CCS\Enum\Root in the current setup registry.
//
INIT_OBJA(&Obja, &UnicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\Root");
Obja.RootDirectory = NULL;
Status = ZwOpenKey(&hSetupRootKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to open setup Enum\\Root for devnode deletion. Status = %lx \n",
Status));
ZwClose(hRootKey);
return;
}
//
// Next, open CCS\Control\Class in the registry being upgraded.
//
INIT_OBJA(&Obja, &UnicodeString, L"Control\\Class");
Obja.RootDirectory = hKeyCCSet;
Status = ZwOpenKey(&hClassKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to open upgrade Control\\Class for devnode deletion. Status = %lx \n",
Status));
ZwClose(hSetupRootKey);
ZwClose(hRootKey);
return;
}
//
// Open CCS\Control\Class in the current setup registry.
//
INIT_OBJA(&Obja, &UnicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class");
Obja.RootDirectory = NULL;
Status = ZwOpenKey(&hSetupClassKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to open setup Control\\Class for devnode deletion. Status = %lx \n",
Status));
ZwClose(hClassKey);
ZwClose(hSetupRootKey);
ZwClose(hRootKey);
return;
}
//
// Allocate some scratch space to work with. The most we'll need is enough for 2
// KEY_BASIC_INFORMATION structures, plus the maximum length of a device instance ID,
// plus a KEY_VALUE_PARTIAL_INFORMATION structure, plus the length of a driver instance
// key path [stringified GUID + '\' + 4 digit ordinal + term NULL], plus 2 large integer
// structures for alignment.
//
MyScratchBufferSize = (2*sizeof(KEY_BASIC_INFORMATION)) + (200*sizeof(WCHAR)) +
sizeof(KEY_VALUE_PARTIAL_INFORMATION) + ((GUID_STRING_LEN+5)*sizeof(WCHAR) +
2*sizeof(LARGE_INTEGER));
MyScratchBuffer = SpMemAlloc(MyScratchBufferSize);
if(!MyScratchBuffer) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Can't allocate memory for deletion of classes of root-enumerated devnodes!\n"));
ZwClose(hSetupClassKey);
ZwClose(hClassKey);
ZwClose(hSetupRootKey);
ZwClose(hRootKey);
return;
}
//
// PART 1: Process [RootDevicesToDelete]
//
//
// Now, traverse the entries under the [RootDevicesToDelete] section, and
// delete each one.
//
for(LineIndex = 0;
DeviceId = SpGetSectionLineIndex(SifHandle, DevicesToDelete, LineIndex, 0);
LineIndex++) {
//
// Open up the device key so we can enumerate the instances.
//
INIT_OBJA(&Obja, &UnicodeString, DeviceId);
Obja.RootDirectory = hRootKey;
Status = ZwOpenKey(&hDeviceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open Enum\\Root\\%ws during devnode deletion. Status = %lx \n",
DeviceId,
Status));
//
// Skip this key and continue.
//
continue;
}
//
// Attempt to open the device key in the setup registry.
//
Obja.RootDirectory = hSetupRootKey;
Status = ZwOpenKey(&hSetupDeviceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open Enum\\Root\\%ws during devnode deletion. Status = %lx \n",
DeviceId,
Status));
//
// It's ok if we don't have this device key in the setup registry.
//
hSetupDeviceKey = NULL;
}
//
// Now enumerate the instance subkeys under this device key.
//
p = ALIGN_UP_POINTER(((PUCHAR)MyScratchBuffer), sizeof(LARGE_INTEGER));
InstanceKeyInfo = (PKEY_BASIC_INFORMATION)p;
InstanceKeyIndex = 0;
while(TRUE) {
Status = ZwEnumerateKey(hDeviceKey,
InstanceKeyIndex,
KeyBasicInformation,
p,
(ULONG)MyScratchBufferSize,
&ResultLength
);
if(!NT_SUCCESS(Status)) {
break;
}
//
// Zero-terminate the instance key name, just in case.
//
InstanceKeyInfo->Name[InstanceKeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Now, open up the instance key so we can check its Driver value.
//
INIT_OBJA(&Obja, &UnicodeString, InstanceKeyInfo->Name);
Obja.RootDirectory = hDeviceKey;
Status = ZwOpenKey(&hInstanceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open Enum\\Root\\%ws\\%ws for potential devnode deletion. Status = %lx \n",
DeviceId,
InstanceKeyInfo->Name,
Status));
//
// Skip this key and continue.
//
InstanceKeyIndex++;
continue;
}
//
// Attempt to open the same instance key in the setup registry.
//
hSetupInstanceKey = NULL;
if (hSetupDeviceKey) {
Obja.RootDirectory = hSetupDeviceKey;
Status = ZwOpenKey(&hSetupInstanceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open setup Enum\\Root\\%ws\\%ws for potential devnode deletion. Status = %lx \n",
DeviceId,
InstanceKeyInfo->Name,
Status));
}
}
//
// Now look for some value entries under this instance key. Don't
// overwrite the instance key name already in MyScratchBuffer,
// since we'll need it later.
//
q = ALIGN_UP_POINTER(((PUCHAR)p + ResultLength), sizeof(LARGE_INTEGER));
if (hSetupInstanceKey) {
//
// Check if the Migrated value still exists on this registry
// key. If so, it was migrated, but wasn't ever used by
// textmode setup and is now specified to be deleted.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, L"Migrated");
Status = ZwQueryValueKey(hSetupInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength);
if (NT_SUCCESS(Status) &&
(KeyValueInfo->Type == REG_DWORD) &&
(*(PULONG)(KeyValueInfo->Data) == 1)) {
DeleteInstanceKey = TRUE;
} else {
DeleteInstanceKey = FALSE;
}
}
//
// First check for the presence of old style "Driver" value.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_DRIVER);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_SZ) {
//
// Delete the Driver key.
//
SppDeleteKeyRecursive(hClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
//
// Also attempt to delete the driver key from the setup
// registry. Note that we don't need to check that it has the
// same Driver value, since we explicitly migrated it to be the
// same value at the start of textmode setup.
//
if (hSetupInstanceKey && DeleteInstanceKey) {
SppDeleteKeyRecursive(hSetupClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
}
} else {
//
// Construct the driver instance as "ClassGuid\nnnn"
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_GUID);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_BINARY) {
Status = RtlStringFromGUID((REFGUID)KeyValueInfo->Data, &guidString);
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status)) {
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_DRVINST);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_DWORD) {
drvInst = *(PULONG)KeyValueInfo->Data;
swprintf((PWCHAR)&KeyValueInfo->Data[0], TEXT("%wZ\\%04u"), &guidString, drvInst);
//
// Delete the Driver key.
//
SppDeleteKeyRecursive(hClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
//
// Also attempt to delete the driver key from the setup
// registry. Note that we don't need to check that it has the
// same Driver value, since we explicitly migrated it to be the
// same value at the start of textmode setup.
//
if (hSetupInstanceKey && DeleteInstanceKey) {
SppDeleteKeyRecursive(hSetupClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
}
}
RtlFreeUnicodeString(&guidString);
}
}
}
//
// Delete the instance key from the setup registry, if we should do so.
//
if (hSetupInstanceKey && DeleteInstanceKey) {
ZwClose(hSetupInstanceKey);
SppDeleteKeyRecursive(hSetupDeviceKey, InstanceKeyInfo->Name, TRUE);
}
//
// Now close the handle, and move on to the next one.
//
ZwClose(hInstanceKey);
InstanceKeyIndex++;
}
//
// Delete the device key, and all instance subkeys.
//
ZwClose(hDeviceKey);
SppDeleteKeyRecursive(hRootKey, DeviceId, TRUE);
//
// If the device has no remaining instances in the setup registry,
// delete the device key.
//
if (hSetupDeviceKey) {
KEY_FULL_INFORMATION keyFullInfo;
Status = ZwQueryKey(hSetupDeviceKey,
KeyFullInformation,
(PVOID)&keyFullInfo,
sizeof(KEY_FULL_INFORMATION),
&ResultLength);
ZwClose(hSetupDeviceKey);
hSetupDeviceKey = NULL;
if ((NT_SUCCESS(Status) || (Status == STATUS_BUFFER_TOO_SMALL)) &&
(keyFullInfo.SubKeys == 0)) {
SppDeleteKeyRecursive(hSetupRootKey, DeviceId, TRUE);
}
}
}
//
// PART 2: Process [RootDeviceClassesToDelete]
//
//
// Now, enumerate all remaining device instances under Enum\Root, looking for
// devices whose class is one of our classes to delete.
//
DeviceKeyInfo = (PKEY_BASIC_INFORMATION)MyScratchBuffer;
DeviceKeyIndex = 0;
while(TRUE && DeviceClassesToDelete) {
Status = ZwEnumerateKey(hRootKey,
DeviceKeyIndex,
KeyBasicInformation,
MyScratchBuffer,
MyScratchBufferSize,
&ResultLength
);
if(!NT_SUCCESS(Status)) {
break;
}
//
// Reset our flag that indicates whether or not we enumerated the instance
// subkeys under this key. We use this later in determining whether the
// device key itself should be deleted.
//
InstanceKeysEnumerated = FALSE;
//
// Zero-terminate the subkey name just in case.
//
DeviceKeyInfo->Name[DeviceKeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Go ahead and bump the used-buffer length by sizeof(WCHAR), to
// accomodate the potential growth caused by adding a terminating NULL.
//
ResultLength += sizeof(WCHAR);
//
// Now, open up the device key so we can enumerate the instances.
//
INIT_OBJA(&Obja, &UnicodeString, DeviceKeyInfo->Name);
Obja.RootDirectory = hRootKey;
Status = ZwOpenKey(&hDeviceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open Enum\\Root\\%ws for potential devnode deletion. Status = %lx \n",
DeviceKeyInfo->Name,
Status));
//
// Skip this key and continue.
//
DeviceKeyIndex++;
continue;
}
//
// Attempt to open the device key in the setup registry.
//
Obja.RootDirectory = hSetupRootKey;
Status = ZwOpenKey(&hSetupDeviceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open setup Enum\\Root\\%ws during devnode deletion. Status = %lx \n",
DeviceKeyInfo->Name,
Status));
//
// It's ok if we don't have this device key in the setup registry.
//
hSetupDeviceKey = NULL;
}
//
// Now enumerate the instance subkeys under this device key. Don't overwrite
// the device ID key name already in MyScratchBuffer, since we'll probably
// be needing it again in the case where all subkeys get deleted.
//
p = ALIGN_UP_POINTER(((PUCHAR)MyScratchBuffer + ResultLength), sizeof(LARGE_INTEGER));
InstanceKeyInfo = (PKEY_BASIC_INFORMATION)p;
InstanceKeyIndex = 0;
InstanceKeysEnumerated = TRUE;
while(TRUE) {
Status = ZwEnumerateKey(hDeviceKey,
InstanceKeyIndex,
KeyBasicInformation,
p,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - p),
&ResultLength
);
if(!NT_SUCCESS(Status)) {
break;
}
//
// Zero-terminate the instance key name, just in case.
//
InstanceKeyInfo->Name[InstanceKeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Go ahead and bump the used-buffer length by sizeof(WCHAR), to
// accomodate the potential growth caused by adding a terminating NULL.
//
ResultLength += sizeof(WCHAR);
//
// Now, open up the instance key so we can check its class.
//
INIT_OBJA(&Obja, &UnicodeString, InstanceKeyInfo->Name);
Obja.RootDirectory = hDeviceKey;
Status = ZwOpenKey(&hInstanceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open Enum\\Root\\%ws\\%ws for potential devnode deletion. Status = %lx \n",
DeviceKeyInfo->Name,
InstanceKeyInfo->Name,
Status));
//
// Skip this key and continue.
//
InstanceKeyIndex++;
continue;
}
//
// Attempt to open the same instance key in the setup registry.
//
hSetupInstanceKey = NULL;
if (hSetupDeviceKey) {
Obja.RootDirectory = hSetupDeviceKey;
Status = ZwOpenKey(&hSetupInstanceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open setup Enum\\Root\\%ws\\%ws for potential devnode deletion. Status = %lx \n",
DeviceId,
InstanceKeyInfo->Name,
Status));
}
}
DeleteInstanceKey = FALSE;
//
// Now look for some value entries under this instance key. Don't
// overwrite the instance key name already in MyScratchBuffer,
// since we'll need it if we discover that the instance should be
// deleted.
//
q = ALIGN_UP_POINTER(((PUCHAR)p + ResultLength), sizeof(LARGE_INTEGER));
//
// If we find a nonzero Phantom value entry, then the devnode
// should be removed.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, L"Phantom");
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if(NT_SUCCESS(Status) &&
((KeyValueInfo->Type != REG_DWORD) ||
*(PULONG)(KeyValueInfo->Data))) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: SpDeleteRootDevnodeKeys: Encountered a left-over phantom in Enum\\Root\\%ws\\%ws. Deleting key. \n",
DeviceKeyInfo->Name,
InstanceKeyInfo->Name));
DeleteInstanceKey = TRUE;
}
if(!DeleteInstanceKey) {
//
// Unless it is a phantom, if we find a nonzero
// FirmwareIdentified value entry, then the devnode should not
// be removed, no matter what class it is.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, L"FirmwareIdentified");
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if(NT_SUCCESS(Status) &&
((KeyValueInfo->Type != REG_DWORD) ||
*(PULONG)(KeyValueInfo->Data))) {
//
// Skip this key and continue;
//
goto CloseInstanceKeyAndContinue;
}
}
if(!DeleteInstanceKey) {
//
// Retrieve the ClassGUID value entry.
//
// First check for the old value.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_CLASSGUID);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if(!NT_SUCCESS(Status)) {
//
// Check the new value.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_GUID);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if(NT_SUCCESS(Status) && KeyValueInfo->Type == REG_BINARY) {
GUID guid;
UNICODE_STRING guidString;
guid = *(GUID *)KeyValueInfo->Data;
Status = RtlStringFromGUID(&guid, &guidString);
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status)) {
KeyValueInfo->Type = REG_SZ;
KeyValueInfo->DataLength = guidString.MaximumLength;
RtlCopyMemory(KeyValueInfo->Data, guidString.Buffer, KeyValueInfo->DataLength);
RtlFreeUnicodeString(&guidString);
} else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: SpDeleteRootDevnodeKeys: Failed to convert GUID to string! \n",
DeviceKeyInfo->Name,
InstanceKeyInfo->Name));
//
// Skip this key and continue;
//
goto CloseInstanceKeyAndContinue;
}
} else {
DeleteInstanceKey = TRUE;
}
}
}
if(DeleteInstanceKey) {
//
// The instance key will be deleted. Check if the instance
// specifies a corresponding Driver key that should also be
// deleted.
//
// First read the old style "Driver" value.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_DRIVER);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_SZ) {
//
// Delete the Driver key.
//
SppDeleteKeyRecursive(hClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
//
// Also attempt to delete the driver key from the setup
// registry. Note that we don't need to check that it has the
// same Driver value, since we explicitly migrated it to be the
// same value at the start of textmode setup.
//
if (hSetupInstanceKey) {
SppDeleteKeyRecursive(hSetupClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
}
} else {
//
// Create the driver instance.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_GUID);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_BINARY) {
Status = RtlStringFromGUID((REFGUID)KeyValueInfo->Data, &guidString);
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status)) {
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_DRVINST);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_DWORD) {
drvInst = *(PULONG)KeyValueInfo->Data;
swprintf((PWCHAR)&KeyValueInfo->Data[0], TEXT("%wZ\\%04u"), &guidString, drvInst);
//
// Delete the Driver key.
//
SppDeleteKeyRecursive(hClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
//
// Also attempt to delete the driver key from the setup
// registry. Note that we don't need to check that it has the
// same Driver value, since we explicitly migrated it to be the
// same value at the start of textmode setup.
//
if (hSetupInstanceKey) {
SppDeleteKeyRecursive(hSetupClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
}
}
RtlFreeUnicodeString(&guidString);
}
}
}
//
// Delete the instance key.
//
ZwClose(hInstanceKey);
SppDeleteKeyRecursive(hDeviceKey, InstanceKeyInfo->Name, TRUE);
//
// Delete the instance key from the setup registry.
//
if (hSetupInstanceKey) {
ZwClose(hSetupInstanceKey);
SppDeleteKeyRecursive(hSetupDeviceKey, InstanceKeyInfo->Name, TRUE);
}
//
// We deleted the instance key, so set the instance enumeration
// index back to zero and continue.
//
InstanceKeyIndex = 0;
continue;
}
//
// This value should be exactly the length of a stringified GUID + terminating NULL.
//
if(KeyValueInfo->DataLength != (GUID_STRING_LEN * sizeof(WCHAR))) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: SpDeleteRootDevnodeKeys: Enum\\Root\\%ws\\%ws has corrupted ClassGUID! \n",
DeviceKeyInfo->Name,
InstanceKeyInfo->Name));
//
// Skip this key and continue;
//
goto CloseInstanceKeyAndContinue;
}
//
// Now loop through the [RootDeviceClassesToDelete] section to see if this class is one
// of the ones whose devices we're supposed to delete.
//
// NTRAID#453953 2001/08/09-JamieHun OsVersion checking is wrong
// as this stands, we will always process all sections
//
for(SectionIndex = 0; DeviceClassesToDelete[SectionIndex].SectionName; SectionIndex++) {
if((!DeviceClassesToDelete[SectionIndex].SectionFlags & OsFlags)
|| (OsVersion < DeviceClassesToDelete[SectionIndex].VerLow)
|| (OsVersion > DeviceClassesToDelete[SectionIndex].VerHigh)) {
//
// not interesting
//
// NTRAID#453953 2001/08/09-JamieHun OsVersion checking is wrong
// we don't get here
//
continue;
}
for(LineIndex = 0;
ClassGuidToDelete = SpGetSectionLineIndex(SifHandle,
DeviceClassesToDelete[SectionIndex].SectionName,
LineIndex,
0);
LineIndex++) {
//
// Compare the two GUID strings.
//
if(!_wcsicmp(ClassGuidToDelete, (PWCHAR)(KeyValueInfo->Data))) {
//
// NTRAID#257655 2001/08/09-JamieHun SMS AppCompat due to #453953
//
if((_wcsicmp(DeviceKeyInfo->Name,L"*SMS_KEYBOARD")==0) ||
(_wcsicmp(DeviceKeyInfo->Name,L"*SMS_MOUSE")==0)) {
//
// looks like an SMS mouse or keyboard
// check service name to be double-safe
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_SERVICE);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_SZ) {
//
// device has a service
//
if(_wcsicmp((PWCHAR)KeyValueInfo->Data,L"kbstuff")==0) {
//
// yup, definately SMS!
// we really don't want to delete this
//
goto CloseInstanceKeyAndContinue;
}
}
}
//
// We have a match. Check if the instance specifies a
// corresponding Driver key that should also be deleted.
//
// First check the old style "Driver" value.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_DRIVER);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_SZ) {
SppDeleteKeyRecursive(hClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
} else {
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_GUID);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_BINARY) {
Status = RtlStringFromGUID((REFGUID)KeyValueInfo->Data, &guidString);
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status)) {
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)q;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_DRVINST);
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength
);
if (NT_SUCCESS(Status) && KeyValueInfo->Type == REG_DWORD) {
drvInst = *(PULONG)KeyValueInfo->Data;
swprintf((PWCHAR)&KeyValueInfo->Data[0], TEXT("%wZ\\%04u"), &guidString, drvInst);
//
// Delete the Driver key.
//
SppDeleteKeyRecursive(hClassKey, (PWCHAR)KeyValueInfo->Data, TRUE);
}
RtlFreeUnicodeString(&guidString);
}
}
}
//
// Nuke this key and break out of the GUID comparison loop.
//
ZwClose(hInstanceKey);
SppDeleteKeyRecursive(hDeviceKey, InstanceKeyInfo->Name, TRUE);
goto DeletedKeyRecursive;
}
}
}
DeletedKeyRecursive:
if(ClassGuidToDelete) {
//
// We deleted the instance key, so set the instance enumeration index back to zero
// and continue.
//
InstanceKeyIndex = 0;
continue;
}
CloseInstanceKeyAndContinue:
//
// If we get to here, then we've decided that this instance key
// should not be deleted. Delete the Control key (if there happens
// to be one) to avoid a painful death at next boot.
//
SppDeleteKeyRecursive(hInstanceKey, L"Control", TRUE);
//
// Now close the handle, and move on to the next one.
//
ZwClose(hInstanceKey);
if (hSetupInstanceKey) {
ZwClose(hSetupInstanceKey);
}
InstanceKeyIndex++;
}
ZwClose(hDeviceKey);
//
// If we dropped out of the loop on instance subkeys, and the index is non-zero,
// then there remains at least one subkey that we didn't delete, so we can't nuke
// the parent. Otherwise, delete the device key.
//
if(InstanceKeysEnumerated && !InstanceKeyIndex) {
SppDeleteKeyRecursive(hRootKey, DeviceKeyInfo->Name, TRUE);
//
// Since we deleted a key, we must reset our enumeration index.
//
DeviceKeyIndex = 0;
} else {
//
// We didn't delete this key--move on to the next one.
//
DeviceKeyIndex++;
}
//
// If the device has no remaining instances in the setup registry,
// delete the device key.
//
if (hSetupDeviceKey) {
KEY_FULL_INFORMATION keyFullInfo;
Status = ZwQueryKey(hSetupDeviceKey,
KeyFullInformation,
(PVOID)&keyFullInfo,
sizeof(KEY_FULL_INFORMATION),
&ResultLength);
ZwClose(hSetupDeviceKey);
if ((NT_SUCCESS(Status) || (Status == STATUS_BUFFER_TOO_SMALL)) &&
(keyFullInfo.SubKeys == 0)) {
SppDeleteKeyRecursive(hSetupRootKey, DeviceKeyInfo->Name, TRUE);
}
}
}
ZwClose(hSetupClassKey);
ZwClose(hClassKey);
ZwClose(hSetupRootKey);
ZwClose(hRootKey);
SpMemFree(MyScratchBuffer);
return;
}
VOID
SppClearMigratedInstanceValues(
IN HANDLE hKeyCCSet
)
/*++
Routine Description:
This routine removes "Migrated" values from device instance keys in the
setup registry that were migrated at the start of textmode setup (from
winnt.sif, via SpMigrateDeviceInstanceData).
Arguments:
hKeyCCSet: Handle to the root of the control set in the system
being upgraded.
Return Value:
None.
Notes:
This routine is not called when performing an ASR setup (not an upgrade).
For upgrade setup, it is safe to remove "Migrated" values from all
device instance keys because these keys were migrated from the system
registry in the winnt.sif during the winnt32 portion of setup, so all the
information will be present when we boot into GUI setup after this.
Note that during ASR setup, these values are not removed during textmode
setp because the registry these instances were migrated from is not restored
until late in GUI setup.
--*/
{
GENERIC_BUFFER_CONTEXT Context;
//
// Allocate some scratch space for the callback routine to work with. The
// most it will need is enough for a KEY_VALUE_PARTIAL_INFORMATION
// structure, plus a stringified GUID, plus a large integer structure for
// alignment.
//
Context.BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
sizeof(DWORD) + sizeof(LARGE_INTEGER);
Context.Buffer = SpMemAlloc(Context.BufferSize);
if(!Context.Buffer) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Can't allocate context for SppClearMigratedInstanceValuesCallback, exiting!\n"));
return;
}
//
// Apply the devnode migration processing callback to all device instance
// keys.
//
SpApplyFunctionToDeviceInstanceKeys(hKeyCCSet,
SppClearMigratedInstanceValuesCallback,
&Context);
//
// Free the allocated context buffer,
//
SpMemFree(Context.Buffer);
return;
}
VOID
SppMigrateDeviceParentId(
IN HANDLE hKeyCCSet,
IN PWSTR DeviceId,
IN PSPP_DEVICE_MIGRATION_CALLBACK_ROUTINE DeviceMigrationCallbackRoutine
)
/*++
Routine Description:
This routine migrates the ParentIdPrefix or UniqueParentID value from the
specified device instance in the registry being upgraded to any device
instances in the current registry, as dictated by the specified
InstanceKeyCallbackRoutine.
Arguments:
hKeyCCSet: Handle to the root of the control set in the system
being upgraded.
DeviceId: Device instance Id of the device in the system being upgraded
whose ParentIdPrefix (or UniqueParentID) value is to be migrated
to device instance keys in the current system registry.
InstanceKeyCallbackRoutine: Callback routine for each device instance key in
the existsing registry that should decide if the values should be
replaced.
Return Value:
None.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING UnicodeString;
HANDLE hEnumKey, hInstanceKey;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
PUCHAR p;
ULONG ResultLength;
DEVICE_MIGRATION_CONTEXT DeviceMigrationContext;
//
// Allocate some scratch space to work with here, and in our callback
// routine. The most we'll need is enough for a
// KEY_VALUE_PARTIAL_INFORMATION structure, the length of a stringified GUID
// + '\' + 4 digit ordinal + terminating NULL, plus a large integer
// structure for alignment.
//
DeviceMigrationContext.BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
((GUID_STRING_LEN + 5)*sizeof(WCHAR)) +
sizeof(LARGE_INTEGER);
DeviceMigrationContext.Buffer = SpMemAlloc(DeviceMigrationContext.BufferSize);
if(!DeviceMigrationContext.Buffer) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Can't allocate memory for device migration processing!\n"));
return;
}
//
// Open the Enum key in the registry being upgraded.
//
INIT_OBJA(&Obja, &UnicodeString, L"Enum");
Obja.RootDirectory = hKeyCCSet;
Status = ZwOpenKey(&hEnumKey, KEY_ALL_ACCESS, &Obja);
if (!NT_SUCCESS(Status)) {
SpMemFree(DeviceMigrationContext.Buffer);
return;
}
//
// Open the specified device instance key in the registry being upgraded.
//
INIT_OBJA(&Obja, &UnicodeString, DeviceId);
Obja.RootDirectory = hEnumKey;
Status = ZwOpenKey(&hInstanceKey, KEY_ALL_ACCESS, &Obja);
ZwClose(hEnumKey);
if (!NT_SUCCESS(Status)) {
//
// Couldn't find the key to migrate, so we're done.
//
SpMemFree(DeviceMigrationContext.Buffer);
return;
}
//
// Retrieve the UniqueParentID, if one exists.
//
DeviceMigrationContext.ParentIdPrefix = NULL;
p = ALIGN_UP_POINTER(((PUCHAR)DeviceMigrationContext.Buffer), sizeof(LARGE_INTEGER));
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)p;
RtlInitUnicodeString(&UnicodeString, L"UniqueParentID");
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
KeyValueInfo,
(ULONG)((DeviceMigrationContext.Buffer +
DeviceMigrationContext.BufferSize) - p),
&ResultLength);
if (NT_SUCCESS(Status)) {
ASSERT(KeyValueInfo->Type == REG_DWORD);
DeviceMigrationContext.UniqueParentID = *(PULONG)(KeyValueInfo->Data);
} else {
//
// No UniqueParentID, so look for the ParentIdPrefix.
//
RtlInitUnicodeString(&UnicodeString, L"ParentIdPrefix");
Status = ZwQueryValueKey(hInstanceKey,
&UnicodeString,
KeyValuePartialInformation,
KeyValueInfo,
(ULONG)((DeviceMigrationContext.Buffer +
DeviceMigrationContext.BufferSize) - p),
&ResultLength);
if (NT_SUCCESS(Status)) {
ASSERT(KeyValueInfo->Type == REG_SZ);
DeviceMigrationContext.ParentIdPrefix = SpDupStringW((PWSTR)KeyValueInfo->Data);
ASSERT(DeviceMigrationContext.ParentIdPrefix);
}
}
ZwClose(hInstanceKey);
if (!NT_SUCCESS(Status)) {
//
// If we couldn't find either value, there's nothing more we can do.
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: No Parent Id values were found for %ws for migration. Status = %lx \n",
DeviceId,
Status));
SpMemFree(DeviceMigrationContext.Buffer);
return;
}
//
// Supply the hKeyCCSet for the system being upgraded.
//
DeviceMigrationContext.hKeyCCSet = hKeyCCSet;
//
// Supply the caller specified device migration callback routine.
//
DeviceMigrationContext.DeviceMigrationCallbackRoutine = DeviceMigrationCallbackRoutine;
//
// Apply the parent id migration callback for all device instance keys.
// This will in turn, call the specified device instance callback routine to
// determine whether parent id migration should be done.
//
SpApplyFunctionToDeviceInstanceKeys(hKeyCCSet,
SppMigrateDeviceParentIdCallback,
&DeviceMigrationContext);
if (DeviceMigrationContext.ParentIdPrefix) {
SpMemFree(DeviceMigrationContext.ParentIdPrefix);
}
SpMemFree(DeviceMigrationContext.Buffer);
return;
}
VOID
SppMigrateDeviceParentIdCallback(
IN HANDLE SetupInstanceKeyHandle,
IN HANDLE UpgradeInstanceKeyHandle OPTIONAL,
IN BOOLEAN RootEnumerated,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is a callback routine for SpApplyFunctionToDeviceInstanceKeys.
Arguments:
SetupInstanceKeyHandle: Handle to the device instance key in the current
registry.
UpgradeInstanceKeyHandle: Handle to the corresponding device instance key in
the system being upgraded, if it exists.
Context: User supplied context.
Return Value:
None.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING UnicodeString, guidString;
PDEVICE_MIGRATION_CONTEXT DeviceMigrationContext;
PUCHAR p;
ULONG ResultLength, drvInst;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
HANDLE hClassKey, hDriverKey;
BOOL CallbackResult;
UNREFERENCED_PARAMETER(SetupInstanceKeyHandle);
UNREFERENCED_PARAMETER(RootEnumerated);
//
// We only care about keys that exist in the system being upgraded.
//
if (!UpgradeInstanceKeyHandle) {
return;
}
//
// Retrieve the "Driver" value from the instance key.
//
DeviceMigrationContext = (PDEVICE_MIGRATION_CONTEXT)Context;
p = ALIGN_UP_POINTER(((PUCHAR)DeviceMigrationContext->Buffer), sizeof(LARGE_INTEGER));
//
// First check the old style "Driver" value.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)p;
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_DRIVER);
Status = ZwQueryValueKey(UpgradeInstanceKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
p,
(ULONG)((DeviceMigrationContext->Buffer +
DeviceMigrationContext->BufferSize) - p),
&ResultLength
);
if (!NT_SUCCESS(Status) || KeyValueInfo->Type != REG_SZ) {
//
// Try the new style "GUID" and "DrvInst" values.
//
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)p;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_GUID);
Status = ZwQueryValueKey(UpgradeInstanceKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
p,
(ULONG)((DeviceMigrationContext->Buffer +
DeviceMigrationContext->BufferSize) - p),
&ResultLength
);
if (!NT_SUCCESS(Status) || KeyValueInfo->Type != REG_BINARY) {
return;
}
Status = RtlStringFromGUID((REFGUID)KeyValueInfo->Data, &guidString);
ASSERT(NT_SUCCESS(Status));
if (!NT_SUCCESS(Status)) {
return;
}
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)p;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_DRVINST);
Status = ZwQueryValueKey(UpgradeInstanceKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
p,
(ULONG)((DeviceMigrationContext->Buffer +
DeviceMigrationContext->BufferSize) - p),
&ResultLength
);
if (!NT_SUCCESS(Status) || KeyValueInfo->Type != REG_DWORD) {
return;
}
drvInst = *(PULONG)KeyValueInfo->Data;
swprintf((PWCHAR)&KeyValueInfo->Data[0], TEXT("%wZ\\%04u"), &guidString, drvInst);
RtlFreeUnicodeString(&guidString);
}
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: SppMigrateDeviceParentIdCallback: Driver = %ws\n",
(PWSTR)KeyValueInfo->Data));
//
// Open the Control\Class key in the system being upgraded.
//
INIT_OBJA(&Obja, &UnicodeString, L"Control\\Class");
Obja.RootDirectory = DeviceMigrationContext->hKeyCCSet;
Status = ZwOpenKey(&hClassKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to open Class key for device migration processing. Status = %lx \n",
Status));
return;
}
//
// Open the device's "Driver" key.
//
INIT_OBJA(&Obja, &UnicodeString, (PWSTR)KeyValueInfo->Data);
Obja.RootDirectory = hClassKey;
Status = ZwOpenKey(&hDriverKey, KEY_ALL_ACCESS, &Obja);
ZwClose(hClassKey);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to open Class\\%ws key for device migration processing. Status = %lx \n",
(PWSTR)KeyValueInfo->Data,
Status));
return;
}
//
// Call the specified device migration callback routine.
//
CallbackResult = (DeviceMigrationContext->DeviceMigrationCallbackRoutine)(
UpgradeInstanceKeyHandle,
hDriverKey);
ZwClose(hDriverKey);
if (!CallbackResult) {
return;
}
//
// Replace the UniqueParentID or ParentIdPrefix values for this device
// instance. First, remove any UniqueParentId or ParentIdPrefix values that
// already exist for this instance key.
//
RtlInitUnicodeString(&UnicodeString, L"ParentIdPrefix");
ZwDeleteValueKey(UpgradeInstanceKeyHandle, &UnicodeString);
RtlInitUnicodeString(&UnicodeString, L"UniqueParentID");
ZwDeleteValueKey(UpgradeInstanceKeyHandle, &UnicodeString);
//
// Replace the instance key's UniqueParentID or ParentIdPrefix with that
// from the device migration context.
//
if (!DeviceMigrationContext->ParentIdPrefix) {
//
// We're using the old UniqueParentID mechanism.
//
RtlInitUnicodeString(&UnicodeString, L"UniqueParentID");
Status = ZwSetValueKey(UpgradeInstanceKeyHandle,
&UnicodeString,
0,
REG_DWORD,
&DeviceMigrationContext->UniqueParentID,
sizeof(DeviceMigrationContext->UniqueParentID));
if (!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to set %ws during device migration processing. Status = %lx \n",
UnicodeString.Buffer,
Status));
}
} else {
//
// We're using the ParentIdPrefix mechanism.
//
RtlInitUnicodeString(&UnicodeString, L"ParentIdPrefix");
Status = ZwSetValueKey(UpgradeInstanceKeyHandle,
&UnicodeString,
0,
REG_SZ,
DeviceMigrationContext->ParentIdPrefix,
(wcslen(DeviceMigrationContext->ParentIdPrefix)+1)*sizeof(WCHAR));
if (!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to set %ws during device migration processing. Status = %lx \n",
UnicodeString.Buffer,
Status));
}
}
return;
}
BOOL
SppParallelClassCallback(
IN HANDLE InstanceKeyHandle,
IN HANDLE DriverKeyHandle
)
/*++
Routine Description:
This routine is a callback routine for SpApplyFunctionToDeviceInstanceKeys.
Arguments:
InstanceKeyHandle: Handle to the device instance key in the system being
upgraded.
DriverKeyHandle: Handle to the driver key for device instance in
the system being upgraded.
Return Value:
Returns TRUE/FALSE.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
PUCHAR MyScratchBuffer;
ULONG MyScratchBufferSize;
PUCHAR p;
ULONG ResultLength;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
HANDLE hClassKey, hDriverKey;
UNICODE_STRING UnicodeString;
GUID guid;
//
// Allocate some scratch space to work with. The most we'll need is enough
// for a KEY_VALUE_PARTIAL_INFORMATION structure, plus a stringified GUID,
// plus a large integer structure for alignment.
//
MyScratchBufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
(GUID_STRING_LEN * sizeof(WCHAR)) +
sizeof(LARGE_INTEGER);
MyScratchBuffer = SpMemAlloc(MyScratchBufferSize);
if(!MyScratchBuffer) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Can't allocate memory for parallel migration processing!\n"));
return FALSE;
}
//
// Check the class of the enumerated device instance, and see if it is a
// member of the "Ports" class.
//
p = ALIGN_UP_POINTER(((PUCHAR)MyScratchBuffer), sizeof(LARGE_INTEGER));
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)p;
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_CLASSGUID);
Status = ZwQueryValueKey(InstanceKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
p,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - p),
&ResultLength);
if (NT_SUCCESS(Status)) {
if (KeyValueInfo->Type == REG_SZ) {
RtlInitUnicodeString(&UnicodeString, (PWSTR)KeyValueInfo->Data);
Status = RtlGUIDFromString(&UnicodeString, &guid);
} else {
Status = STATUS_UNSUCCESSFUL;
}
} else {
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)p;
RtlInitUnicodeString(&UnicodeString, REGSTR_VALUE_GUID);
Status = ZwQueryValueKey(InstanceKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
p,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - p),
&ResultLength);
if (NT_SUCCESS(Status)) {
if (KeyValueInfo->Type == REG_BINARY) {
guid = *(GUID *)KeyValueInfo->Data;
} else {
Status = STATUS_UNSUCCESSFUL;
}
}
}
if (NT_SUCCESS(Status)) {
if (!IsEqualGUID(&GUID_DEVCLASS_PORTS, &guid)) {
//
// Not a match.
//
Status = STATUS_UNSUCCESSFUL;
}
}
if (!NT_SUCCESS(Status)) {
return FALSE;
}
//
// Check the "PortSubClass" value from the device's driver key.
//
RtlInitUnicodeString(&UnicodeString, REGSTR_VAL_PORTSUBCLASS);
Status = ZwQueryValueKey(DriverKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
p,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - p),
&ResultLength);
if (!NT_SUCCESS(Status) ||
(KeyValueInfo->Type != REG_BINARY) ||
(KeyValueInfo->DataLength != sizeof(BYTE)) ||
(*(PBYTE)(KeyValueInfo->Data) != 0x0)) {
return FALSE;
}
//
// This device instance is a parallel port device.
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: \tSppParallelClassCallback: Found a parallel port!\n"));
return TRUE;
}
VOID
SppClearMigratedInstanceValuesCallback(
IN HANDLE SetupInstanceKeyHandle,
IN HANDLE UpgradeInstanceKeyHandle OPTIONAL,
IN BOOLEAN RootEnumerated,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is a callback routine for SpApplyFunctionToDeviceInstanceKeys.
Arguments:
SetupInstanceKeyHandle: Handle to the device instance key in the current
registry.
UpgradeInstanceKeyHandle: Handle to the corresponding device instance key in
the system being upgraded, if it exists.
Context: User supplied context.
Return Value:
None.
--*/
{
NTSTATUS Status;
UNICODE_STRING UnicodeString;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
PUCHAR p;
ULONG ResultLength;
PGENERIC_BUFFER_CONTEXT BufferContext;
//
// To save us the effort of allocating a buffer on every iteration of the
// callback, SppClearMigratedInstanceValues has already allocated a buffer
// for us to use, and supplied to us as our context.
//
BufferContext = (PGENERIC_BUFFER_CONTEXT)Context;
ASSERT(BufferContext->Buffer);
ASSERT(BufferContext->BufferSize > 0);
//
// Check if the Migrated value still exists on this registry key. If so, it
// was migrated, but wasn't seen by textmode setup.
//
p = ALIGN_UP_POINTER(((PUCHAR)BufferContext->Buffer), sizeof(LARGE_INTEGER));
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)p;
RtlInitUnicodeString(&UnicodeString, L"Migrated");
Status = ZwQueryValueKey(SetupInstanceKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
p,
(ULONG)(BufferContext->BufferSize),
&ResultLength);
if (NT_SUCCESS(Status)) {
//
// If there is a Migrated value, it should be well-formed, but we still
// want to delete it no matter what it is.
//
ASSERT(KeyValueInfo->Type == REG_DWORD);
ASSERT(*(PULONG)(KeyValueInfo->Data) == 1);
if (UpgradeInstanceKeyHandle) {
//
// This instance key exists in the upgraded registry, so we'll
// remove the Migrated value from it in the setup registry.
//
Status = ZwDeleteValueKey(SetupInstanceKeyHandle, &UnicodeString);
ASSERT(NT_SUCCESS(Status));
//
// Remove the migrated value from the key in the upgraded registry
// only if it is root-enumerated, because those devices should
// always be enumerated, no matter what.
//
// (If the instance key is not root-enumerated, the value should
// really stay as it is - so that migrated values on ASR machines
// are preserved on upgrades.)
//
if (RootEnumerated) {
ZwDeleteValueKey(UpgradeInstanceKeyHandle, &UnicodeString);
}
}
}
return;
}
VOID
SpApplyFunctionToDeviceInstanceKeys(
IN HANDLE hKeyCCSet,
IN PSPP_INSTANCEKEY_CALLBACK_ROUTINE InstanceKeyCallbackRoutine,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine enumerates device instance keys in the setup registry, and
calls the specified callback routine for each such device instance key.
Arguments:
hKeyCCSet: Handle to the root of the control set in the system
being upgraded.
InstanceKeyCallbackRoutine - Supplies a pointer to a function that will be
called for each device instance key in the setup registry.
The prototype of the function is as follows:
typedef VOID (*PSPP_INSTANCEKEY_CALLBACK_ROUTINE) (
IN HANDLE SetupInstanceKeyHandle,
IN HANDLE UpgradeInstanceKeyHandle OPTIONAL,
IN BOOLEAN RootEnumerated,
IN OUT PVOID Context
);
where SetupInstanceKeyHandle is the handle to an enumerated device
instance key in the setup registry, UpgradeInstanceKeyHandle is the
handle to the corresponding device instance key in the registry being
upgraded (if exists), and Context is a pointer to user-defined data.
Return Value:
None.
Note:
Note that a device instance key in the system being upgraded is opened only
after the corresponding device instance key was enumerated in the setup
registry.
--*/
{
NTSTATUS Status;
HANDLE hEnumKey, hEnumeratorKey, hDeviceKey, hInstanceKey;
HANDLE hUpgradeEnumKey, hUpgradeEnumeratorKey, hUpgradeDeviceKey, hUpgradeInstanceKey;
BOOLEAN RootEnumerated;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING UnicodeString;
PUCHAR MyScratchBuffer;
ULONG MyScratchBufferSize;
ULONG EnumeratorKeyIndex, DeviceKeyIndex, InstanceKeyIndex, ResultLength;
PKEY_BASIC_INFORMATION EnumeratorKeyInfo, DeviceKeyInfo, InstanceKeyInfo;
PUCHAR p, q, r;
//
// First, open CCS\Enum in the setup registry.
//
INIT_OBJA(&Obja, &UnicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum");
Obja.RootDirectory = NULL;
Status = ZwOpenKey(&hEnumKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Unable to open setup Enum for device migration processing. Status = %lx \n",
Status));
return;
}
//
// Next, open CCS\Enum in the registry being upgraded.
//
INIT_OBJA(&Obja, &UnicodeString, L"Enum");
Obja.RootDirectory = hKeyCCSet;
Status = ZwOpenKey(&hUpgradeEnumKey, KEY_ALL_ACCESS, &Obja);
if (!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL,
"SETUP: Unable to open upgrade Enum for device migration processing. Status = %lx \n",
Status));
//
// This is really odd, but not fatal.
//
hUpgradeEnumKey = NULL;
}
//
// Allocate some scratch space to work with. The most we'll need is enough
// for 3 KEY_BASIC_INFORMATION structures, plus the maximum length of a
// device instance ID, plus 3 large integer structures for alignment.
//
MyScratchBufferSize = (3*sizeof(KEY_BASIC_INFORMATION)) +
(200*sizeof(WCHAR)) +
(3*sizeof(LARGE_INTEGER));
MyScratchBuffer = SpMemAlloc(MyScratchBufferSize);
if(!MyScratchBuffer) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
"SETUP: Can't allocate memory for device migration processing!\n"));
ZwClose(hEnumKey);
return;
}
//
// First, enumerate the enumerator subkeys under the Enum key.
//
EnumeratorKeyInfo = (PKEY_BASIC_INFORMATION)MyScratchBuffer;
EnumeratorKeyIndex = 0;
while(TRUE) {
Status = ZwEnumerateKey(hEnumKey,
EnumeratorKeyIndex,
KeyBasicInformation,
MyScratchBuffer,
MyScratchBufferSize,
&ResultLength);
if(!NT_SUCCESS(Status)) {
break;
}
//
// Zero-terminate the subkey name just in case.
//
EnumeratorKeyInfo->Name[EnumeratorKeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Go ahead and bump the used-buffer length by sizeof(WCHAR), to
// accomodate the potential growth caused by adding a terminating NULL.
//
ResultLength += sizeof(WCHAR);
//
// Determine if the subkey devices are root-enumerated.
//
RootEnumerated = (_wcsnicmp(EnumeratorKeyInfo->Name,
REGSTR_KEY_ROOTENUM, 4) == 0);
//
// Now, open up the enumerator key so we can enumerate the devices.
//
INIT_OBJA(&Obja, &UnicodeString, EnumeratorKeyInfo->Name);
Obja.RootDirectory = hEnumKey;
Status = ZwOpenKey(&hEnumeratorKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
//
// Skip this key and continue.
//
EnumeratorKeyIndex++;
continue;
}
//
// Open the enumerator key in the registry being upgraded.
//
hUpgradeEnumeratorKey = NULL;
if (hUpgradeEnumKey) {
Obja.RootDirectory = hUpgradeEnumKey;
Status = ZwOpenKey(&hUpgradeEnumeratorKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
//
// Again, this is odd, but not fatal.
//
hUpgradeEnumeratorKey = NULL;
}
}
//
// Now enumerate the device subkeys under this enumerator key. Don't
// overwrite the enumerator key name already in MyScratchBuffer.
//
p = ALIGN_UP_POINTER(((PUCHAR)MyScratchBuffer + ResultLength), sizeof(LARGE_INTEGER));
//
// Now, enumerate all devices under the enumerator.
//
DeviceKeyInfo = (PKEY_BASIC_INFORMATION)p;
DeviceKeyIndex = 0;
while(TRUE) {
Status = ZwEnumerateKey(hEnumeratorKey,
DeviceKeyIndex,
KeyBasicInformation,
p,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - p),
&ResultLength);
if(!NT_SUCCESS(Status)) {
break;
}
//
// Zero-terminate the subkey name just in case.
//
DeviceKeyInfo->Name[DeviceKeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Go ahead and bump the used-buffer length by sizeof(WCHAR), to
// accomodate the potential growth caused by adding a terminating NULL.
//
ResultLength += sizeof(WCHAR);
//
// Now, open up the device key so we can enumerate the instances.
//
INIT_OBJA(&Obja, &UnicodeString, DeviceKeyInfo->Name);
Obja.RootDirectory = hEnumeratorKey;
Status = ZwOpenKey(&hDeviceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
//
// Skip this key and continue.
//
DeviceKeyIndex++;
continue;
}
//
// Open the device key in the registry being upgraded.
//
hUpgradeDeviceKey = NULL;
if (hUpgradeEnumeratorKey) {
Obja.RootDirectory = hUpgradeEnumeratorKey;
Status = ZwOpenKey(&hUpgradeDeviceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
//
// Again, this is odd, but not fatal.
//
hUpgradeDeviceKey = NULL;
}
}
//
// Now enumerate the device subkeys under this enumerator key. Don't
// overwrite the enumerator key name already in MyScratchBuffer.
//
q = ALIGN_UP_POINTER(((PUCHAR)p + ResultLength), sizeof(LARGE_INTEGER));
//
// Now, enumerate all instances under the device.
//
InstanceKeyInfo = (PKEY_BASIC_INFORMATION)q;
InstanceKeyIndex = 0;
while(TRUE) {
Status = ZwEnumerateKey(hDeviceKey,
InstanceKeyIndex,
KeyBasicInformation,
q,
(ULONG)((MyScratchBuffer + MyScratchBufferSize) - q),
&ResultLength);
if(!NT_SUCCESS(Status)) {
break;
}
//
// Zero-terminate the subkey name just in case.
//
InstanceKeyInfo->Name[InstanceKeyInfo->NameLength/sizeof(WCHAR)] = 0;
//
// Go ahead and bump the used-buffer length by sizeof(WCHAR), to
// accomodate the potential growth caused by adding a terminating NULL.
//
ResultLength += sizeof(WCHAR);
//
// Now, open up the instance key.
//
INIT_OBJA(&Obja, &UnicodeString, InstanceKeyInfo->Name);
Obja.RootDirectory = hDeviceKey;
Status = ZwOpenKey(&hInstanceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
//
// Skip this key and continue.
//
InstanceKeyIndex++;
continue;
}
//
// Open the instance key in the registry being upgraded.
//
hUpgradeInstanceKey = NULL;
if (hUpgradeDeviceKey) {
Obja.RootDirectory = hUpgradeDeviceKey;
Status = ZwOpenKey(&hUpgradeInstanceKey, KEY_ALL_ACCESS, &Obja);
if(!NT_SUCCESS(Status)) {
//
// Again, this is odd, but not fatal.
//
hUpgradeInstanceKey = NULL;
}
}
//
// Call the specified callback routine for this device instance key.
//
InstanceKeyCallbackRoutine(hInstanceKey,
hUpgradeInstanceKey,
RootEnumerated,
Context);
InstanceKeyIndex++;
ZwClose(hInstanceKey);
if (hUpgradeInstanceKey) {
ZwClose(hUpgradeInstanceKey);
}
}
DeviceKeyIndex++;
ZwClose(hDeviceKey);
if (hUpgradeDeviceKey) {
ZwClose(hUpgradeDeviceKey);
}
}
EnumeratorKeyIndex++;
ZwClose(hEnumeratorKey);
if (hUpgradeEnumeratorKey) {
ZwClose(hUpgradeEnumeratorKey);
}
}
ZwClose(hEnumKey);
if (hUpgradeEnumKey) {
ZwClose(hUpgradeEnumKey);
}
SpMemFree(MyScratchBuffer);
return;
}
BOOLEAN
SppEnsureHardwareProfileIsPresent(
IN HANDLE hKeyCCSet
)
/*++
Routine Description:
This routine checks for the presence of the presence of the following keys:
HKLM\System\CurrentControlSet\Control\IDConfigDB\Hardware Profiles
HKLM\System\CurrentControlSet\Hardware Profiles
If these keys exist, it checks the profile information subkeys under
IDConfigDB for a "Pristine Profile".
If the Pristine Profile is not in the proper NT5 format, that is
under the \0000 subkey, with a PreferenceOrder == -1 and Pristine == 1,
then it is deleted, and we rely on a valid pristine profile with
these settings to be migrated from the SETUPREG.HIV. We then re-order
the PreferenceOrder values for the remaining hardware profiles, and
make sure sure each has a HwProfileGuid.
If a valid Pristine profile is found, it is not removed, and will
not be replaced during migration.
If one of either the CCS\Control\IDConfigDB\Hardware Profiles key, or
the CCS\Hardware Profiles keys is missing, then the set of hardware
profiles is invalid, and both keys will be removed and migrated from
the SETUPREG.HIV.
Arguments:
hKeyCCSet - Handle to the root of the control set in the system
being upgraded.
Return Value:
If successful, the return value is TRUE, otherwise it is FALSE.
--*/
{
OBJECT_ATTRIBUTES ObjaID, ObjaHw;
UNICODE_STRING UnicodeString, TempString, UnicodeValueName;
UNICODE_STRING UnicodeKeyName, GuidString, UnicodeLabel;
NTSTATUS Status;
HANDLE IDConfigProfiles=NULL, IDConfigEntry=NULL;
HANDLE HwProfiles=NULL, HwProfileEntry=NULL;
ULONG profileNumber;
ULONG len;
PWSTR SubkeyName;
ULONG ValueBufferSize;
BOOLEAN b = TRUE;
BOOLEAN ReOrder = FALSE, bKeyNameIs0000 = FALSE;
ULONG pristinePreferenceOrder, preferenceOrder;
ULONG enumIndex, resultLength;
ULONG nameIndex, dockState;
UUID uuid;
PKEY_BASIC_INFORMATION pKeyInfo;
PKEY_VALUE_FULL_INFORMATION pValueInfo;
//
// Initialize Object Attributes for Hardware profile specific keys
//
INIT_OBJA(&ObjaID, &UnicodeString, L"Control\\IDConfigDB\\Hardware Profiles");
ObjaID.RootDirectory = hKeyCCSet;
INIT_OBJA(&ObjaHw, &TempString, L"Hardware Profiles");
ObjaHw.RootDirectory = hKeyCCSet;
//
// Attempt to open "CCS\Control\IDConfigDB\Hardware Profiles"
// and "CCS\Hardware Profiles" keys.
// If either key is missing, this is an inconsistent state;
// make sure neither key is present and rely on the migration of these
// keys from SETUPREG.HIV to provide the basic state (pristine only).
//
if ((ZwOpenKey(&IDConfigProfiles,
KEY_READ | KEY_WRITE,
&ObjaID) != STATUS_SUCCESS) ||
(ZwOpenKey(&HwProfiles,
KEY_READ | KEY_WRITE,
&ObjaHw) != STATUS_SUCCESS)) {
SppDeleteKeyRecursive(hKeyCCSet, UnicodeString.Buffer, TRUE);
SppDeleteKeyRecursive(hKeyCCSet, TempString.Buffer, TRUE);
goto Clean;
}
//
// Look for the pristine profile.
//
enumIndex = 0;
while(TRUE) {
//
//Enumerate through each Profile Key
//
Status = ZwEnumerateKey(IDConfigProfiles,
enumIndex,
KeyBasicInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&resultLength);
if(!NT_SUCCESS(Status)) {
//
// couldn't enumerate subkeys
//
if(Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: ** Unable to enumerate existing Hardware Profiles (%lx)\n", Status));
b = FALSE;
}
break;
}
//
// Zero-terminate the subkey name just in case.
//
pKeyInfo = (PKEY_BASIC_INFORMATION)TemporaryBuffer;
pKeyInfo->Name[pKeyInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL;
SubkeyName = SpDupStringW(pKeyInfo->Name);
RtlInitUnicodeString(&UnicodeKeyName, SubkeyName);
//
// See if this Profile is occupying the space the Pristine Profile should
// occupy. We'll check to see if it is really the Pristine Profile later.
//
Status = RtlUnicodeStringToInteger( &UnicodeKeyName, 10, &profileNumber );
if (!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Could not get integer profile number for key %ws (%lx)\n",
UnicodeKeyName.Buffer,Status));
bKeyNameIs0000 = FALSE;
} else {
bKeyNameIs0000 = (profileNumber==0);
}
//
// Open the subkey
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Checking Profile Key %ws (%lx)\n",UnicodeKeyName.Buffer,Status));
InitializeObjectAttributes (&ObjaID,
&UnicodeKeyName,
OBJ_CASE_INSENSITIVE,
IDConfigProfiles,
NULL);
Status = ZwOpenKey(&IDConfigEntry,
KEY_ALL_ACCESS,
&ObjaID);
if (!NT_SUCCESS(Status)) {
//
// Couldn't open this particular profile key, just log
// it and check the others, shouldn't stop Setup here.
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: ** Unable to open enumerated Hardware Profile key %ws (%lx)\n",
UnicodeKeyName.Buffer, Status));
SpMemFree(SubkeyName);
enumIndex++;
continue;
}
//
// Look for the Pristine Entry
//
RtlInitUnicodeString(&UnicodeValueName, L"Pristine");
Status = ZwQueryValueKey(IDConfigEntry,
&UnicodeValueName,
KeyValueFullInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&resultLength);
pValueInfo = (PKEY_VALUE_FULL_INFORMATION)TemporaryBuffer;
if (NT_SUCCESS(Status) && (pValueInfo->Type == REG_DWORD) &&
(* (PULONG) ((PUCHAR)pValueInfo + pValueInfo->DataOffset))) {
//
// Found the Pristine Entry, now find its PreferenceOrder
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Found what appears to be a Pristine profile (%lx)\n",Status));
RtlInitUnicodeString(&UnicodeValueName, REGSTR_VAL_PREFERENCEORDER);
Status = ZwQueryValueKey(IDConfigEntry,
&UnicodeValueName,
KeyValueFullInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&resultLength);
if(NT_SUCCESS(Status) && (pValueInfo->Type == REG_DWORD)) {
//
// Found the PreferenceOrder of the Pristine;
// save it so we can fill in the gap left after we delete it.
//
pristinePreferenceOrder = (* (PULONG) ((PUCHAR)pValueInfo + pValueInfo->DataOffset));
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: PreferenceOrder of this Pristine == %u\n",
pristinePreferenceOrder));
//
// At most one Pristine Profile should ever be found and reordered,
// or else the reordering of profiles will not work properly.
//
ASSERT(!ReOrder);
if (bKeyNameIs0000 && (pristinePreferenceOrder == -1)) {
//
// This is a valid 0000 Pristine Profile Key, don't touch it.
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Key %ws is a valid pristine profile\n",
UnicodeKeyName.Buffer));
enumIndex++;
} else {
//
// This is an old-style Pristine Profile, delete it and the corresponding
// key under "CCS\Hardware Profiles", and rely on the Pristine Profile
// keys migrated from setupreg.hiv (as specified in txtsetup.sif)
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Key %ws is an invalid pristine profile, deleteing this key.\n",
UnicodeKeyName.Buffer));
ReOrder = TRUE;
ZwDeleteKey(IDConfigEntry);
SppDeleteKeyRecursive(HwProfiles,
UnicodeKeyName.Buffer,
TRUE);
}
} else {
//
// An invalid Pristine config has no PreferenceOrder,
// Just delete it, and nobody should miss it.
//
ZwDeleteKey(IDConfigEntry);
SppDeleteKeyRecursive(HwProfiles,
UnicodeKeyName.Buffer,
TRUE);
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Invalid PreferenceOrder value for key %ws, deleting this key. (%lx)\n",
UnicodeKeyName.Buffer,Status));
}
} else {
//
// Not a Pristine Profile
//
if (bKeyNameIs0000) {
//
// We need to wipe out any non-Pristine Profiles currently occupying key \0000
// to make room for the new Pristine Profile that we'll migrate over later.
// (sorry, but nobody has any business being here in the first place.)
//
ZwDeleteKey(IDConfigEntry);
SppDeleteKeyRecursive(HwProfiles,
UnicodeKeyName.Buffer,
TRUE);
} else {
//
// Check that it has a PreferenceOrder
//
RtlInitUnicodeString(&UnicodeValueName, REGSTR_VAL_PREFERENCEORDER);
Status = ZwQueryValueKey(IDConfigEntry,
&UnicodeValueName,
KeyValueFullInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&resultLength);
if(!NT_SUCCESS(Status) || (pValueInfo->Type != REG_DWORD)) {
//
// Invalid or missing PreferenceOrder for this profile;
// Since this profile was most likely inaccessible anyways,
// just delete it and the corresponding entry under CCS\\Hardware Profiles.
//
ZwDeleteKey(IDConfigEntry);
SppDeleteKeyRecursive(HwProfiles,
UnicodeKeyName.Buffer,
TRUE);
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Invalid PreferenceOrder value for key %ws, deleting this key. (%lx)\n",
UnicodeKeyName.Buffer,Status));
} else {
//
// Make sure all profiles have a HwProfileGuid value.
//
RtlInitUnicodeString(&UnicodeValueName, L"HwProfileGuid");
Status = ZwQueryValueKey(IDConfigEntry,
&UnicodeValueName,
KeyValueFullInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&resultLength);
pValueInfo = (PKEY_VALUE_FULL_INFORMATION)TemporaryBuffer;
if (!NT_SUCCESS(Status) || (pValueInfo->Type != REG_SZ)) {
//
// Profile doesn't have a HwProfileGuid; make one up.
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Missing or invalid HwProfileGuid for Profile %ws, creating one (%lx)\n",
UnicodeKeyName.Buffer, Status));
Status = ExUuidCreate(&uuid);
if (NT_SUCCESS(Status)) {
Status = RtlStringFromGUID(&uuid, &GuidString);
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status)) {
Status = ZwSetValueKey(IDConfigEntry,
&UnicodeValueName,
0,
REG_SZ,
GuidString.Buffer,
GuidString.Length + sizeof(UNICODE_NULL));
RtlFreeUnicodeString(&GuidString);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: ** Unable to set HwProfileGuid value for key %ws, Status = (%lx)\n",
UnicodeKeyName.Buffer,Status));
}
} else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: ** Unable to create string from GUID (Status = %lx)\n",
Status));
}
} else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: ** Could not create a GUID for this profile (Status = %lx)\n",
Status));
}
}
//
// only raise enumIndex when we don't delete a key.
//
enumIndex++;
}
}
}
SpMemFree(SubkeyName);
ZwClose(IDConfigEntry);
IDConfigEntry = NULL;
}
//
// If we don't need to reorder any PreferenceOrder values, we're done.
//
if (!ReOrder) {
goto Clean;
}
//
// ReOrder PreferenceOrder values after deleting one
// to make up for the gap.
//
enumIndex = 0;
while(TRUE) {
//
//Enumerate through each Profile Key again
//
Status = ZwEnumerateKey(IDConfigProfiles,
enumIndex,
KeyBasicInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&resultLength);
if(!NT_SUCCESS(Status)) {
if(Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: ** Unable to reorder remaining Hardware Profiles (%lx)\n", Status));
b = FALSE;
}
break;
}
//
// Zero-terminate the subkey name just in case.
//
pKeyInfo = (PKEY_BASIC_INFORMATION)TemporaryBuffer;
pKeyInfo->Name[pKeyInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL;
SubkeyName = SpDupStringW(pKeyInfo->Name);
RtlInitUnicodeString(&UnicodeKeyName, SubkeyName);
InitializeObjectAttributes (&ObjaID,
&UnicodeKeyName,
OBJ_CASE_INSENSITIVE,
IDConfigProfiles,
NULL);
Status = ZwOpenKey (&IDConfigEntry,
KEY_ALL_ACCESS,
&ObjaID);
if (!NT_SUCCESS(Status)) {
//
// Couldn't open this particular profile key, just log
// it and check the others, shouldn't stop Setup here.
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: ** Unable to open enumerated Hardware Profile key %ws (%lx)\n",
UnicodeKeyName.Buffer, Status));
SpMemFree(SubkeyName);
enumIndex++;
continue;
}
pValueInfo = (PKEY_VALUE_FULL_INFORMATION)(TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2));
ValueBufferSize = sizeof(TemporaryBuffer) / 2;
//
// Get the PreferenceOrder for this profile
//
RtlInitUnicodeString(&UnicodeValueName, REGSTR_VAL_PREFERENCEORDER);
Status = ZwQueryValueKey(IDConfigEntry,
&UnicodeValueName,
KeyValueFullInformation,
pValueInfo,
ValueBufferSize,
&len);
if(NT_SUCCESS(Status) && (pValueInfo->Type == REG_DWORD)) {
//
// Got the Preference Order
//
ASSERT((* (PULONG) ((PUCHAR)pValueInfo + pValueInfo->DataOffset)) != pristinePreferenceOrder);
if (((* (PULONG) ((PUCHAR)pValueInfo + pValueInfo->DataOffset)) > pristinePreferenceOrder) &&
((* (PULONG) ((PUCHAR)pValueInfo + pValueInfo->DataOffset)) != -1)) {
//
// Re-order PreferenceOrders for profiles other than a valid pristine,
// beyond deleted pristine up one.
//
preferenceOrder = (* (PULONG) ((PUCHAR)pValueInfo + pValueInfo->DataOffset)) - 1;
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: ReOrdering Profile %ws to PreferenceOrder %u\n",
UnicodeKeyName.Buffer,preferenceOrder));
Status = ZwSetValueKey(IDConfigEntry,
&UnicodeValueName,
0,
REG_DWORD,
&preferenceOrder,
sizeof(preferenceOrder));
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to change PreferenceOrder for Profile %ws, Status = (%lx)\n",
UnicodeKeyName.Buffer,Status));
b = FALSE;
}
}
} else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: *** Couldn't determine PreferenceOrder of profile %ws (%lx)\n",
UnicodeKeyName.Buffer,Status));
}
enumIndex++;
SpMemFree(SubkeyName);
ZwClose(IDConfigEntry);
IDConfigEntry = NULL;
}
Clean:
if (NULL != IDConfigProfiles) {
ZwClose (IDConfigProfiles);
}
if (NULL != IDConfigEntry) {
ZwClose (IDConfigEntry);
}
if (NULL != HwProfiles) {
ZwClose (HwProfiles);
}
if (NULL != HwProfileEntry) {
ZwClose (HwProfileEntry);
}
return b;
}
VOID
SppSetGuimodeUpgradePath(
IN HANDLE hKeySoftwareHive,
IN HANDLE hKeyControlSet
)
{
PWSTR Default_Path[3] = { L"%SystemRoot%\\system32",
L"%SystemRoot%",
L"%SystemRoot%\\system32\\WBEM"};
UNICODE_STRING StringRegPath;
UNICODE_STRING StringRegOldPath, UnicodeString;
PKEY_VALUE_PARTIAL_INFORMATION pValueInfo;
ULONG len;
PWSTR CurrentPath = NULL;
PWSTR p,q,final;
OBJECT_ATTRIBUTES Obja;
HKEY hKeyEnv;
DWORD err;
BOOL Found;
int i;
INIT_OBJA( &Obja, &UnicodeString, L"Control\\Session Manager\\Environment" );
Obja.RootDirectory = hKeyControlSet;
err = ZwOpenKey( &hKeyEnv, KEY_ALL_ACCESS, &Obja );
if( NT_SUCCESS( err )){
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP:SppSetGuimodeUpgradePath - Opened the Environment key\n" ));
RtlInitUnicodeString(&StringRegPath, L"Path");
err = ZwQueryValueKey(
hKeyEnv,
&StringRegPath,
KeyValuePartialInformation,
TemporaryBuffer,
sizeof(TemporaryBuffer),
&len);
if( NT_SUCCESS(err)) {
pValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer;
if( pValueInfo->Type == REG_EXPAND_SZ || pValueInfo->Type == REG_SZ) {
CurrentPath = SpDupStringW( (PWSTR)pValueInfo->Data );
// Now we try to extract from the existing path all elements that are not part of the default
// path that we maintain during GUI Setup. We then append that to the default path. That way
// we don't end up duplicating path elements over successive upgrades. We store this in the
// 'OldPath' value and restore it at the end of GUI mode.
//
TemporaryBuffer[0]=L'\0';
for(i=0; i<ELEMENT_COUNT(Default_Path); i++){
wcscat( TemporaryBuffer, Default_Path[i] );
wcscat( TemporaryBuffer, L";");
}
TemporaryBuffer[wcslen(TemporaryBuffer)-1]=L'\0';
//Set the default path in the registry
err = ZwSetValueKey(
hKeyEnv,
&StringRegPath,
0,
REG_EXPAND_SZ,
TemporaryBuffer,
((wcslen(TemporaryBuffer)+1)*sizeof(WCHAR)));
if( !NT_SUCCESS( err ) )
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "\nSETUP: Error %x in saving path. Ignoring and not resetting PATH for GUI Setup\n", err));
for( p=q=CurrentPath; p && *p; ){
//Jump to the ';' delimiter
if( q = wcsstr(p, L";") )
*q=0;
// Compare with elements of our default path
Found=FALSE;
for(i=0; i<ELEMENT_COUNT(Default_Path); i++){
if (!_wcsicmp(p,Default_Path[i])) {
Found=TRUE;
break;
}
}
if(!Found){
wcscat( TemporaryBuffer, L";");
wcscat( TemporaryBuffer, p);
}
if(q)
p=q+1;
else
break;
}
RtlInitUnicodeString(&StringRegOldPath, L"OldPath");
//
// Set the Oldpath always, if it exists or not
//
err = ZwSetValueKey(
hKeyEnv,
&StringRegOldPath,
0,
REG_EXPAND_SZ,
TemporaryBuffer,
((wcslen(TemporaryBuffer)+1)*sizeof(WCHAR)));
if( !NT_SUCCESS( err ) )
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "\nSETUP: Error %x in saving old PATH. \n", err));
} else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "\nSETUP:PATH type in registry not REG_EXPAND_SZ nor REG_SZ. Resetting PATH to default\n"));
TemporaryBuffer[0]=L'\0';
for(i=0; i<ELEMENT_COUNT(Default_Path); i++){
wcscat( TemporaryBuffer, Default_Path[i] );
wcscat( TemporaryBuffer, L";");
}
TemporaryBuffer[wcslen(TemporaryBuffer)-1]=L'\0';
//Set the default path in the registry
err = ZwSetValueKey(
hKeyEnv,
&StringRegPath,
0,
REG_EXPAND_SZ,
TemporaryBuffer,
((wcslen(TemporaryBuffer)+1)*sizeof(WCHAR)));
if( !NT_SUCCESS( err ) )
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "\nSETUP: Error %x in saving path. Ignoring and not resetting PATH for GUI Setup\n", err));
}
}else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "\nSETUP:Query for PATH value failed with error %x. Ignoring and not resetting PATH for GUI Setup\n",err));
}
ZwClose( hKeyEnv );
}else
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "\nSETUP:Error %x while opening Environment key. Ignoring and not resetting PATH for GUI Setup\n",err));
if( CurrentPath )
SpMemFree( CurrentPath );
return;
}
NTSTATUS
SppMigratePrinterKeys(
IN HANDLE hControlSet,
IN HANDLE hDestSoftwareHive
)
/*++
Routine Description:
This routine migrates HKLM\SYSTEM\CurrentControlSet\Control\Print\Printers to
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers.
Arguments:
hControlSet - Handle to CurrentControlSet key in the system hive of the system being upgraded
hDestSoftwareHive - Handle to the root of the software hive on the system
being upgraded.
Return Value:
Status value indicating outcome of operation.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING UnicodeString;
PWSTR SrcPrinterKeyPath = L"Control\\Print\\Printers";
PWSTR DstPrinterKeyName = L"Printers";
PWSTR DstPrinterKeyPath = SpDupStringW(L"Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers");
HANDLE SrcKey;
HANDLE DstKey;
//
// Find out if the destination key exists
//
INIT_OBJA(&Obja,&UnicodeString,DstPrinterKeyPath);
Obja.RootDirectory = hDestSoftwareHive;
Status = ZwOpenKey(&DstKey,KEY_ALL_ACCESS,&Obja);
if( NT_SUCCESS( Status ) ) {
//
// If the key exists, then there is no need to do any migration.
// The migration has occurred on previous upgrades.
//
ZwClose( DstKey );
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: HKLM\\SYSTEM\\CurrentControlSet\\%ls doesn't need to be migrated. \n", DstPrinterKeyPath));
SpMemFree( DstPrinterKeyPath );
return( Status );
} else if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
//
// The key doesn't exist, so we need to do migration.
// First create the parent key.
//
PWSTR p;
p = wcsrchr ( DstPrinterKeyPath, L'\\' );
if (p) {
*p = L'\0';
}
INIT_OBJA(&Obja,&UnicodeString,DstPrinterKeyPath);
Obja.RootDirectory = hDestSoftwareHive;
Status = ZwCreateKey(&DstKey,
KEY_ALL_ACCESS,
&Obja,
0,
NULL,
REG_OPTION_NON_VOLATILE,
NULL );
if( !NT_SUCCESS( Status ) ) {
//
// If unable to create the parent key, then don't do migration
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to create HKLM\\SOFTWARE\\%ls. Status = %lx \n", DstPrinterKeyPath, Status));
SpMemFree( DstPrinterKeyPath );
return( Status );
}
} else {
//
// We can't really determine whether or not the migration has occurred in the past, because the key is
// unaccessible. So son't attempt to do migration.
//
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open HKLM\\SOFTWARE\\%ls. Status = %lx \n", DstPrinterKeyPath, Status));
SpMemFree( DstPrinterKeyPath );
return( Status );
}
//
// At this point we now that the migration needs to be done.
// First, open the source key. Note that DstPrinterKeyPath is no longer needed.
//
SpMemFree( DstPrinterKeyPath );
INIT_OBJA(&Obja,&UnicodeString,SrcPrinterKeyPath);
Obja.RootDirectory = hControlSet;
Status = ZwOpenKey(&SrcKey,KEY_ALL_ACCESS,&Obja);
if( !NT_SUCCESS( Status ) ) {
//
// If unable to open the source key, then fail.
//
ZwClose( DstKey );
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open HKLM\\SYSTEM\\CurrentControlSet\\%ls. Status = %lx \n", SrcPrinterKeyPath, Status));
return( Status );
}
Status = SppCopyKeyRecursive( SrcKey,
DstKey,
NULL,
DstPrinterKeyName,
FALSE,
TRUE
);
if( !NT_SUCCESS( Status ) ) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to migrate %ls. Status = %lx\n", SrcPrinterKeyPath, Status));
}
ZwClose( SrcKey );
ZwClose( DstKey );
//
// If the key was migrated successfully, then attempt to delete the source key.
// if we are unable to delete the key, then silently fail.
//
if( NT_SUCCESS( Status ) ) {
NTSTATUS Status1;
PWSTR q, r;
//
// q will point to "Control\Print"
// r will point to "Printers"
//
q = SpDupStringW( SrcPrinterKeyPath );
r = wcsrchr ( q, L'\\' );
*r = L'\0';
r++;
INIT_OBJA(&Obja,&UnicodeString,q);
Obja.RootDirectory = hControlSet;
Status1 = ZwOpenKey(&SrcKey,KEY_ALL_ACCESS,&Obja);
if( NT_SUCCESS( Status1 ) ) {
Status1 = SppDeleteKeyRecursive(SrcKey,
r,
TRUE);
if( !NT_SUCCESS( Status1 ) ) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to delete %ls\\%ls. Status = %lx\n", q, r, Status1));
}
ZwClose( SrcKey );
} else {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to delete %ls. ZwOpenKey() failed. Status = %lx\n", SrcPrinterKeyPath, Status1));
}
SpMemFree(q);
}
return( Status );
}