You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3638 lines
132 KiB
3638 lines
132 KiB
/*++
|
|
|
|
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
|
|
// we want this as hhll
|
|
//
|
|
MangledVersion = (DWORD)SpStringToLong( Value, NULL, 16 );
|
|
|
|
OsVersion = RtlUshortByteSwap((USHORT)MangledVersion) & 0xffff;
|
|
} 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.
|
|
//
|
|
for(SectionIndex = 0; DeviceClassesToDelete[SectionIndex].SectionName; SectionIndex++) {
|
|
if((!(DeviceClassesToDelete[SectionIndex].SectionFlags & OsFlags))
|
|
|| (OsVersion < DeviceClassesToDelete[SectionIndex].VerLow)
|
|
|| (OsVersion > DeviceClassesToDelete[SectionIndex].VerHigh)) {
|
|
//
|
|
// not interesting
|
|
//
|
|
continue;
|
|
}
|
|
for(LineIndex = 0;
|
|
ClassGuidToDelete = SpGetSectionLineIndex(SifHandle,
|
|
DeviceClassesToDelete[SectionIndex].SectionName,
|
|
LineIndex,
|
|
0);
|
|
LineIndex++) {
|
|
//
|
|
// Compare the two GUID strings.
|
|
//
|
|
if(!_wcsicmp(ClassGuidToDelete, (PWCHAR)(KeyValueInfo->Data))) {
|
|
//
|
|
// 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 );
|
|
}
|