3432 lines
104 KiB
C
3432 lines
104 KiB
C
/*++
|
||
|
||
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;
|
||
}
|