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.
3431 lines
104 KiB
3431 lines
104 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
ppcddb.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Plug and Play Critical Device Database (CDDB)
|
|
and related "features".
|
|
|
|
Author:
|
|
|
|
James G. Cavalaris (jamesca) 01-Nov-2001
|
|
|
|
Environment:
|
|
|
|
Kernel mode.
|
|
|
|
Revision History:
|
|
|
|
29-Jul-1997 Jim Cavalaris (t-jcaval)
|
|
|
|
Creation and initial implementation.
|
|
|
|
01-Nov-2001 Jim Cavalaris (jamesca)
|
|
|
|
Added routines for device pre-installation setup.
|
|
|
|
--*/
|
|
|
|
#include "pnpmgrp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <wdmguid.h>
|
|
#include "picddb.h"
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'dcpP')
|
|
#endif
|
|
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGEDATA")
|
|
#pragma const_seg("PAGECONST")
|
|
#endif // ALLOC_DATA_PRAGMA
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PpCriticalProcessCriticalDevice)
|
|
#pragma alloc_text(PAGE, PpCriticalGetDeviceLocationStrings)
|
|
|
|
#pragma alloc_text(PAGE, PiCriticalOpenCriticalDeviceKey)
|
|
#pragma alloc_text(PAGE, PiCriticalCopyCriticalDeviceProperties)
|
|
#pragma alloc_text(PAGE, PiCriticalPreInstallDevice)
|
|
#pragma alloc_text(PAGE, PiCriticalOpenDevicePreInstallKey)
|
|
#pragma alloc_text(PAGE, PiCriticalOpenFirstMatchingSubKey)
|
|
#pragma alloc_text(PAGE, PiCriticalCallbackVerifyCriticalEntry)
|
|
|
|
#pragma alloc_text(PAGE, PiQueryInterface)
|
|
#pragma alloc_text(PAGE, PiCopyKeyRecursive)
|
|
#pragma alloc_text(PAGE, PiCriticalQueryRegistryValueCallback)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
typedef struct _PI_CRITICAL_QUERY_CONTEXT {
|
|
PVOID Buffer;
|
|
ULONG Size;
|
|
}PI_CRITICAL_QUERY_CONTEXT, *PPI_CRITICAL_QUERY_CONTEXT;
|
|
|
|
//
|
|
// Critical Device Database data
|
|
//
|
|
|
|
//
|
|
// Specifies whether the critical device database functionality is enabled.
|
|
// (currently always TRUE).
|
|
//
|
|
|
|
BOOLEAN PiCriticalDeviceDatabaseEnabled = TRUE;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Critical Device Database routines
|
|
//
|
|
|
|
NTSTATUS
|
|
PiCriticalOpenCriticalDeviceKey(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN HANDLE CriticalDeviceDatabaseRootHandle OPTIONAL,
|
|
OUT PHANDLE CriticalDeviceEntryHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the registry key containing the critical device
|
|
settings for the specified device.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode -
|
|
|
|
Specifies the device whose critical settings are to be retrieved.
|
|
|
|
CriticalDeviceDatabaseRootHandle -
|
|
|
|
Optionally, specifies a handle to the key that should be considered the
|
|
root of the critical device database to be searched for this device.
|
|
|
|
If no handle is supplied, the default critical device database is used:
|
|
|
|
System\\CurrentControlSet\\Control\\CriticalDeviceDatabase
|
|
|
|
CriticalDeviceEntryHandle -
|
|
|
|
Returns a handle to the registry key containing critical device settings
|
|
for the specified device.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, tmpStatus;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE DeviceInstanceHandle;
|
|
PWSTR SearchIds[2];
|
|
ULONG SearchIdsIndex;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInfo;
|
|
PWCHAR DeviceIds;
|
|
HANDLE DatabaseRootHandle;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
if ((!ARGUMENT_PRESENT(DeviceNode)) ||
|
|
(!ARGUMENT_PRESENT(CriticalDeviceEntryHandle))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Initialize output parameter.
|
|
//
|
|
*CriticalDeviceEntryHandle = NULL;
|
|
|
|
if (CriticalDeviceDatabaseRootHandle != NULL) {
|
|
//
|
|
// We were given a root database to be searched.
|
|
//
|
|
DatabaseRootHandle = CriticalDeviceDatabaseRootHandle;
|
|
|
|
} else {
|
|
//
|
|
// No root database handle supplied, so we open a key to the default
|
|
// global critical device database root.
|
|
//
|
|
PiWstrToUnicodeString(
|
|
&UnicodeString,
|
|
CM_REGISTRY_MACHINE(REGSTR_PATH_CRITICALDEVICEDATABASE));
|
|
|
|
Status =
|
|
IopOpenRegistryKeyEx(
|
|
&DatabaseRootHandle,
|
|
NULL,
|
|
&UnicodeString,
|
|
KEY_READ);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
ASSERT(DatabaseRootHandle != NULL);
|
|
|
|
//
|
|
// Open the device instance registry key.
|
|
//
|
|
DeviceInstanceHandle = NULL;
|
|
|
|
Status =
|
|
IopDeviceObjectToDeviceInstance(
|
|
DeviceNode->PhysicalDeviceObject,
|
|
&DeviceInstanceHandle,
|
|
KEY_READ);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(DeviceInstanceHandle == NULL);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(DeviceInstanceHandle != NULL);
|
|
|
|
//
|
|
// Search for a match for this device in the critical device database first
|
|
// by HardwareId, then by CompatibleId.
|
|
//
|
|
SearchIds[0] = REGSTR_VALUE_HARDWAREID;
|
|
SearchIds[1] = REGSTR_VALUE_COMPATIBLEIDS;
|
|
|
|
for (SearchIdsIndex = 0;
|
|
SearchIdsIndex < RTL_NUMBER_OF(SearchIds);
|
|
SearchIdsIndex++) {
|
|
|
|
//
|
|
// Retrieve the SearchIds for the device.
|
|
//
|
|
// NOTE - we currently retrieve these hardware and compatible ids from
|
|
// the device instance registry key, so they need to have been written
|
|
// there by now, during enumeration. If a critical device database
|
|
// match for a device is expected to be found prior to that, these
|
|
// properties should be queried directly form the device instead.
|
|
//
|
|
keyValueInfo = NULL;
|
|
|
|
Status =
|
|
IopGetRegistryValue(
|
|
DeviceInstanceHandle,
|
|
SearchIds[SearchIdsIndex],
|
|
&keyValueInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(keyValueInfo == NULL);
|
|
continue;
|
|
}
|
|
|
|
ASSERT(keyValueInfo != NULL);
|
|
|
|
//
|
|
// Make sure the returned registry value is a multi-sz.
|
|
//
|
|
if (keyValueInfo->Type != REG_MULTI_SZ) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
ExFreePool(keyValueInfo);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Munge all search ids in the multi-sz list.
|
|
//
|
|
|
|
DeviceIds = (PWCHAR)KEY_VALUE_DATA(keyValueInfo);
|
|
|
|
UnicodeString.Buffer = DeviceIds;
|
|
UnicodeString.Length = (USHORT)keyValueInfo->DataLength;
|
|
UnicodeString.MaximumLength = UnicodeString.Length;
|
|
|
|
tmpStatus =
|
|
IopReplaceSeperatorWithPound(
|
|
&UnicodeString,
|
|
&UnicodeString
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(tmpStatus));
|
|
|
|
//
|
|
// Check each munged device id for a match in the
|
|
// CriticalDeviceDatabase, by attempting to open the first matching
|
|
// subkey.
|
|
//
|
|
// Use PiCriticalCallbackVerifyCriticalEntry to determine if a matching
|
|
// subkey satisfies additional match requirements.
|
|
//
|
|
// NOTE: 01-Dec-2001 : Jim Cavalaris (jamesca)
|
|
//
|
|
// We do this because the previous implementation of the Critical Device
|
|
// Database match code would search all matching subkeys until it found
|
|
// one with a valid Service. This may be because matches may not have
|
|
// been found in the most appropriate order of hw-id/compat-ids, by
|
|
// decreasing relevance. Now that we do so, we should hopefully not
|
|
// need to resort to a less-relevant database match with a service, over
|
|
// a more specific one. A match with no service should mean none is
|
|
// required. That however, would involve allowing devices to go through
|
|
// the critical device database, and receive no Service match when we
|
|
// might possibly find one - something we may not have done before.
|
|
//
|
|
// Until all these issues are sorted out, we'll just use a verification
|
|
// callback routine to implement the logic that has always been there -
|
|
// check for Service and ClassGUID entry values before declaring an
|
|
// entry a match. If we want to change the behavior of what is
|
|
// considered a match, just change the callback routine - OR - provide
|
|
// no callback routine to simply declare the the first matching subkey
|
|
// name as a match.
|
|
//
|
|
|
|
Status =
|
|
PiCriticalOpenFirstMatchingSubKey(
|
|
DeviceIds,
|
|
DatabaseRootHandle,
|
|
KEY_READ,
|
|
(PCRITICAL_MATCH_CALLBACK)PiCriticalCallbackVerifyCriticalEntry,
|
|
CriticalDeviceEntryHandle
|
|
);
|
|
|
|
ExFreePool(keyValueInfo);
|
|
|
|
//
|
|
// Stop if we found a match in this list of device ids.
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
ASSERT(*CriticalDeviceEntryHandle != NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the device instance registry key handle.
|
|
//
|
|
ZwClose(DeviceInstanceHandle);
|
|
|
|
Clean0:
|
|
|
|
//
|
|
// If we opened our own key to the database root, close it now.
|
|
//
|
|
if ((CriticalDeviceDatabaseRootHandle == NULL) &&
|
|
(DatabaseRootHandle != NULL)) {
|
|
ZwClose(DatabaseRootHandle);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PiCriticalOpenCriticalDeviceKey
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PiCriticalCopyCriticalDeviceProperties(
|
|
IN HANDLE DeviceInstanceHandle,
|
|
IN HANDLE CriticalDeviceEntryHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will copy the Service, ClassGUID, LowerFilters and UpperFilters
|
|
device registry properties from the matching database entry to the device
|
|
instance registry key.
|
|
|
|
Arguments:
|
|
|
|
DeviceInstanceHandle -
|
|
|
|
Specifies a handle to the device instance key that is to be populated
|
|
with critical entries from the critical device database.
|
|
|
|
CriticalDeviceEntryHandle -
|
|
|
|
Specifies a handle to the matching critical device database entry that
|
|
contains critical device instance registry values to populate.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
** Values places in a given critical device database entry must be
|
|
applicable to ALL INSTANCES OF A MATCHING DEVICE ID.
|
|
|
|
|
|
** Specifically, you MUST NOT write/copy values that are SPECIFIC TO A
|
|
SINGLE INSTANCE OF A DEVICE to/from a critical device database entry.
|
|
|
|
The "hands-off" list includes (but is not restricted to)
|
|
instance-specific values such as:
|
|
|
|
REGSTR_VALUE_DRIVER ("Driver")
|
|
REGSTR_VAL_LOCATION_INFORMATION ("LocationInformation")
|
|
REGSTR_VALUE_PARENT_ID_PREFIX ("ParentIdPrefix")
|
|
REGSTR_VALUE_UNIQUE_PARENT_ID ("UniqueParentID")
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, tmpStatus;
|
|
RTL_QUERY_REGISTRY_TABLE QueryParameters[9];
|
|
UNICODE_STRING Service, ClassGuid, LowerFilters, UpperFilters;
|
|
UNICODE_STRING UnicodeValueName;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueFullInfo;
|
|
ULONG DeviceType, Characteristics, Exclusive, dummy;
|
|
PI_CRITICAL_QUERY_CONTEXT SecurityContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
if ((DeviceInstanceHandle == NULL) ||
|
|
(CriticalDeviceEntryHandle == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Query registry values from the matching critical device database entry.
|
|
//
|
|
// Initialize unicode strings with NULL Buffers.
|
|
// RTL_QUERY_REGISTRY_DIRECT will allocate buffers as necessary.
|
|
//
|
|
PiWstrToUnicodeString(&Service, NULL);
|
|
PiWstrToUnicodeString(&ClassGuid, NULL);
|
|
PiWstrToUnicodeString(&LowerFilters, NULL);
|
|
PiWstrToUnicodeString(&UpperFilters, NULL);
|
|
|
|
DeviceType = 0;
|
|
Exclusive = 0;
|
|
Characteristics = 0;
|
|
dummy = 0;
|
|
SecurityContext.Buffer = NULL;
|
|
SecurityContext.Size = 0;
|
|
|
|
//
|
|
// RTL_QUERY_REGISTRY_DIRECT uses system provided QueryRoutine.
|
|
// Look at the DDK documentation for more details on this flag.
|
|
//
|
|
RtlZeroMemory(
|
|
QueryParameters,
|
|
sizeof(QueryParameters)
|
|
);
|
|
|
|
QueryParameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
QueryParameters[0].Name = REGSTR_VALUE_SERVICE;
|
|
QueryParameters[0].EntryContext = &Service;
|
|
QueryParameters[0].DefaultType = REG_SZ;
|
|
QueryParameters[0].DefaultData = L"";
|
|
QueryParameters[0].DefaultLength = 0;
|
|
|
|
QueryParameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
QueryParameters[1].Name = REGSTR_VALUE_CLASSGUID;
|
|
QueryParameters[1].EntryContext = &ClassGuid;
|
|
QueryParameters[1].DefaultType = REG_SZ;
|
|
QueryParameters[1].DefaultData = L"";
|
|
QueryParameters[1].DefaultLength = 0;
|
|
|
|
QueryParameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND;
|
|
QueryParameters[2].Name = REGSTR_VALUE_LOWERFILTERS;
|
|
QueryParameters[2].EntryContext = &LowerFilters;
|
|
QueryParameters[2].DefaultType = REG_MULTI_SZ;
|
|
QueryParameters[2].DefaultData = L"";
|
|
QueryParameters[2].DefaultLength = 0;
|
|
|
|
QueryParameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND;
|
|
QueryParameters[3].Name = REGSTR_VALUE_UPPERFILTERS;
|
|
QueryParameters[3].EntryContext = &UpperFilters;
|
|
QueryParameters[3].DefaultType = REG_MULTI_SZ;
|
|
QueryParameters[3].DefaultData = L"";
|
|
QueryParameters[3].DefaultLength = 0;
|
|
|
|
QueryParameters[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
QueryParameters[4].Name = REGSTR_VAL_DEVICE_TYPE;
|
|
QueryParameters[4].EntryContext = &DeviceType;
|
|
QueryParameters[4].DefaultType = REG_DWORD;
|
|
QueryParameters[4].DefaultData = &dummy;
|
|
QueryParameters[4].DefaultLength = sizeof(DeviceType);
|
|
|
|
QueryParameters[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
QueryParameters[5].Name = REGSTR_VAL_DEVICE_EXCLUSIVE;
|
|
QueryParameters[5].EntryContext = &Exclusive;
|
|
QueryParameters[5].DefaultType = REG_DWORD;
|
|
QueryParameters[5].DefaultData = &dummy;
|
|
QueryParameters[5].DefaultLength = sizeof(Exclusive);
|
|
|
|
QueryParameters[6].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
QueryParameters[6].Name = REGSTR_VAL_DEVICE_CHARACTERISTICS;
|
|
QueryParameters[6].EntryContext = &Characteristics;
|
|
QueryParameters[6].DefaultType = REG_DWORD;
|
|
QueryParameters[6].DefaultData = &dummy;
|
|
QueryParameters[6].DefaultLength = sizeof(Characteristics);
|
|
|
|
QueryParameters[7].QueryRoutine = PiCriticalQueryRegistryValueCallback;
|
|
QueryParameters[7].Name = REGSTR_VAL_DEVICE_SECURITY_DESCRIPTOR;
|
|
QueryParameters[7].EntryContext = &SecurityContext;
|
|
QueryParameters[7].DefaultType = REG_BINARY;
|
|
QueryParameters[7].DefaultData = NULL;
|
|
QueryParameters[7].DefaultLength = 0;
|
|
|
|
Status =
|
|
RtlQueryRegistryValues(
|
|
RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
|
|
(PWSTR)CriticalDeviceEntryHandle,
|
|
QueryParameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// If successful so far, fix up some values, as needed.
|
|
//
|
|
|
|
if ((Service.Length == 0) &&
|
|
(Service.Buffer != NULL)) {
|
|
//
|
|
// Don't write an empty Service string.
|
|
//
|
|
RtlFreeUnicodeString(&Service);
|
|
PiWstrToUnicodeString(&Service, NULL);
|
|
}
|
|
|
|
if ((ClassGuid.Length == 0) &&
|
|
(ClassGuid.Buffer != NULL)) {
|
|
//
|
|
// Don't write an empty ClassGUID string.
|
|
//
|
|
RtlFreeUnicodeString(&ClassGuid);
|
|
PiWstrToUnicodeString(&ClassGuid, NULL);
|
|
}
|
|
|
|
if ((UpperFilters.Length <= sizeof(UNICODE_NULL)) &&
|
|
(UpperFilters.Buffer != NULL)) {
|
|
//
|
|
// Don't write empty UpperFilter multi-sz values.
|
|
//
|
|
RtlFreeUnicodeString(&UpperFilters);
|
|
PiWstrToUnicodeString(&UpperFilters, NULL);
|
|
}
|
|
|
|
if ((LowerFilters.Length <= sizeof(UNICODE_NULL)) &&
|
|
(LowerFilters.Buffer != NULL)) {
|
|
//
|
|
// Don't write empty LowerFilter multi-sz values.
|
|
//
|
|
RtlFreeUnicodeString(&LowerFilters);
|
|
PiWstrToUnicodeString(&LowerFilters, NULL);
|
|
}
|
|
|
|
//
|
|
// Set the critical device registry property values only if we have a
|
|
// Service value to set for the device.
|
|
//
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_WARNING_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"Setting up critical service\n"));
|
|
|
|
//
|
|
// NOTE: The PiCriticalCallbackVerifyCriticalEntry critical database entry
|
|
// verification callback should never validate a critical device database
|
|
// entry with no REGSTR_VALUE_SERVICE value.
|
|
//
|
|
|
|
if (Service.Buffer != NULL) {
|
|
//
|
|
// Set the "Service" device registry property.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VALUE_SERVICE);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ: %wZ\n",
|
|
&UnicodeValueName,
|
|
&Service));
|
|
|
|
ASSERT(DeviceInstanceHandle != NULL);
|
|
|
|
//
|
|
// Use the status from attempting to set the Service value as the
|
|
// final status of the critical settings copy operation.
|
|
//
|
|
|
|
Status =
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
Service.Buffer,
|
|
Service.Length + sizeof(UNICODE_NULL)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"Error setting %wZ, (Status = %#08lx)\n",
|
|
&UnicodeValueName, Status));
|
|
}
|
|
|
|
|
|
} else {
|
|
//
|
|
// No Service value to set is considered a failure of the entire
|
|
// critical settings copy operation.
|
|
//
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"No Service for critical entry!\n"));
|
|
|
|
//
|
|
// NOTE: We should never encounter this situation because the
|
|
// PiCriticalCallbackVerifyCriticalEntry critical database entry
|
|
// verification callback should never validate a critical device
|
|
// database entry with no Service value, hence the ASSERT.
|
|
//
|
|
|
|
ASSERT(Service.Buffer != NULL);
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
//
|
|
// If not successful setting up the service for this device, do not set the
|
|
// other critical settings.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Set the "ClassGUID" device registry property.
|
|
//
|
|
|
|
if (ClassGuid.Buffer != NULL) {
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VALUE_CLASSGUID);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ: %wZ\n",
|
|
&UnicodeValueName,
|
|
&ClassGuid));
|
|
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
ClassGuid.Buffer,
|
|
ClassGuid.Length + sizeof(UNICODE_NULL)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set the "LowerFilters" device registry property.
|
|
//
|
|
|
|
if (LowerFilters.Buffer != NULL) {
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VALUE_LOWERFILTERS);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ:\n",
|
|
&UnicodeValueName));
|
|
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_MULTI_SZ,
|
|
LowerFilters.Buffer,
|
|
LowerFilters.Length
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set the "UpperFilters" device registry property.
|
|
//
|
|
|
|
if (UpperFilters.Buffer != NULL) {
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VALUE_UPPERFILTERS);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ:\n",
|
|
&UnicodeValueName));
|
|
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_MULTI_SZ,
|
|
UpperFilters.Buffer,
|
|
UpperFilters.Length
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set "DeviceType" device registry property.
|
|
//
|
|
|
|
if (DeviceType) {
|
|
|
|
//
|
|
// Set the "DeviceType" device registry property.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VAL_DEVICE_TYPE);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ: %X\n",
|
|
&UnicodeValueName,
|
|
DeviceType));
|
|
|
|
//
|
|
// Use the status from attempting to set the DeviceType value as the
|
|
// final status of the critical settings copy operation.
|
|
//
|
|
|
|
Status =
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&DeviceType,
|
|
sizeof(DeviceType)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"Error setting %wZ, (Status = %#08lx)\n",
|
|
&UnicodeValueName, Status));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If not successful setting up the DeviceType for this device, do not set the
|
|
// other critical settings.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Set "Exclusive" device registry property.
|
|
//
|
|
|
|
if (Exclusive) {
|
|
|
|
//
|
|
// Set the "Exclusive" device registry property.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VAL_DEVICE_EXCLUSIVE);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ: %X\n",
|
|
&UnicodeValueName,
|
|
Exclusive));
|
|
|
|
//
|
|
// Use the status from attempting to set the Exclusive value as the
|
|
// final status of the critical settings copy operation.
|
|
//
|
|
|
|
Status =
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&Exclusive,
|
|
sizeof(Exclusive)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"Error setting %wZ, (Status = %#08lx)\n",
|
|
&UnicodeValueName, Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If not successful setting up the Exclusive for this device, do not set
|
|
// the other critical settings.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Set "Characteristics" device registry property.
|
|
//
|
|
|
|
if (Characteristics) {
|
|
|
|
//
|
|
// Set the "Characteristics" device registry property.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VAL_DEVICE_CHARACTERISTICS);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ: %X\n",
|
|
&UnicodeValueName,
|
|
Characteristics));
|
|
|
|
//
|
|
// Use the status from attempting to set the Characteristics value as the
|
|
// final status of the critical settings copy operation.
|
|
//
|
|
|
|
Status =
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&Characteristics,
|
|
sizeof(Characteristics)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"Error setting %wZ, (Status = %#08lx)\n",
|
|
&UnicodeValueName, Status));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If not successful setting up the Characteristics for this device, do not
|
|
// set the other critical settings.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Clean0;
|
|
}
|
|
|
|
if (SecurityContext.Buffer) {
|
|
|
|
//
|
|
// Set the "Security" device registry property.
|
|
//
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VAL_DEVICE_SECURITY_DESCRIPTOR);
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"%wZ\n",
|
|
&UnicodeValueName));
|
|
|
|
//
|
|
// Use the status from attempting to set the Security value as the
|
|
// final status of the critical settings copy operation.
|
|
//
|
|
|
|
Status =
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
SecurityContext.Buffer,
|
|
SecurityContext.Size
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_INFO_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"Error setting %wZ, (Status = %#08lx)\n",
|
|
&UnicodeValueName, Status));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If not successful setting up the Characteristics for this device, do not
|
|
// set the other critical settings.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Now, check the critical device entry registry key for the flag that
|
|
// indicates that the device settings are complete, and should be respected
|
|
// by user-mode device installation. If it exists, set that value on the
|
|
// device instance registry key for the device whose critical settings we
|
|
// are copying.
|
|
//
|
|
|
|
keyValueFullInfo = NULL;
|
|
|
|
tmpStatus =
|
|
IopGetRegistryValue(
|
|
CriticalDeviceEntryHandle,
|
|
REGSTR_VAL_PRESERVE_PREINSTALL,
|
|
&keyValueFullInfo);
|
|
|
|
if (NT_SUCCESS(tmpStatus)) {
|
|
|
|
ASSERT(keyValueFullInfo != NULL);
|
|
ASSERT(keyValueFullInfo->Type == REG_DWORD);
|
|
ASSERT(keyValueFullInfo->DataLength == sizeof(ULONG));
|
|
|
|
if ((keyValueFullInfo->Type == REG_DWORD) &&
|
|
(keyValueFullInfo->DataLength == sizeof(ULONG))) {
|
|
|
|
//
|
|
// Write the value to the device instance registry key.
|
|
//
|
|
|
|
PiWstrToUnicodeString(
|
|
&UnicodeValueName,
|
|
REGSTR_VAL_PRESERVE_PREINSTALL);
|
|
|
|
tmpStatus =
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
keyValueFullInfo->TitleIndex,
|
|
keyValueFullInfo->Type,
|
|
(PVOID)((PUCHAR)keyValueFullInfo + keyValueFullInfo->DataOffset),
|
|
keyValueFullInfo->DataLength);
|
|
|
|
if (!NT_SUCCESS(tmpStatus)) {
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalCopyCriticalDeviceProperties: "
|
|
"Unable to set %wZ value to instance key.\n",
|
|
&UnicodeValueName));
|
|
}
|
|
}
|
|
|
|
ExFreePool(keyValueFullInfo);
|
|
}
|
|
|
|
Clean0:
|
|
|
|
//
|
|
// Free any allocated unicode strings.
|
|
// (RtlFreeUnicodeString can handle NULL strings)
|
|
//
|
|
|
|
RtlFreeUnicodeString(&Service);
|
|
RtlFreeUnicodeString(&ClassGuid);
|
|
RtlFreeUnicodeString(&LowerFilters);
|
|
RtlFreeUnicodeString(&UpperFilters);
|
|
|
|
if (SecurityContext.Buffer) {
|
|
|
|
ExFreePool(SecurityContext.Buffer);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PiCriticalCopyCriticalDeviceProperties
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PpCriticalProcessCriticalDevice(
|
|
IN PDEVICE_NODE DeviceNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks the critical device database for a match against one of
|
|
the device's hardware or compatible ids. If a device is found, then it will
|
|
be assigned a Service, ClassGUID, and potentiall LowerFilters and
|
|
UpperFilters, and based on the contents of the matching database entry.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode -
|
|
|
|
Specifies the device node to be processed via the critical device
|
|
database.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, tmpStatus;
|
|
HANDLE CriticalDeviceEntryHandle, DeviceInstanceHandle;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueFullInfo;
|
|
UNICODE_STRING UnicodeValueName;
|
|
ULONG ConfigFlags;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// First, make sure that the critical device database is currently enabled.
|
|
//
|
|
if (!PiCriticalDeviceDatabaseEnabled) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (!ARGUMENT_PRESENT(DeviceNode)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
CriticalDeviceEntryHandle = NULL;
|
|
DeviceInstanceHandle = NULL;
|
|
|
|
//
|
|
// Attempt to open the matching critical device entry key.
|
|
//
|
|
|
|
Status =
|
|
PiCriticalOpenCriticalDeviceKey(
|
|
DeviceNode,
|
|
NULL, // use default CriticalDeviceDatabase root key
|
|
&CriticalDeviceEntryHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(CriticalDeviceEntryHandle == NULL);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(CriticalDeviceEntryHandle != NULL);
|
|
|
|
//
|
|
// Open the device instance registry key.
|
|
//
|
|
|
|
Status =
|
|
IopDeviceObjectToDeviceInstance(
|
|
DeviceNode->PhysicalDeviceObject,
|
|
&DeviceInstanceHandle,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(DeviceInstanceHandle == NULL);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(DeviceInstanceHandle != NULL);
|
|
|
|
//
|
|
// Copy critical device entries for this device. The return status
|
|
// indicates that a Service was successfully set up for this device. Only
|
|
// in that case should we clear the device of any problems it may have.
|
|
//
|
|
|
|
Status =
|
|
PiCriticalCopyCriticalDeviceProperties(
|
|
DeviceInstanceHandle,
|
|
CriticalDeviceEntryHandle
|
|
);
|
|
|
|
//
|
|
// NOTE: The Status returned by this routine indicates whether the Service
|
|
// value was successfully copied to the device instance key. Only f we
|
|
// successfully processed this device as a critical device should we:
|
|
//
|
|
// - attempt pre-installation of settings (should not preinstall a device
|
|
// that could not be critically installed).
|
|
//
|
|
// - clear the reinstall and failed install config flags, and set the
|
|
// finish-install configflag (should not attempt to start the device
|
|
// otherwise).
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// First, attempt pre-installation of settings for devices matching an
|
|
// entry in the critical device database.
|
|
//
|
|
|
|
//
|
|
// Use the matching critical device database entry key for this device
|
|
// as the root of the preinstall database.
|
|
//
|
|
// i.e. <CriticalDeviceDatabaseRoot>\\<CriticalDeviceEntry>
|
|
//
|
|
// This allows pre-install settings settings to be applied to a device
|
|
// at a specific location, only if it matches some device id.
|
|
//
|
|
|
|
tmpStatus =
|
|
PiCriticalPreInstallDevice(
|
|
DeviceNode,
|
|
CriticalDeviceEntryHandle
|
|
);
|
|
|
|
if (NT_SUCCESS(tmpStatus)) {
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PpCriticalProcessCriticalDevice: "
|
|
"Pre-installation successfully completed for devnode %#08lx\n",
|
|
DeviceNode));
|
|
}
|
|
|
|
//
|
|
// Next, set the ConfigFlags appropriately.
|
|
//
|
|
|
|
//
|
|
// Initialize the ConfigFlags to 0, in case none exist yet.
|
|
//
|
|
|
|
ConfigFlags = 0;
|
|
|
|
//
|
|
// Retrieve the existing ConfigFlags for the device.
|
|
//
|
|
|
|
keyValueFullInfo = NULL;
|
|
|
|
tmpStatus =
|
|
IopGetRegistryValue(
|
|
DeviceInstanceHandle,
|
|
REGSTR_VALUE_CONFIG_FLAGS,
|
|
&keyValueFullInfo
|
|
);
|
|
|
|
//
|
|
// If ConfigFlags were successfully retrieved, use them instead.
|
|
//
|
|
|
|
if (NT_SUCCESS(tmpStatus)) {
|
|
|
|
ASSERT(keyValueFullInfo != NULL);
|
|
|
|
if (keyValueFullInfo->Type == REG_DWORD && keyValueFullInfo->DataLength == sizeof(ULONG)) {
|
|
ConfigFlags = *(PULONG)KEY_VALUE_DATA(keyValueFullInfo);
|
|
}
|
|
|
|
ExFreePool(keyValueFullInfo);
|
|
}
|
|
|
|
//
|
|
// Clear the "needs re-install" and "failed install" ConfigFlags.
|
|
//
|
|
|
|
ConfigFlags &= ~(CONFIGFLAG_REINSTALL | CONFIGFLAG_FAILEDINSTALL);
|
|
|
|
//
|
|
// Installation is not considered complete, so set
|
|
// CONFIGFLAG_FINISH_INSTALL so we will still get a new hw found popup
|
|
// and go through the class installer.
|
|
//
|
|
|
|
ConfigFlags |= CONFIGFLAG_FINISH_INSTALL;
|
|
|
|
PiWstrToUnicodeString(&UnicodeValueName, REGSTR_VALUE_CONFIG_FLAGS);
|
|
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&ConfigFlags,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// Make sure the device does not have any problems relating to
|
|
// either being not configured or not installed.
|
|
//
|
|
ASSERT(!PipDoesDevNodeHaveProblem(DeviceNode) ||
|
|
PipIsDevNodeProblem(DeviceNode, CM_PROB_NOT_CONFIGURED) ||
|
|
PipIsDevNodeProblem(DeviceNode, CM_PROB_FAILED_INSTALL) ||
|
|
PipIsDevNodeProblem(DeviceNode, CM_PROB_REINSTALL));
|
|
|
|
PipClearDevNodeProblem(DeviceNode);
|
|
}
|
|
|
|
Clean0:
|
|
|
|
if (CriticalDeviceEntryHandle != NULL) {
|
|
ZwClose(CriticalDeviceEntryHandle);
|
|
}
|
|
|
|
if (DeviceInstanceHandle != NULL) {
|
|
ZwClose(DeviceInstanceHandle);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PpCriticalProcessCriticalDevice
|
|
|
|
|
|
|
|
//
|
|
// Critical Device Database routines related to device pre-installation.
|
|
//
|
|
|
|
NTSTATUS
|
|
PiCriticalPreInstallDevice(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN HANDLE PreInstallDatabaseRootHandle OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to pre-install instance-specific settings for
|
|
non-configured devices that will be started based on information in
|
|
the Plug and Play Critical Device Database (CDDB).
|
|
|
|
It is intended to complement the CriticalDeviceDatabase by applying
|
|
instance-specific settings to a device, rather than the hardware-id /
|
|
compatible-id specific settings applied by the CriticalDeviceDatabase.
|
|
|
|
Matches for a specific device-instance are made by comparing device location
|
|
information returned by the device and its ancestors with pre-seeded
|
|
database entries of the same format.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode -
|
|
|
|
Specifies the device whose settings are to be pre-installed.
|
|
|
|
PreInstallDatabaseRootHandle -
|
|
|
|
Optionally, specifies a handle to the key that should be considered the
|
|
root of the pre-install database to be searched for this device.
|
|
|
|
This may be a handle to the key for the CriticalDeviceDatabase entry
|
|
that matched this device - OR - may be the root of a single database
|
|
that contains pre-install settings for all devices in the system.
|
|
|
|
If no handle is supplied, the default global pre-install database is
|
|
used:
|
|
|
|
System\\CurrentControlSet\\Control\\CriticalPreInstallDatabase
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status, tmpStatus;
|
|
HANDLE PreInstallHandle, DeviceInstanceHandle;
|
|
HANDLE DeviceHardwareKeyHandle, DeviceSoftwareKeyHandle;
|
|
HANDLE PreInstallHardwareKeyHandle, PreInstallSoftwareKeyHandle;
|
|
UNICODE_STRING UnicodeString;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueFullInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (!ARGUMENT_PRESENT(DeviceNode)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Open the device pre-install settings root key.
|
|
//
|
|
PreInstallHandle = NULL;
|
|
|
|
Status =
|
|
PiCriticalOpenDevicePreInstallKey(
|
|
DeviceNode,
|
|
PreInstallDatabaseRootHandle,
|
|
&PreInstallHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"No pre-install settings found for devnode %#08lx\n",
|
|
DeviceNode));
|
|
ASSERT(PreInstallHandle == NULL);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(PreInstallHandle != NULL);
|
|
|
|
//
|
|
// Open the pre-install settings Hardware subkey.
|
|
//
|
|
PiWstrToUnicodeString(&UnicodeString, _REGSTR_KEY_PREINSTALL_HARDWARE);
|
|
|
|
PreInstallHardwareKeyHandle = NULL;
|
|
|
|
Status =
|
|
IopOpenRegistryKeyEx(
|
|
&PreInstallHardwareKeyHandle,
|
|
PreInstallHandle,
|
|
&UnicodeString,
|
|
KEY_READ
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ASSERT(PreInstallHardwareKeyHandle != NULL);
|
|
|
|
//
|
|
// We have hardware settings to pre-install for this device, so open
|
|
// the device's hardware key.
|
|
//
|
|
DeviceHardwareKeyHandle = NULL;
|
|
|
|
Status =
|
|
IoOpenDeviceRegistryKey(
|
|
DeviceNode->PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_ALL_ACCESS,
|
|
&DeviceHardwareKeyHandle);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"DeviceHardwareKeyHandle (%#08lx) successfully opened for devnode %#08lx\n",
|
|
DeviceHardwareKeyHandle, DeviceNode));
|
|
ASSERT(DeviceHardwareKeyHandle != NULL);
|
|
|
|
//
|
|
// Copy the pre-install hardware settings to the device's
|
|
// hardware key.
|
|
//
|
|
// NOTE that we specify that existing hardware settings for the
|
|
// device should NOT be replaced with values from the pre-install
|
|
// database. This is because:
|
|
//
|
|
// - If the device is truly being installed from scratch, then it
|
|
// will have no pre-existing values in this key, and all values
|
|
// will be copied from the pre-install database anyways.
|
|
//
|
|
// - If the device does happen to have pre-existing settings, but
|
|
// happened to get processed by the CDDB for some wacky reason
|
|
// like it was just missing ConfigFlags, we don't want to
|
|
// override them, just add to them.
|
|
//
|
|
Status =
|
|
PiCopyKeyRecursive(
|
|
PreInstallHardwareKeyHandle, // SourceKey
|
|
DeviceHardwareKeyHandle, // TargetKey
|
|
NULL,
|
|
NULL,
|
|
FALSE, // CopyAlways
|
|
FALSE // ApplyACLsAlways
|
|
);
|
|
|
|
ZwClose(DeviceHardwareKeyHandle);
|
|
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"DeviceHardwareKeyHandle was NOT successfully opened for devnode %#08lx\n",
|
|
DeviceNode));
|
|
ASSERT(DeviceHardwareKeyHandle == NULL);
|
|
}
|
|
|
|
ZwClose(PreInstallHardwareKeyHandle);
|
|
|
|
} else {
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"No hardware pre-install settings found for devnode %#08lx\n",
|
|
DeviceNode));
|
|
ASSERT(PreInstallHardwareKeyHandle == NULL);
|
|
}
|
|
|
|
//
|
|
// Open the pre-install settings Software subkey.
|
|
//
|
|
PiWstrToUnicodeString(&UnicodeString, _REGSTR_KEY_PREINSTALL_SOFTWARE);
|
|
|
|
PreInstallSoftwareKeyHandle = NULL;
|
|
|
|
Status =
|
|
IopOpenRegistryKeyEx(
|
|
&PreInstallSoftwareKeyHandle,
|
|
PreInstallHandle,
|
|
&UnicodeString,
|
|
KEY_READ
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ASSERT(PreInstallSoftwareKeyHandle != NULL);
|
|
|
|
//
|
|
// We have software settings to pre-install for this device, so
|
|
// open/create the device's software key.
|
|
//
|
|
DeviceSoftwareKeyHandle = NULL;
|
|
|
|
Status =
|
|
IopOpenOrCreateDeviceRegistryKey(
|
|
DeviceNode->PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
KEY_ALL_ACCESS,
|
|
TRUE,
|
|
&DeviceSoftwareKeyHandle);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"DeviceSoftwareKeyHandle (%#08lx) successfully opened for devnode %#08lx\n",
|
|
DeviceSoftwareKeyHandle, DeviceNode));
|
|
ASSERT(DeviceSoftwareKeyHandle != NULL);
|
|
|
|
//
|
|
// Copy the pre-install software settings to the device's
|
|
// software key.
|
|
//
|
|
// NOTE that we specify that existing software settings for the
|
|
// device should NOT be replaced with values from the pre-install
|
|
// database. This is because:
|
|
//
|
|
// - If the device is truly being installed from scratch, then it
|
|
// will have no pre-existing values in this key, and all values
|
|
// will be copied from the pre-install database anyways.
|
|
//
|
|
// - If the device does happen to have pre-existing settings, but
|
|
// happened to get processed by the CDDB for some wacky reason
|
|
// like it was just missing ConfigFlags, we don't want to
|
|
// override them, just add to them.
|
|
//
|
|
Status =
|
|
PiCopyKeyRecursive(
|
|
PreInstallSoftwareKeyHandle, // SourceKey
|
|
DeviceSoftwareKeyHandle, // TargetKey
|
|
NULL,
|
|
NULL,
|
|
FALSE, // CopyAlways
|
|
FALSE // ApplyACLsAlways,
|
|
);
|
|
|
|
ZwClose(DeviceSoftwareKeyHandle);
|
|
|
|
} else {
|
|
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"DeviceSoftwareKeyHandle was NOT successfully opened for devnode %#08lx\n",
|
|
DeviceNode));
|
|
ASSERT(DeviceSoftwareKeyHandle == NULL);
|
|
}
|
|
|
|
ZwClose(PreInstallSoftwareKeyHandle);
|
|
|
|
} else {
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"No software pre-install settings found for devnode %#08lx\n",
|
|
DeviceNode));
|
|
ASSERT(PreInstallSoftwareKeyHandle == NULL);
|
|
}
|
|
|
|
//
|
|
// Now, check the device pre-install registry key for the flag that
|
|
// indicates that the pre-install settings are complete, and should be
|
|
// respected by user-mode device installation.
|
|
//
|
|
|
|
keyValueFullInfo = NULL;
|
|
|
|
tmpStatus =
|
|
IopGetRegistryValue(
|
|
PreInstallHandle,
|
|
REGSTR_VAL_PRESERVE_PREINSTALL,
|
|
&keyValueFullInfo);
|
|
|
|
if (NT_SUCCESS(tmpStatus)) {
|
|
|
|
ASSERT(keyValueFullInfo != NULL);
|
|
|
|
//
|
|
// Open the device instance registry key.
|
|
//
|
|
DeviceInstanceHandle = NULL;
|
|
|
|
tmpStatus =
|
|
IopDeviceObjectToDeviceInstance(
|
|
DeviceNode->PhysicalDeviceObject,
|
|
&DeviceInstanceHandle,
|
|
KEY_ALL_ACCESS);
|
|
|
|
if (NT_SUCCESS(tmpStatus)) {
|
|
|
|
ASSERT(DeviceInstanceHandle != NULL);
|
|
|
|
//
|
|
// Write the value to the device instance registry key.
|
|
//
|
|
|
|
PiWstrToUnicodeString(
|
|
&UnicodeString,
|
|
REGSTR_VAL_PRESERVE_PREINSTALL);
|
|
|
|
tmpStatus =
|
|
ZwSetValueKey(
|
|
DeviceInstanceHandle,
|
|
&UnicodeString,
|
|
keyValueFullInfo->TitleIndex,
|
|
keyValueFullInfo->Type,
|
|
(PVOID)((PUCHAR)keyValueFullInfo + keyValueFullInfo->DataOffset),
|
|
keyValueFullInfo->DataLength);
|
|
|
|
if (!NT_SUCCESS(tmpStatus)) {
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"Unable to set %wZ value in instance key for devnode %#08lx\n",
|
|
&UnicodeString, DeviceNode));
|
|
}
|
|
|
|
ZwClose(DeviceInstanceHandle);
|
|
|
|
} else {
|
|
IopDbgPrint((IOP_ENUMERATION_VERBOSE_LEVEL,
|
|
"PiCriticalPreInstallDevice: "
|
|
"Unable to open device instance key for devnode %#08lx\n",
|
|
DeviceNode));
|
|
ASSERT(DeviceInstanceHandle == NULL);
|
|
}
|
|
|
|
ExFreePool(keyValueFullInfo);
|
|
}
|
|
|
|
ZwClose(PreInstallHandle);
|
|
|
|
Clean0:
|
|
|
|
return Status;
|
|
|
|
} // PiCriticalPreInstallDevice
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PiCriticalOpenDevicePreInstallKey(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN HANDLE PreInstallDatabaseRootHandle OPTIONAL,
|
|
OUT PHANDLE PreInstallHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the registry key containing the pre-install settings
|
|
for the specified device.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode -
|
|
|
|
Specifies the device whose pre-install settings are to be retrieved.
|
|
|
|
PreInstallDatabaseRootHandle -
|
|
|
|
Optionally, specifies a handle to the key that should be considered the
|
|
root of the pre-install database to be searched for this device.
|
|
|
|
This may be a handle to the key for the CriticalDeviceDatabase entry
|
|
that matched this device - OR - may be the root of a single database
|
|
that contains pre-install settings for all devices in the system.
|
|
|
|
PreInstallHandle -
|
|
|
|
Returns a handle to the registry key containing the pre-install settings
|
|
for the specified device.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE DatabaseRootHandle = NULL, DevicePathsHandle;
|
|
PWCHAR DeviceLocationStrings = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
if ((!ARGUMENT_PRESENT(DeviceNode)) ||
|
|
(!ARGUMENT_PRESENT(PreInstallHandle))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Initialize output parameter.
|
|
//
|
|
*PreInstallHandle = NULL;
|
|
|
|
if (PreInstallDatabaseRootHandle != NULL) {
|
|
//
|
|
// We were given a root database to be searched.
|
|
//
|
|
DatabaseRootHandle = PreInstallDatabaseRootHandle;
|
|
|
|
} else {
|
|
//
|
|
// No root database handle supplied, so we open a key to the default
|
|
// global device pre-install database root.
|
|
//
|
|
PiWstrToUnicodeString(
|
|
&UnicodeString,
|
|
CM_REGISTRY_MACHINE(_REGSTR_PATH_DEFAULT_PREINSTALL_DATABASE_ROOT));
|
|
|
|
Status =
|
|
IopOpenRegistryKeyEx(
|
|
&DatabaseRootHandle,
|
|
NULL,
|
|
&UnicodeString,
|
|
KEY_READ);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
ASSERT(DatabaseRootHandle != NULL);
|
|
|
|
//
|
|
// Open the DevicePaths subkey of the pre-install database root.
|
|
//
|
|
// This key contains subkeys which are device location paths for devices
|
|
// that may potentially be present on the system. These are devices whose
|
|
// settings we would like to pre-install, so that they may be used the very
|
|
// first time the device is started, rather than requiring the plug and play
|
|
// config manager, and/or user intervention.
|
|
//
|
|
PiWstrToUnicodeString(&UnicodeString, _REGSTR_KEY_DEVICEPATHS);
|
|
DevicePathsHandle = NULL;
|
|
|
|
Status =
|
|
IopOpenRegistryKeyEx(
|
|
&DevicePathsHandle,
|
|
DatabaseRootHandle,
|
|
&UnicodeString,
|
|
KEY_READ);
|
|
|
|
//
|
|
// If we opened our own key to the database root, close it now.
|
|
//
|
|
if ((PreInstallDatabaseRootHandle == NULL) &&
|
|
(DatabaseRootHandle != NULL)) {
|
|
ZwClose(DatabaseRootHandle);
|
|
}
|
|
|
|
//
|
|
// If unsuccessful opening the DevicePaths key, we're done.
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(DevicePathsHandle == NULL);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(DevicePathsHandle != NULL);
|
|
|
|
//
|
|
// Retrieve the multi-sz list of device location string paths for this
|
|
// device.
|
|
//
|
|
Status =
|
|
PpCriticalGetDeviceLocationStrings(
|
|
DeviceNode,
|
|
&DeviceLocationStrings
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(DeviceLocationStrings == NULL);
|
|
ZwClose(DevicePathsHandle);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(DeviceLocationStrings != NULL);
|
|
|
|
//
|
|
// Open the first matching subkey.
|
|
// No verification callback needed, the first match will do.
|
|
//
|
|
Status =
|
|
PiCriticalOpenFirstMatchingSubKey(
|
|
DeviceLocationStrings,
|
|
DevicePathsHandle,
|
|
KEY_READ,
|
|
(PCRITICAL_MATCH_CALLBACK)NULL,
|
|
PreInstallHandle
|
|
);
|
|
|
|
//
|
|
// Close the DevicePaths key.
|
|
//
|
|
ZwClose(DevicePathsHandle);
|
|
|
|
Clean0:
|
|
|
|
//
|
|
// Free the list of device location path strings, if we received one.
|
|
//
|
|
if (DeviceLocationStrings != NULL) {
|
|
ExFreePool(DeviceLocationStrings);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PiCriticalOpenDevicePreInstallKey
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PiCriticalOpenFirstMatchingSubKey(
|
|
IN PWCHAR MultiSzKeyNames,
|
|
IN HANDLE RootHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN PCRITICAL_MATCH_CALLBACK MatchingSubkeyCallback OPTIONAL,
|
|
OUT PHANDLE MatchingKeyHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the first subkey of the supplied root that matched a
|
|
string in the multi-sz list.
|
|
|
|
Arguments:
|
|
|
|
MultiSzKeyNames -
|
|
|
|
Supplies a multi-sz list of possible matching subkey names.
|
|
|
|
RootHandle -
|
|
|
|
Specifies a handle to the root key that should be searched for a
|
|
matching subkey.
|
|
|
|
DesiredAccess -
|
|
|
|
Specifies the desired access the matching subkey should be opened with,
|
|
if found.
|
|
|
|
MatchingSubkeyCallback -
|
|
|
|
Optionally, specifies a callback routine to be called with matching
|
|
subkeys to perform additional verification of potential subkey matches.
|
|
If the callback routine returns FALSE for a potential match, the subkey
|
|
is then considered NOT to be a match, and the search will continue.
|
|
|
|
MatchingKeyHandle -
|
|
|
|
Specifies the address of a variable to retrieve the open handle to the
|
|
first matching subkey.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PWSTR p;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
if ((!ARGUMENT_PRESENT(MultiSzKeyNames)) ||
|
|
(RootHandle == NULL) ||
|
|
(!ARGUMENT_PRESENT(MatchingKeyHandle))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Initialize output parameter.
|
|
//
|
|
*MatchingKeyHandle = NULL;
|
|
|
|
//
|
|
// Start with no match found yet, in case the multi-sz is empty.
|
|
//
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
//
|
|
// Check each string in the multi-sz list.
|
|
//
|
|
for (p = MultiSzKeyNames; *p != UNICODE_NULL; p += wcslen(p)+1) {
|
|
|
|
//
|
|
// Attempt to open a corresponding subkey of the root.
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString, p);
|
|
|
|
Status =
|
|
IopOpenRegistryKeyEx(
|
|
MatchingKeyHandle,
|
|
RootHandle,
|
|
&UnicodeString,
|
|
DesiredAccess);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ASSERT(*MatchingKeyHandle != NULL);
|
|
|
|
//
|
|
// We have a conditional match - check the MatchingSubkeyCallback
|
|
// for verification, if we have one.
|
|
//
|
|
|
|
if ((ARGUMENT_PRESENT(MatchingSubkeyCallback)) &&
|
|
(!(MatchingSubkeyCallback(*MatchingKeyHandle)))) {
|
|
|
|
//
|
|
// Not a match.
|
|
//
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
//
|
|
// Close the key and continue.
|
|
//
|
|
|
|
ZwClose(*MatchingKeyHandle);
|
|
*MatchingKeyHandle = NULL;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Match!
|
|
//
|
|
break;
|
|
}
|
|
|
|
ASSERT(*MatchingKeyHandle == NULL);
|
|
*MatchingKeyHandle = NULL;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ASSERT(*MatchingKeyHandle != NULL);
|
|
} else {
|
|
ASSERT(*MatchingKeyHandle == NULL);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PiCriticalOpenFirstMatchingSubKey
|
|
|
|
|
|
|
|
BOOLEAN
|
|
PiCriticalCallbackVerifyCriticalEntry(
|
|
IN HANDLE CriticalDeviceEntryHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback routine to verify that the specified critical
|
|
device database entry key can be used to supply critical device settings.
|
|
|
|
Arguments:
|
|
|
|
CriticalDeviceEntryHandle -
|
|
|
|
Specifies a handle to the registry key containing critical device
|
|
settings for the specified device.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the key conatins valid settings for a matching critical
|
|
device database entry, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueFullInfo;
|
|
ULONG DataType, DataLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
if (CriticalDeviceEntryHandle == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// For critical device database entries, a match is only a match if it
|
|
// contains a "Service" value.
|
|
//
|
|
keyValueFullInfo = NULL;
|
|
|
|
Status =
|
|
IopGetRegistryValue(CriticalDeviceEntryHandle,
|
|
REGSTR_VALUE_SERVICE,
|
|
&keyValueFullInfo);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(keyValueFullInfo == NULL);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(keyValueFullInfo != NULL);
|
|
|
|
DataType = keyValueFullInfo->Type;
|
|
DataLength = keyValueFullInfo->DataLength;
|
|
|
|
ExFreePool(keyValueFullInfo);
|
|
|
|
//
|
|
// Make sure the returned registry value is a non-null reg sz.
|
|
//
|
|
if ((DataType != REG_SZ) || (DataLength <= sizeof(UNICODE_NULL))) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// so far, so good...
|
|
//
|
|
|
|
//
|
|
// For critical device database entries, a match is only a match if it
|
|
// contains a valid "ClassGUID" value - or none at all.
|
|
//
|
|
keyValueFullInfo = NULL;
|
|
|
|
Status =
|
|
IopGetRegistryValue(
|
|
CriticalDeviceEntryHandle,
|
|
REGSTR_VALUE_CLASSGUID,
|
|
&keyValueFullInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ASSERT(keyValueFullInfo == NULL);
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
//
|
|
// No ClassGUID entry is considered a valid match.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(keyValueFullInfo != NULL);
|
|
|
|
DataType = keyValueFullInfo->Type;
|
|
DataLength = keyValueFullInfo->DataLength;
|
|
|
|
ExFreePool(keyValueFullInfo);
|
|
|
|
//
|
|
// Make sure the returned registry value is a reg-sz of the right size. The
|
|
// data must be at least as the length of the data for a stringified-GUID.
|
|
// No ClassGUID value is also valid, so a null reg-sz ClassGUID entry is
|
|
// also considered a valid match. Anything else is invalid, and shouldn't
|
|
// be used.
|
|
//
|
|
// NOTE: 01-Dec-2001 : Jim Cavalaris (jamesca)
|
|
//
|
|
// A ClassGUID value with Data that is too long for a stringified GUID
|
|
// will actually still be considered valid. We should fix this to
|
|
// consider only valid stringified GUIDs, but this is way this was done
|
|
// previously, so we won't change it for this release. Something to
|
|
// consider in the future.
|
|
//
|
|
if ((DataType != REG_SZ) ||
|
|
((DataLength < (GUID_STRING_LEN*sizeof(WCHAR)-sizeof(UNICODE_NULL))) &&
|
|
(DataLength > sizeof(UNICODE_NULL)))) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Clean0;
|
|
}
|
|
|
|
Clean0:
|
|
|
|
return ((BOOLEAN)NT_SUCCESS(Status));
|
|
|
|
} // PiCriticalCallbackVerifyCriticalEntry
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PpCriticalGetDeviceLocationStrings(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
OUT PWCHAR *DeviceLocationStrings
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the device location string for a device node.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode -
|
|
|
|
Specifies the device whose location strings are to be retrieved.
|
|
|
|
DeviceLocationStrings -
|
|
|
|
Returns a multi-sz string of the device location path strings, composed
|
|
from the set of location strings returned from each device in the
|
|
anscestry of the specified device.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
ULONG QueriedLocationStringsArraySize;
|
|
PWSTR *QueriedLocationStrings = NULL;
|
|
PULONG QueriedLocationStringsCount = NULL;
|
|
|
|
PNP_LOCATION_INTERFACE LocationInterface;
|
|
PWSTR TempMultiSz;
|
|
ULONG TempMultiSzLength;
|
|
|
|
PWSTR p, pdlp;
|
|
ULONG LongestStringLengthAtLevel;
|
|
ULONG FinalStringLevel, i;
|
|
|
|
ULONG DeviceLocationPathMultiSzStringCount;
|
|
ULONG DeviceLocationPathMultiSzLength;
|
|
PWCHAR DeviceLocationPathMultiSz = NULL;
|
|
|
|
ULONG CombinationsRemaining, CombinationEnumIndex;
|
|
ULONG MultiSzIndex, MultiSzLookupIndex, StringLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
if ((!ARGUMENT_PRESENT(DeviceNode)) ||
|
|
(!ARGUMENT_PRESENT(DeviceLocationStrings))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Initialize out parameters.
|
|
//
|
|
*DeviceLocationStrings = NULL;
|
|
|
|
//
|
|
// We should NEVER have to query for the location of the root devnode.
|
|
//
|
|
if (DeviceNode == IopRootDeviceNode) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Count the number of devnodes in the ancestry of the device to find out
|
|
// the max number of location string sets we may have to query for.
|
|
//
|
|
QueriedLocationStringsArraySize = 0;
|
|
for (deviceNode = DeviceNode;
|
|
deviceNode != IopRootDeviceNode;
|
|
deviceNode = deviceNode->Parent) {
|
|
QueriedLocationStringsArraySize++;
|
|
}
|
|
|
|
ASSERT(QueriedLocationStringsArraySize > 0);
|
|
|
|
//
|
|
// Allocate and initialize an array of string buffer pointers for all
|
|
// devices in the ancestry, and a corresponding array for the number of
|
|
// strings retrieved for each device.
|
|
//
|
|
QueriedLocationStrings =
|
|
(PWSTR*)ExAllocatePool(PagedPool,
|
|
QueriedLocationStringsArraySize*sizeof(PWSTR));
|
|
|
|
if (QueriedLocationStrings == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Clean0;
|
|
}
|
|
|
|
RtlZeroMemory(QueriedLocationStrings,
|
|
QueriedLocationStringsArraySize*sizeof(PWSTR));
|
|
|
|
|
|
QueriedLocationStringsCount =
|
|
(ULONG*)ExAllocatePool(PagedPool,
|
|
QueriedLocationStringsArraySize*sizeof(ULONG));
|
|
|
|
if (QueriedLocationStringsCount == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Clean0;
|
|
}
|
|
|
|
RtlZeroMemory(QueriedLocationStringsCount,
|
|
QueriedLocationStringsArraySize*sizeof(ULONG));
|
|
|
|
//
|
|
// Starting at the target device, walk up the devnode tree, retrieving the
|
|
// set of location strings for all ancestors up to (but not including) the
|
|
// root devnode. We'll stop when we've reached the top of the tree, or when
|
|
// some intermediate device has explicitly declared that the translation is
|
|
// complete.
|
|
//
|
|
i = 0;
|
|
|
|
//
|
|
// Along the way, we count the total number of string combinations that can
|
|
// be formed by taking a single string from the multi-sz list at each level.
|
|
// This is simply the product of the number of string elements in the
|
|
// multi-sz list at each level.
|
|
//
|
|
DeviceLocationPathMultiSzStringCount = 1;
|
|
|
|
//
|
|
// Also along the way, calculate the length (in chars) of the longest device
|
|
// location path that can be generated from all combinations. This is just
|
|
// the sum of the longest string at each level (LongestStringLengthAtLevel
|
|
// below), plus the necessary path component separator strings and NULL
|
|
// terminating character.
|
|
//
|
|
//
|
|
// WARNING!! EXCESSIVELY VERBOSE COMMENT AHEAD!!
|
|
//
|
|
// NOTE: 27-Nov-2001 : Jim Cavalaris (jamesca)
|
|
//
|
|
// We use this length to calculate the length of the buffer required to
|
|
// hold the entire multi-sz list of device location paths ASSUMING ALL
|
|
// GENERATED STRINGS ARE EQUALLY AS LONG. This is an UPPER-BOUND, so we
|
|
// may end up allocating more memory than we actually need.
|
|
//
|
|
// This should be ok, since in the ideal (and assumed to be most-common)
|
|
// case, only one location string will ever be returned per device - in
|
|
// which case this calculation will be exactly the size required.
|
|
//
|
|
// In the event that multiple strings are returned per device, we should
|
|
// expect these strings to all be relatively short, and equal (or similar)
|
|
// in length. In that case, this calculation will be exactly the size
|
|
// required (or similar).
|
|
//
|
|
// We also currently do not expect to have to query many devices in the
|
|
// ancestry to complete the translation, so we shouldn't event expect too
|
|
// many combinations.
|
|
//
|
|
// These are our assumptions, so consider yourself warned! If any of
|
|
// these change such that we would need to allocate an excessive amount of
|
|
// memory, you will want to either run through the same algorithm the
|
|
// device location path generation code runs through just to calculate the
|
|
// exact size, or find some way to enumerate the device location path
|
|
// combinations incrementally.
|
|
//
|
|
DeviceLocationPathMultiSzLength = 0;
|
|
|
|
for (deviceNode = DeviceNode;
|
|
deviceNode != IopRootDeviceNode;
|
|
deviceNode = deviceNode->Parent) {
|
|
|
|
//
|
|
// Query the device for the location interface.
|
|
//
|
|
Status = PiQueryInterface(deviceNode->PhysicalDeviceObject,
|
|
&GUID_PNP_LOCATION_INTERFACE,
|
|
PNP_LOCATION_INTERFACE_VERSION,
|
|
sizeof(PNP_LOCATION_INTERFACE),
|
|
(PINTERFACE)&LocationInterface);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// If the location interface was not available for some device
|
|
// before translation is complete, the entire operation is
|
|
// unsuccessful.
|
|
//
|
|
ASSERT((Status == STATUS_NOT_SUPPORTED) || (Status == STATUS_INSUFFICIENT_RESOURCES));
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// If the location interface is supported, the required interface
|
|
// routines must be supplied.
|
|
//
|
|
ASSERT(LocationInterface.InterfaceReference != NULL);
|
|
ASSERT(LocationInterface.InterfaceDereference != NULL);
|
|
ASSERT(LocationInterface.GetLocationString != NULL);
|
|
|
|
if (LocationInterface.GetLocationString != NULL) {
|
|
|
|
//
|
|
// Initialize the location string.
|
|
//
|
|
TempMultiSz = NULL;
|
|
|
|
//
|
|
// Get the set of location strings for this device.
|
|
//
|
|
Status = LocationInterface.GetLocationString(
|
|
LocationInterface.Context,
|
|
&TempMultiSz);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// If successful, the caller must have supplied us with a
|
|
// buffer.
|
|
//
|
|
ASSERT(TempMultiSz != NULL);
|
|
|
|
//
|
|
// If not, the call was not really successful.
|
|
//
|
|
if (TempMultiSz == NULL) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// If a multi-sz list of device location strings was returned,
|
|
// inspect it, and keep note of a few things. Specifically, the
|
|
// number of strings in the multi-sz list, the length of the
|
|
// multi-sz list, and the length of the longest string in the
|
|
// list.
|
|
//
|
|
QueriedLocationStringsCount[i] = 0;
|
|
TempMultiSzLength = 0;
|
|
LongestStringLengthAtLevel = 0;
|
|
|
|
for (p = TempMultiSz; *p != UNICODE_NULL; p += wcslen(p)+1) {
|
|
//
|
|
// Count the number of strings at this level (in this
|
|
// multi-sz list).
|
|
//
|
|
QueriedLocationStringsCount[i]++;
|
|
|
|
//
|
|
// Determine the length (in chars) of the multi-sz list so
|
|
// we can allocate our own buffer, and copy it.
|
|
//
|
|
TempMultiSzLength += (ULONG)(wcslen(p) + 1);
|
|
|
|
//
|
|
// Also determine the length of the longest string of all
|
|
// strings in this multi-sz list so we can estimate the
|
|
// length required for all device location path
|
|
// combinations.
|
|
//
|
|
StringLength = (ULONG)wcslen(p);
|
|
if (StringLength > LongestStringLengthAtLevel) {
|
|
|
|
LongestStringLengthAtLevel = StringLength;
|
|
}
|
|
}
|
|
|
|
ASSERT(QueriedLocationStringsCount[i] > 0);
|
|
ASSERT(TempMultiSzLength > 0);
|
|
ASSERT(LongestStringLengthAtLevel > 0);
|
|
|
|
//
|
|
// Include the length of the double NULL-terminating character.
|
|
//
|
|
TempMultiSzLength += 1;
|
|
|
|
//
|
|
// After analyzing the device location strings at each level,
|
|
// update the number of device path combinations possible by
|
|
// simply multiplying the combinations possible so far by the
|
|
// number of strings retrieved for this level (in this multi-sz list).
|
|
//
|
|
DeviceLocationPathMultiSzStringCount *= QueriedLocationStringsCount[i];
|
|
|
|
//
|
|
// Also, update the length of the longest device location path
|
|
// possible by adding the length of the longest string available
|
|
// at this level.
|
|
//
|
|
DeviceLocationPathMultiSzLength += LongestStringLengthAtLevel;
|
|
|
|
//
|
|
// Make our own copy of the caller supplied multi-sz list of
|
|
// device location strings.
|
|
//
|
|
QueriedLocationStrings[i] =
|
|
(PWSTR)ExAllocatePool(PagedPool,
|
|
TempMultiSzLength*sizeof(WCHAR));
|
|
|
|
if (QueriedLocationStrings[i] != NULL) {
|
|
//
|
|
// Note on array element ordering - since we start at the
|
|
// target devnode and walk up the chain of parents, we don't
|
|
// yet know just how high up the translation will go. We
|
|
// add children towards the front of the array, so if
|
|
// translation is complete before every ancestor is queried,
|
|
// we'll just end up with some empty entries at the end.
|
|
//
|
|
RtlCopyMemory(QueriedLocationStrings[i],
|
|
TempMultiSz,
|
|
TempMultiSzLength*sizeof(WCHAR));
|
|
i++;
|
|
|
|
} else {
|
|
//
|
|
// Unable to allocate a buffer for our own list of pointers.
|
|
//
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Free the callee-allocated buffer.
|
|
//
|
|
ExFreePool(TempMultiSz);
|
|
TempMultiSz = NULL;
|
|
|
|
} else {
|
|
//
|
|
// If unsuccessful, make sure no location string was returned by
|
|
// the interface routine.
|
|
//
|
|
ASSERT(TempMultiSz == NULL);
|
|
|
|
//
|
|
// If the driver failed the call, but still allocated memory for
|
|
// us anyways, we'll clean up after it.
|
|
//
|
|
if (TempMultiSz != NULL) {
|
|
ExFreePool(TempMultiSz);
|
|
TempMultiSz = NULL;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// If a GetLocationString location interface routine was not
|
|
// supplied with the interface for some device before translation is
|
|
// complete, the entire operation is unsuccessful.
|
|
//
|
|
// Fall through to dereference the interface below, then exit.
|
|
//
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Dereference the Location Interface.
|
|
//
|
|
if (LocationInterface.InterfaceDereference != NULL) {
|
|
LocationInterface.InterfaceDereference(LocationInterface.Context);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// If unsuccessful while requesting location information for some
|
|
// device before translation was complete, the entire operation is
|
|
// unsuccessful.
|
|
//
|
|
goto Clean0;
|
|
|
|
} else if ((Status == STATUS_TRANSLATION_COMPLETE) ||
|
|
(i == QueriedLocationStringsArraySize)) {
|
|
//
|
|
// If successful, and the last device queried specifically indicated
|
|
// the end of the translation - OR - this is the last device in the
|
|
// ancestry, and therefore translation is explicitly complete, note
|
|
// translation is complete.
|
|
//
|
|
Status = STATUS_TRANSLATION_COMPLETE;
|
|
|
|
//
|
|
// Account for the length of the NULL-terminating character in our
|
|
// longest-length single string component estimate.
|
|
//
|
|
DeviceLocationPathMultiSzLength += 1;
|
|
|
|
//
|
|
// Stop walking up the device tree.
|
|
//
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Success so far, but we still need to query more devices for
|
|
// location strings.
|
|
//
|
|
ASSERT(i < QueriedLocationStringsArraySize);
|
|
|
|
//
|
|
// Account for the length of a location path separator after every
|
|
// path component but the last.
|
|
//
|
|
DeviceLocationPathMultiSzLength +=
|
|
IopConstStringLength(_CRITICAL_DEVICE_LOCATION_PATH_SEPARATOR_STRING);
|
|
}
|
|
|
|
//
|
|
// The location information of every device in the ancestry has been queried
|
|
// successfully.
|
|
//
|
|
ASSERT(Status == STATUS_TRANSLATION_COMPLETE);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Make sure we queried at least one device.
|
|
//
|
|
ASSERT(i > 0);
|
|
|
|
//
|
|
// Allocate a buffer large enough to assume that all device location path
|
|
// string combinations are as long as the longest device location path
|
|
// string formed. Also account for the double NULL-terminating character.
|
|
//
|
|
DeviceLocationPathMultiSzLength *= DeviceLocationPathMultiSzStringCount;
|
|
DeviceLocationPathMultiSzLength += 1;
|
|
|
|
DeviceLocationPathMultiSz =
|
|
(PWCHAR)ExAllocatePool(PagedPool,
|
|
DeviceLocationPathMultiSzLength*sizeof(WCHAR));
|
|
|
|
if (DeviceLocationPathMultiSz == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Clean0;
|
|
}
|
|
|
|
RtlZeroMemory(DeviceLocationPathMultiSz,
|
|
DeviceLocationPathMultiSzLength*sizeof(WCHAR));
|
|
|
|
//
|
|
// We should now have an array of multi-sz strings returned by the location
|
|
// string interface routine for a set of devices in the ancestry of the
|
|
// specified device. From these multi-sz strings, we now need to build all
|
|
// possible device paths.
|
|
//
|
|
|
|
//
|
|
// First, determine where the first string in the device path is stored.
|
|
// Since we stored these in the array in order, starting with the child
|
|
// device, the last non-NULL string placed in the array (i - 1) is the most
|
|
// significant location string.
|
|
//
|
|
FinalStringLevel = i-1;
|
|
ASSERT(QueriedLocationStrings[FinalStringLevel] != NULL);
|
|
ASSERT(QueriedLocationStringsCount[FinalStringLevel] > 0);
|
|
|
|
//
|
|
// Build all string combinations by enumerating the total number of possible
|
|
// combinations, and picking the appropriate string element from each
|
|
// multi-sz list on each iteration.
|
|
//
|
|
pdlp = DeviceLocationPathMultiSz;
|
|
|
|
for (CombinationEnumIndex = 0;
|
|
CombinationEnumIndex < DeviceLocationPathMultiSzStringCount;
|
|
CombinationEnumIndex++) {
|
|
|
|
//
|
|
// Start with the multi-sz list at the FinalStringLevel, and work down
|
|
// to level 0.
|
|
//
|
|
i = FinalStringLevel;
|
|
|
|
//
|
|
// When starting from level 0, the number of combinations remaining is
|
|
// simply the total number of combinations that can be formed from all
|
|
// levels. The number of combination remaining will be adjusted after
|
|
// selecting a string from each subsequent level, by discounting the
|
|
// combinations that the level contributed.
|
|
//
|
|
CombinationsRemaining = DeviceLocationPathMultiSzStringCount;
|
|
|
|
for ( ; ; ) {
|
|
|
|
ASSERT(CombinationsRemaining != 0);
|
|
|
|
//
|
|
// Calculate the index of the string in the multi-sz list at this
|
|
// level that is needed by this enumeration.
|
|
//
|
|
if (CombinationEnumIndex == 0) {
|
|
|
|
//
|
|
// On the first enumeration, just pick the first element from
|
|
// every level.
|
|
//
|
|
MultiSzLookupIndex = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// NOTE: 27-Nov-2001 : Jim Cavalaris (jamesca)
|
|
//
|
|
// For subsequent enumerations, the element to pick at each
|
|
// level to generate this enumeration's device location path is
|
|
// calculated based on:
|
|
//
|
|
// - the enumeration element we require,
|
|
// - the number of combinations remaining to be generated,
|
|
// - the number of elements to choose from at this level.
|
|
//
|
|
// This will will build all possible combinations of device
|
|
// location paths, enumerating elements from the least
|
|
// significant device (the target device) to the most
|
|
// significant device (tranlstaion complete), considering the
|
|
// the order of the strings at a particular level have been
|
|
// placed in the multi-sz list in order of decreasing relevance
|
|
// (i.e. most relevant location string for a device first).
|
|
//
|
|
|
|
//
|
|
// - CombinationsRemaining is the number of complete elements
|
|
// that must be built from the selections available from all
|
|
// levels above the current level.
|
|
//
|
|
// - (CombinationsRemaining / QueriedLocationStringsCount[i])
|
|
// describes the number of iterations through each element at
|
|
// the current level that are required before selecting the next
|
|
// element.
|
|
//
|
|
// - dividing that number into the index of the current
|
|
// enumeration gives the absolute index of the element in the
|
|
// expanded version of the selections at that level.
|
|
//
|
|
// - mod by the number of elements actually at this level to
|
|
// indicate which one to select.
|
|
//
|
|
|
|
MultiSzLookupIndex =
|
|
(CombinationEnumIndex /
|
|
(CombinationsRemaining / QueriedLocationStringsCount[i])) %
|
|
QueriedLocationStringsCount[i];
|
|
|
|
//
|
|
// (you may just want to trust me on this one.)
|
|
//
|
|
}
|
|
|
|
//
|
|
// Find the calculated string.
|
|
//
|
|
MultiSzIndex = 0;
|
|
for (p = QueriedLocationStrings[i]; MultiSzIndex < MultiSzLookupIndex; p += wcslen(p)+1) {
|
|
MultiSzIndex++;
|
|
ASSERT(*p != UNICODE_NULL);
|
|
ASSERT(MultiSzIndex < QueriedLocationStringsCount[i]);
|
|
}
|
|
|
|
//
|
|
// Append the string to the buffer.
|
|
//
|
|
RtlCopyMemory(pdlp, p, wcslen(p)*sizeof(WCHAR));
|
|
pdlp += wcslen(p);
|
|
|
|
if (i == 0) {
|
|
//
|
|
// This is the last level. NULL terminate this device location
|
|
// path combination string just formed.
|
|
//
|
|
*pdlp = UNICODE_NULL;
|
|
pdlp += 1;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If there are still more levels to process, append the device
|
|
// location path separator string.
|
|
//
|
|
RtlCopyMemory(pdlp,
|
|
_CRITICAL_DEVICE_LOCATION_PATH_SEPARATOR_STRING,
|
|
IopConstStringSize(_CRITICAL_DEVICE_LOCATION_PATH_SEPARATOR_STRING));
|
|
pdlp += IopConstStringLength(_CRITICAL_DEVICE_LOCATION_PATH_SEPARATOR_STRING);
|
|
|
|
//
|
|
// Adjust the total remaining number of string combinations that are
|
|
// possible to form from the string lists at the remaining levels.
|
|
//
|
|
CombinationsRemaining /= QueriedLocationStringsCount[i];
|
|
|
|
//
|
|
// Process the next level down.
|
|
//
|
|
i--;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Double-NULL terminate the entire device location path multi-sz list.
|
|
//
|
|
*pdlp = UNICODE_NULL;
|
|
|
|
//
|
|
// The multi-sz list of device location paths for this device has been built
|
|
// successfully.
|
|
//
|
|
|
|
*DeviceLocationStrings = DeviceLocationPathMultiSz;
|
|
|
|
Clean0:
|
|
|
|
//
|
|
// Free any memory we may have allocated along the way.
|
|
//
|
|
if (QueriedLocationStrings != NULL) {
|
|
ASSERT(QueriedLocationStringsArraySize > 0);
|
|
for (i = 0; i < QueriedLocationStringsArraySize; i++) {
|
|
if (QueriedLocationStrings[i] != NULL) {
|
|
ExFreePool(QueriedLocationStrings[i]);
|
|
}
|
|
}
|
|
ExFreePool(QueriedLocationStrings);
|
|
}
|
|
if (QueriedLocationStringsCount != NULL) {
|
|
ASSERT(QueriedLocationStringsArraySize > 0);
|
|
ExFreePool(QueriedLocationStringsCount);
|
|
}
|
|
|
|
//
|
|
// If unsuccesful, make sure we don't return a buffer to the caller.
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ASSERT(*DeviceLocationStrings == NULL);
|
|
|
|
ASSERT(DeviceLocationPathMultiSz == NULL);
|
|
if (DeviceLocationPathMultiSz != NULL) {
|
|
ExFreePool(DeviceLocationPathMultiSz);
|
|
}
|
|
|
|
} else {
|
|
ASSERT(*DeviceLocationStrings != NULL);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PpCriticalGetDeviceLocationStrings
|
|
|
|
|
|
|
|
//
|
|
// Generic synchronous query interface routine
|
|
// (may be moved from this as a public utility routine as needed)
|
|
//
|
|
|
|
|
|
NTSTATUS
|
|
PiQueryInterface(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN CONST GUID * InterfaceGuid,
|
|
IN USHORT InterfaceVersion,
|
|
IN USHORT InterfaceSize,
|
|
OUT PINTERFACE Interface
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queries the specified device for the requested interface.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
Specifies a device object in the stack to query.
|
|
The query-interface irp will be sent to the top of the stack.
|
|
|
|
InterfaceGuid -
|
|
The GUID of the interface requested.
|
|
|
|
InterfaceVersion -
|
|
The version of the interface requested.
|
|
|
|
InterfaceSize -
|
|
The size of the interface requested.
|
|
|
|
Interface -
|
|
The place in which to return the interface.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCCESS if the interface was retrieved, else an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
KEVENT Event;
|
|
PDEVICE_OBJECT deviceObject;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpStackNext;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// There is no file object associated with this Irp, so the event may be located
|
|
// on the stack as a non-object manager object.
|
|
//
|
|
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
|
|
//
|
|
// Get a pointer to the topmost device object in the stack of devices.
|
|
//
|
|
deviceObject = IoGetAttachedDeviceReference(DeviceObject);
|
|
|
|
Irp = IoBuildSynchronousFsdRequest(
|
|
IRP_MJ_PNP,
|
|
deviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&Event,
|
|
&IoStatusBlock);
|
|
|
|
if (Irp) {
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IrpStackNext = IoGetNextIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Create an interface query out of the Irp.
|
|
//
|
|
IrpStackNext->MinorFunction = IRP_MN_QUERY_INTERFACE;
|
|
IrpStackNext->Parameters.QueryInterface.InterfaceType = (GUID*)InterfaceGuid;
|
|
IrpStackNext->Parameters.QueryInterface.Size = InterfaceSize;
|
|
IrpStackNext->Parameters.QueryInterface.Version = InterfaceVersion;
|
|
IrpStackNext->Parameters.QueryInterface.Interface = (PINTERFACE)Interface;
|
|
IrpStackNext->Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
|
|
|
Status = IoCallDriver(deviceObject, Irp);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
//
|
|
// This waits using KernelMode, so that the stack, and therefore the
|
|
// event on that stack, is not paged out.
|
|
//
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
|
|
return Status;
|
|
|
|
} // PpQueryInterface
|
|
|
|
|
|
|
|
//
|
|
// Recursive registry key/value copy utility routine.
|
|
// (may be moved from this as a public utility routine as needed)
|
|
//
|
|
|
|
|
|
NTSTATUS
|
|
PiCopyKeyRecursive(
|
|
IN HANDLE SourceKeyRootHandle,
|
|
IN HANDLE TargetKeyRootHandle,
|
|
IN PWSTR SourceKeyPath OPTIONAL,
|
|
IN PWSTR TargetKeyPath OPTIONAL,
|
|
IN BOOLEAN CopyValuesAlways,
|
|
IN BOOLEAN ApplyACLsAlways
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine recursively copies a source key to a target key. Any new keys
|
|
that are created will receive the same security that is present on the
|
|
source key.
|
|
|
|
Arguments:
|
|
|
|
SourceKeyRootHandle -
|
|
|
|
Handle to root source key
|
|
|
|
TargetKeyRootHandle -
|
|
|
|
Handle to root target key
|
|
|
|
SourceKeyPath -
|
|
|
|
Source key relative path to the subkey which needs to be recursively
|
|
copied. If this is NULL, SourceKeyRootHandle is the key from which the
|
|
recursive copy is to be done.
|
|
|
|
TargetKeyPath -
|
|
|
|
Target root key relative path to the subkey which needs to be
|
|
recursively copied. if this is NULL, TargetKeyRootHandle is the key from
|
|
which the recursive copy is to be done.
|
|
|
|
CopyValuesAlways -
|
|
|
|
If FALSE, this routine doesn't copy values which are already there on
|
|
the target tree.
|
|
|
|
ApplyACLsAlways -
|
|
|
|
If TRUE, attempts to copy ACLs to existing registry keys. Otherwise,
|
|
the ACL of the source keys are only applied to new registry keys.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
Partially based on the recursive key copy routine implemented for text-mode
|
|
setup, setupdd!SppCopyKeyRecursive.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HANDLE SourceKeyHandle = NULL, TargetKeyHandle = NULL;
|
|
OBJECT_ATTRIBUTES ObjaSource, ObjaTarget;
|
|
UNICODE_STRING UnicodeStringSource, UnicodeStringTarget, UnicodeStringValue;
|
|
NTSTATUS TempStatus;
|
|
ULONG ResultLength, Index;
|
|
PSECURITY_DESCRIPTOR Security = NULL;
|
|
|
|
PKEY_FULL_INFORMATION KeyFullInfoBuffer;
|
|
ULONG MaxNameLen, MaxValueNameLen;
|
|
PKEY_VALUE_FULL_INFORMATION ValueFullInfoBuffer;
|
|
|
|
PVOID KeyInfoBuffer;
|
|
PKEY_BASIC_INFORMATION KeyBasicInfo;
|
|
|
|
PVOID ValueInfoBuffer;
|
|
PKEY_VALUE_BASIC_INFORMATION ValueBasicInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a handle to the source key.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(SourceKeyPath)) {
|
|
//
|
|
// No path supplied; make sure that we at least have a source root key.
|
|
//
|
|
ASSERT(SourceKeyRootHandle != NULL);
|
|
|
|
if (SourceKeyRootHandle == NULL) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Use source root as the source key.
|
|
//
|
|
SourceKeyHandle = SourceKeyRootHandle;
|
|
|
|
} else {
|
|
//
|
|
// Open the specified source key path off the root.
|
|
// SourceKeyRootHandle may be NULL if SourceKeyPath is a fully qualified
|
|
// registry path.
|
|
//
|
|
RtlInitUnicodeString(
|
|
&UnicodeStringSource,
|
|
SourceKeyPath);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjaSource,
|
|
&UnicodeStringSource,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
SourceKeyRootHandle,
|
|
(PSECURITY_DESCRIPTOR)NULL);
|
|
|
|
Status =
|
|
ZwOpenKey(
|
|
&SourceKeyHandle,
|
|
KEY_READ,
|
|
&ObjaSource);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to open key %ws in the source hive (%lx)\n",
|
|
SourceKeyPath, Status));
|
|
goto Clean0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Should have source key now.
|
|
//
|
|
ASSERT(SourceKeyHandle != NULL);
|
|
|
|
//
|
|
// Next, get the security descriptor from the source key so we can create
|
|
// the target key with the correct ACL.
|
|
//
|
|
TempStatus =
|
|
ZwQuerySecurityObject(
|
|
SourceKeyHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
0,
|
|
&ResultLength);
|
|
|
|
if (TempStatus == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
Security =
|
|
(PSECURITY_DESCRIPTOR)ExAllocatePool(PagedPool,
|
|
ResultLength);
|
|
|
|
if (Security != NULL) {
|
|
|
|
TempStatus =
|
|
ZwQuerySecurityObject(
|
|
SourceKeyHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
Security,
|
|
ResultLength,
|
|
&ResultLength);
|
|
|
|
if (!NT_SUCCESS(TempStatus)) {
|
|
ExFreePool(Security);
|
|
Security = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Security == NULL) {
|
|
//
|
|
// We'll continue the copy, but won't be able to apply the source ACL to
|
|
// the target.
|
|
//
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to query security for key %ws in the source hive (%lx)\n",
|
|
SourceKeyPath, TempStatus));
|
|
}
|
|
|
|
|
|
//
|
|
// Get a handle to the target key.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(TargetKeyPath)) {
|
|
//
|
|
// No path supplied; make sure that we at least have a target root key.
|
|
//
|
|
ASSERT(TargetKeyRootHandle != NULL);
|
|
|
|
if (TargetKeyRootHandle == NULL) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// No path supplied; use target root as the target key.
|
|
//
|
|
TargetKeyHandle = TargetKeyRootHandle;
|
|
|
|
} else {
|
|
//
|
|
// Attempt to open (not create) the target key first.
|
|
// TargetKeyRootHandle may be NULL if TargetKeyPath is a fully qualified
|
|
// registry path.
|
|
//
|
|
RtlInitUnicodeString(
|
|
&UnicodeStringTarget,
|
|
TargetKeyPath);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjaTarget,
|
|
&UnicodeStringTarget,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
TargetKeyRootHandle,
|
|
(PSECURITY_DESCRIPTOR)NULL);
|
|
|
|
Status =
|
|
ZwOpenKey(
|
|
&TargetKeyHandle,
|
|
KEY_ALL_ACCESS,
|
|
&ObjaTarget);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// Assume that failure was because the key didn't exist.
|
|
//
|
|
ASSERT(Status == STATUS_OBJECT_NAME_NOT_FOUND);
|
|
|
|
//
|
|
// 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 (if
|
|
// available).
|
|
//
|
|
// NOTE: 01-Dec-2001 : Jim Cavalaris (jamesca)
|
|
//
|
|
// Security attributes of the source key are always applied to the
|
|
// newly created target key root, rather than inherited from the
|
|
// target key root handle - irespective of the ApplyACLsAlways
|
|
// parameter. This may not be desired in all cases!
|
|
//
|
|
ObjaTarget.SecurityDescriptor = Security;
|
|
|
|
Status =
|
|
ZwCreateKey(
|
|
&TargetKeyHandle,
|
|
KEY_ALL_ACCESS,
|
|
&ObjaTarget,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to create target key %ws(%lx)\n",
|
|
TargetKeyPath, Status));
|
|
goto Clean0;
|
|
}
|
|
|
|
} else if (ApplyACLsAlways) {
|
|
//
|
|
// Key already exists - apply the source ACL to the target.
|
|
//
|
|
TempStatus =
|
|
ZwSetSecurityObject(
|
|
TargetKeyHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
Security);
|
|
|
|
if (!NT_SUCCESS(TempStatus)) {
|
|
//
|
|
// Unable to apply source ACL to target.
|
|
//
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to copy ACL to existing key %ws(%lx)\n",
|
|
TargetKeyPath, TempStatus));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Should have target key now.
|
|
//
|
|
ASSERT(TargetKeyHandle != NULL);
|
|
|
|
//
|
|
// Query the source key to determine the size of the buffer required to
|
|
// enumerated the longest key and value names. If successful, we are
|
|
// responsible for freeing the returned buffer.
|
|
//
|
|
KeyFullInfoBuffer = NULL;
|
|
|
|
Status =
|
|
IopGetRegistryKeyInformation(
|
|
SourceKeyHandle,
|
|
&KeyFullInfoBuffer);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(KeyFullInfoBuffer == NULL);
|
|
goto Clean0;
|
|
}
|
|
|
|
ASSERT(KeyFullInfoBuffer != NULL);
|
|
|
|
//
|
|
// Note the longest subkey name and value name length for the source key.
|
|
//
|
|
MaxNameLen = KeyFullInfoBuffer->MaxNameLen + 1;
|
|
MaxValueNameLen = KeyFullInfoBuffer->MaxValueNameLen + 1;
|
|
|
|
ExFreePool(KeyFullInfoBuffer);
|
|
|
|
//
|
|
// Allocate a key info buffer large enough to hold the basic information for
|
|
// the enumerated key with the longest name.
|
|
//
|
|
KeyInfoBuffer =
|
|
(PVOID)ExAllocatePool(PagedPool,
|
|
sizeof(KEY_BASIC_INFORMATION) +
|
|
(MaxNameLen*sizeof(WCHAR)));
|
|
if (KeyInfoBuffer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Clean0;
|
|
}
|
|
|
|
KeyBasicInfo = (PKEY_BASIC_INFORMATION)KeyInfoBuffer;
|
|
|
|
for (Index = 0; ; Index++) {
|
|
|
|
//
|
|
// Enumerate source subkeys.
|
|
//
|
|
Status =
|
|
ZwEnumerateKey(
|
|
SourceKeyHandle,
|
|
Index,
|
|
KeyBasicInformation,
|
|
KeyBasicInfo,
|
|
sizeof(KEY_BASIC_INFORMATION)+(MaxNameLen*sizeof(WCHAR)),
|
|
&ResultLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
// Since we queried the key for the MaxNameLength prior to
|
|
// allocating, we shouldn't get STATUS_BUFFER_OVERFLOW either.
|
|
//
|
|
ASSERT(Status != STATUS_BUFFER_TOO_SMALL);
|
|
ASSERT(Status != STATUS_BUFFER_OVERFLOW);
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
//
|
|
// Finished enumerating keys.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
//
|
|
// Some other error while enumerating keys.
|
|
//
|
|
if (ARGUMENT_PRESENT(SourceKeyPath)) {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to enumerate subkeys in key %ws(%lx)\n",
|
|
SourceKeyPath, Status));
|
|
} else {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to enumerate subkeys in root key(%lx)\n",
|
|
Status));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// NULL-terminate the subkey name just in case.
|
|
//
|
|
KeyBasicInfo->Name[KeyBasicInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
//
|
|
// Recursively create the subkey in the target key.
|
|
//
|
|
Status =
|
|
PiCopyKeyRecursive(
|
|
SourceKeyHandle,
|
|
TargetKeyHandle,
|
|
KeyBasicInfo->Name,
|
|
KeyBasicInfo->Name,
|
|
CopyValuesAlways,
|
|
ApplyACLsAlways);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to copy subkey recursively in key %ws(%lx)\n",
|
|
KeyBasicInfo->Name, Status));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the key info buffer.
|
|
//
|
|
ASSERT(KeyInfoBuffer);
|
|
ExFreePool(KeyInfoBuffer);
|
|
KeyInfoBuffer = NULL;
|
|
|
|
//
|
|
// Stop copying if we encountered some error along the way.
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a value name info buffer large enough to hold the basic value
|
|
// information for the enumerated value with the longest name.
|
|
//
|
|
ValueInfoBuffer =
|
|
(PVOID)ExAllocatePool(PagedPool,
|
|
sizeof(KEY_VALUE_FULL_INFORMATION) +
|
|
(MaxValueNameLen*sizeof(WCHAR)));
|
|
if (ValueInfoBuffer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Clean0;
|
|
}
|
|
|
|
|
|
ValueBasicInfo = (PKEY_VALUE_BASIC_INFORMATION)ValueInfoBuffer;
|
|
|
|
for (Index = 0; ; Index++) {
|
|
|
|
//
|
|
// Enumerate source key values.
|
|
//
|
|
Status =
|
|
ZwEnumerateValueKey(
|
|
SourceKeyHandle,
|
|
Index,
|
|
KeyValueBasicInformation,
|
|
ValueBasicInfo,
|
|
sizeof(KEY_VALUE_FULL_INFORMATION) + (MaxValueNameLen*sizeof(WCHAR)),
|
|
&ResultLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
// Since we queried the key for the MaxValueNameLength prior to
|
|
// allocating, we shouldn't get STATUS_BUFFER_OVERFLOW either.
|
|
//
|
|
ASSERT(Status != STATUS_BUFFER_TOO_SMALL);
|
|
ASSERT(Status != STATUS_BUFFER_OVERFLOW);
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
//
|
|
// Finished enumerating values.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
//
|
|
// Some other error while enumerating values.
|
|
//
|
|
if (ARGUMENT_PRESENT(SourceKeyPath)) {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to enumerate values in key %ws(%lx)\n",
|
|
SourceKeyPath, Status));
|
|
|
|
} else {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to enumerate values in root key(%lx)\n",
|
|
Status));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// NULL-terminate the value name just in case.
|
|
//
|
|
ValueBasicInfo->Name[ValueBasicInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
UnicodeStringValue.Buffer = ValueBasicInfo->Name;
|
|
UnicodeStringValue.Length = (USHORT)ValueBasicInfo->NameLength;
|
|
UnicodeStringValue.MaximumLength = UnicodeStringValue.Length;
|
|
|
|
//
|
|
// If it is a conditional copy, we need to check if the value already
|
|
// exists in the target, in which case we shouldn't set the value.
|
|
//
|
|
if (!CopyValuesAlways) {
|
|
|
|
KEY_VALUE_BASIC_INFORMATION TempValueBasicInfo;
|
|
|
|
//
|
|
// To see if the value exists, we attempt to get basic information
|
|
// on the key value and pass in a buffer that's large enough only
|
|
// for the fixed part of the basic info structure. If this is
|
|
// successful or reports buffer overflow, then the key
|
|
// exists. Otherwise it doesn't exist.
|
|
//
|
|
|
|
Status =
|
|
ZwQueryValueKey(
|
|
TargetKeyHandle,
|
|
&UnicodeStringValue,
|
|
KeyValueBasicInformation,
|
|
&TempValueBasicInfo,
|
|
sizeof(TempValueBasicInfo),
|
|
&ResultLength);
|
|
|
|
//
|
|
// STATUS_BUFFER_TOO_SMALL would mean that there was not enough room
|
|
// for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(Status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if ((NT_SUCCESS(Status)) ||
|
|
(Status == STATUS_BUFFER_OVERFLOW)) {
|
|
//
|
|
// Value exists, and we shouldn't change it.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retrieve the full source value information.
|
|
//
|
|
ValueFullInfoBuffer = NULL;
|
|
|
|
Status =
|
|
IopGetRegistryValue(
|
|
SourceKeyHandle,
|
|
UnicodeStringValue.Buffer,
|
|
&ValueFullInfoBuffer);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ASSERT(ValueFullInfoBuffer != NULL);
|
|
|
|
//
|
|
// If successful, write it to the target key.
|
|
//
|
|
Status =
|
|
ZwSetValueKey(
|
|
TargetKeyHandle,
|
|
&UnicodeStringValue,
|
|
ValueFullInfoBuffer->TitleIndex,
|
|
ValueFullInfoBuffer->Type,
|
|
(PVOID)((PUCHAR)ValueFullInfoBuffer + ValueFullInfoBuffer->DataOffset),
|
|
ValueFullInfoBuffer->DataLength);
|
|
|
|
ExFreePool(ValueFullInfoBuffer);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (ARGUMENT_PRESENT(TargetKeyPath)) {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to set value %ws in key %ws(%lx)\n",
|
|
UnicodeStringValue.Buffer, TargetKeyPath, Status));
|
|
} else {
|
|
IopDbgPrint((IOP_ENUMERATION_ERROR_LEVEL,
|
|
"PiCopyKeyRecursive: unable to set value %ws(%lx)\n",
|
|
UnicodeStringValue.Buffer, Status));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the value info buffer.
|
|
//
|
|
ASSERT(ValueInfoBuffer);
|
|
ExFreePool(ValueInfoBuffer);
|
|
|
|
Clean0:
|
|
|
|
if (Security != NULL) {
|
|
ExFreePool(Security);
|
|
}
|
|
|
|
//
|
|
// Close handles only if explicitly opened by this routine.
|
|
//
|
|
if ((ARGUMENT_PRESENT(SourceKeyPath)) &&
|
|
(SourceKeyHandle != NULL)) {
|
|
ASSERT(SourceKeyHandle != SourceKeyRootHandle);
|
|
ZwClose(SourceKeyHandle);
|
|
}
|
|
|
|
if ((ARGUMENT_PRESENT(TargetKeyPath)) &&
|
|
(TargetKeyHandle != NULL)) {
|
|
ASSERT(TargetKeyHandle != TargetKeyRootHandle);
|
|
ZwClose(TargetKeyHandle);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // PiCopyKeyRecursive
|
|
|
|
NTSTATUS
|
|
PiCriticalQueryRegistryValueCallback(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
{
|
|
PPI_CRITICAL_QUERY_CONTEXT context = (PPI_CRITICAL_QUERY_CONTEXT)EntryContext;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(ValueName);
|
|
|
|
if (ValueType == REG_BINARY && ValueLength && ValueData) {
|
|
|
|
context->Buffer = ExAllocatePool(PagedPool, ValueLength);
|
|
if (context->Buffer) {
|
|
|
|
RtlCopyMemory(context->Buffer, ValueData, ValueLength);
|
|
context->Size = ValueLength;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|