3808 lines
123 KiB
C
3808 lines
123 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
pnpsubs.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the plug-and-play subroutines for the
|
||
I/O system.
|
||
|
||
|
||
Author:
|
||
|
||
Shie-Lin Tzong (shielint) 3-Jan-1995
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "iop.h"
|
||
|
||
//
|
||
// Prototype of internal functions
|
||
//
|
||
|
||
BOOLEAN
|
||
IopIsDeviceInstanceEnabled(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
IN HANDLE ServiceHandle
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, IopCreateMadeupNode)
|
||
#pragma alloc_text(PAGE, IopRemoveStringFromValueKey)
|
||
#pragma alloc_text(PAGE, IopAppendStringToValueKey)
|
||
#pragma alloc_text(PAGE, IopConcatenateUnicodeStrings)
|
||
#pragma alloc_text(PAGE, IopPrepareDriverLoading)
|
||
#pragma alloc_text(PAGE, IopServiceInstanceToDeviceInstance)
|
||
#pragma alloc_text(PAGE, IopOpenRegistryKeyPersist)
|
||
#pragma alloc_text(PAGE, IopOpenServiceEnumKeys)
|
||
#pragma alloc_text(PAGE, IopOpenCurrentHwProfileDeviceInstanceKey)
|
||
#pragma alloc_text(PAGE, IopGetDeviceInstanceCsConfigFlags)
|
||
#pragma alloc_text(PAGE, IopSetDeviceInstanceCsConfigFlags)
|
||
#pragma alloc_text(PAGE, IopApplyFunctionToSubKeys)
|
||
#pragma alloc_text(PAGE, IopRegMultiSzToUnicodeStrings)
|
||
#pragma alloc_text(PAGE, IopApplyFunctionToServiceInstances)
|
||
#pragma alloc_text(PAGE, IopIsDuplicatedDevices)
|
||
#pragma alloc_text(PAGE, IopMarkDuplicateDevice)
|
||
#pragma alloc_text(PAGE, IopFreeUnicodeStringList)
|
||
#pragma alloc_text(PAGE, IopDriverLoadingFailed)
|
||
#pragma alloc_text(PAGE, IopIsDeviceInstanceEnabled)
|
||
#if _PNP_POWER_
|
||
#pragma alloc_text(PAGE, IopDetermineResourceListSize)
|
||
#pragma alloc_text(PAGE, IopReferenceDriverObjectByName)
|
||
#pragma alloc_text(PAGE, IopReferenceDeviceHandler)
|
||
#endif // _PNP_POWER_
|
||
#endif
|
||
|
||
NTSTATUS
|
||
IopCreateMadeupNode(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
OUT PHANDLE ReturnedHandle,
|
||
OUT PUNICODE_STRING KeyName,
|
||
OUT PULONG InstanceNumber,
|
||
IN BOOLEAN ResourceOwned
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a new instance node under System\Enum\Root\*Madeup<Name>
|
||
key and all the required default value entries. Also a value entry under
|
||
Service\ServiceKeyName\Enum is created to point to the newly created madeup
|
||
entry. A handle and the keyname of the new key are returned to caller.
|
||
Caller must free the unicode string when he is done with it.
|
||
|
||
Parameters:
|
||
|
||
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
||
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
||
that caused the driver to load. This is the RegistryPath parameter
|
||
to the DriverEntry routine.
|
||
|
||
ReturnedHandle - Supplies a variable to receive the handle of the
|
||
newly created key.
|
||
|
||
KeyName - Supplies a variable to receive the name of the newly created
|
||
key.
|
||
|
||
InstanceNumber - supplies a variable to receive the InstanceNumber value
|
||
entry created under service\name\enum subkey.
|
||
|
||
ResourceOwned - supplies a BOOLEAN variable to indicate if caller owns
|
||
the registry resource exclusively.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL;
|
||
UNICODE_STRING tmpKeyName, unicodeInstanceName, unicodeString;
|
||
UNICODE_STRING rootKeyName, unicodeValueName, unicodeKeyName;
|
||
HANDLE handle, enumRootHandle, hTreeHandle;
|
||
ULONG instance;
|
||
UCHAR unicodeBuffer[20];
|
||
ULONG tmpValue, disposition = 0;
|
||
NTSTATUS status;
|
||
PWSTR p;
|
||
// PCHAR pc1, pc2;
|
||
BOOLEAN releaseResource = FALSE;
|
||
|
||
if (!ResourceOwned) {
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
||
releaseResource = TRUE;
|
||
}
|
||
|
||
//
|
||
// Open LocalMachine\System\CurrentControlSet\Enum\Root
|
||
//
|
||
|
||
status = IopOpenRegistryKey(&enumRootHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumRootName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
goto local_exit0;
|
||
}
|
||
|
||
//
|
||
// Open, and create if not already exist, System\Enum\Root\LEGACY_<ServiceName>
|
||
// First, try to find the ServiceName by extracting it from user supplied
|
||
// ServiceKeyName.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&tmpKeyName, REGSTR_KEY_MADEUP);
|
||
IopConcatenateUnicodeStrings(&unicodeKeyName, &tmpKeyName, ServiceKeyName);
|
||
RtlUpcaseUnicodeString(&unicodeKeyName, &unicodeKeyName, FALSE);
|
||
status = IopOpenRegistryKeyPersist(&handle,
|
||
enumRootHandle,
|
||
&unicodeKeyName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE,
|
||
NULL
|
||
);
|
||
ZwClose(enumRootHandle);
|
||
if (!NT_SUCCESS(status)) {
|
||
RtlFreeUnicodeString(&unicodeKeyName);
|
||
goto local_exit0;
|
||
}
|
||
|
||
instance = 1;
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEXT_INSTANCE);
|
||
status = ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&instance,
|
||
sizeof(instance)
|
||
);
|
||
|
||
instance--;
|
||
*InstanceNumber = instance;
|
||
PiUlongToInstanceKeyUnicodeString(&unicodeInstanceName,
|
||
unicodeBuffer + sizeof(WCHAR), // reserve first WCHAR space
|
||
20 - sizeof(WCHAR),
|
||
instance
|
||
);
|
||
status = IopOpenRegistryKeyPersist(ReturnedHandle,
|
||
handle,
|
||
&unicodeInstanceName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE,
|
||
&disposition
|
||
);
|
||
ZwClose(handle);
|
||
if (!NT_SUCCESS(status)) {
|
||
RtlFreeUnicodeString(&unicodeKeyName);
|
||
goto local_exit0;
|
||
}
|
||
|
||
//
|
||
// Prepare newly created registry key name for returning to caller
|
||
//
|
||
|
||
*(PWSTR)unicodeBuffer = OBJ_NAME_PATH_SEPARATOR;
|
||
unicodeInstanceName.Buffer = (PWSTR)unicodeBuffer;
|
||
unicodeInstanceName.Length += sizeof(WCHAR);
|
||
unicodeInstanceName.MaximumLength += sizeof(WCHAR);
|
||
PiWstrToUnicodeString(&rootKeyName, REGSTR_KEY_ROOTENUM);
|
||
RtlInitUnicodeString(&tmpKeyName, L"\\");
|
||
IopConcatenateUnicodeStrings(&unicodeString, &tmpKeyName, &unicodeKeyName);
|
||
RtlFreeUnicodeString(&unicodeKeyName);
|
||
IopConcatenateUnicodeStrings(&tmpKeyName, &rootKeyName, &unicodeString);
|
||
RtlFreeUnicodeString(&unicodeString);
|
||
IopConcatenateUnicodeStrings(KeyName, &tmpKeyName, &unicodeInstanceName);
|
||
|
||
if (disposition == REG_CREATED_NEW_KEY) {
|
||
|
||
//
|
||
// Create all the default value entry for the newly created key.
|
||
// Service = ServiceKeyName
|
||
// FoundAtEnum = 1
|
||
// BaseDevicePath = HTREE\ROOT\0000 a default parent
|
||
// Class = "Unkown"
|
||
// ClassGUID = GUID for unknown class
|
||
// Problem = 0
|
||
// StatusFlags = 0
|
||
// ConfigFlags = 0
|
||
|
||
//
|
||
// Create "Control" subkey with "NewlyCreated" value key
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_CONTROL);
|
||
status = IopOpenRegistryKey(&handle,
|
||
*ReturnedHandle,
|
||
&unicodeValueName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEWLY_CREATED);
|
||
tmpValue = 0;
|
||
ZwSetValueKey(handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(tmpValue)
|
||
);
|
||
ZwClose(handle);
|
||
}
|
||
|
||
handle = *ReturnedHandle;
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_SERVICE);
|
||
p = (PWSTR)ExAllocatePool(PagedPool,
|
||
ServiceKeyName->Length + sizeof(UNICODE_NULL));
|
||
RtlMoveMemory(p, ServiceKeyName->Buffer, ServiceKeyName->Length);
|
||
p[ServiceKeyName->Length / sizeof (WCHAR)] = UNICODE_NULL;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
p,
|
||
ServiceKeyName->Length + sizeof(UNICODE_NULL)
|
||
);
|
||
ExFreePool(p);
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_FOUNDATENUM);
|
||
tmpValue = 1;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(tmpValue)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_CLASS);
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
REGSTR_VALUE_UNKNOWN,
|
||
sizeof(REGSTR_VALUE_UNKNOWN)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_CLASSGUID);
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
REGSTR_VALUE_UNKNOWN_CLASS_GUID,
|
||
sizeof(REGSTR_VALUE_UNKNOWN_CLASS_GUID)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_PROBLEM);
|
||
tmpValue = 0;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(tmpValue)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_STATUSFLAGS);
|
||
tmpValue = 0;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&tmpValue,
|
||
sizeof(tmpValue)
|
||
);
|
||
|
||
//
|
||
// Initialize BaseDevicePath to HTREE\ROOT\0
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_BASEDEVICEPATH);
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
REGSTR_VALUE_HTREE_ROOT_0,
|
||
sizeof(REGSTR_VALUE_HTREE_ROOT_0)
|
||
);
|
||
|
||
//
|
||
// Initialize DeviceDesc= value entry. If the service key has a "DisplayName"
|
||
// value entry, it is used as the DeviceDesc value. Otherwise, the word "Device"
|
||
// is appended to the service key name and stored as the "DeviceDesc"
|
||
//
|
||
|
||
status = IopOpenServiceEnumKeys(ServiceKeyName,
|
||
KEY_READ,
|
||
&handle,
|
||
NULL,
|
||
FALSE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
BOOLEAN freeUnicodeString = FALSE;
|
||
|
||
keyValueInformation = NULL;
|
||
unicodeString.Length = 0;
|
||
status = IopGetRegistryValue(handle,
|
||
REGSTR_VALUE_DISPLAY_NAME,
|
||
&keyValueInformation
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
if (keyValueInformation->Type == REG_SZ) {
|
||
if (keyValueInformation->DataLength > sizeof(UNICODE_NULL)) {
|
||
IopRegistryDataToUnicodeString(&unicodeString,
|
||
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength
|
||
);
|
||
}
|
||
}
|
||
}
|
||
if (unicodeString.Length == 0) {
|
||
|
||
//
|
||
// No DispalyName. So try to read the "Type=" value entry
|
||
//
|
||
|
||
PKEY_VALUE_FULL_INFORMATION fullInfo;
|
||
ULONG serviceType;
|
||
|
||
status = IopGetRegistryValue(handle,
|
||
REGSTR_VAL_SHARES_TYPE,
|
||
&fullInfo
|
||
);
|
||
serviceType = 0;
|
||
if (NT_SUCCESS(status)) {
|
||
if (fullInfo->Type == REG_DWORD) {
|
||
if (fullInfo->DataLength >= sizeof(ULONG)) {
|
||
serviceType = *(PULONG)KEY_VALUE_DATA(fullInfo);
|
||
}
|
||
}
|
||
ExFreePool(fullInfo);
|
||
}
|
||
|
||
//
|
||
// No DisplayName value in the service. if the Type= SERVICE_DRIVER we will append
|
||
// "Device" Otherwise we append "Service"
|
||
//
|
||
|
||
if (serviceType & SERVICE_DRIVER) {
|
||
RtlInitUnicodeString(&unicodeValueName, L" Device");
|
||
} else {
|
||
RtlInitUnicodeString(&unicodeValueName, L" Service");
|
||
}
|
||
IopConcatenateUnicodeStrings(&unicodeString, ServiceKeyName, &unicodeValueName);
|
||
freeUnicodeString = TRUE;
|
||
}
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_DEVICE_DESC);
|
||
ZwSetValueKey(*ReturnedHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
unicodeString.Buffer,
|
||
unicodeString.Length + sizeof(UNICODE_NULL)
|
||
);
|
||
if (freeUnicodeString) {
|
||
RtlFreeUnicodeString(&unicodeString);
|
||
}
|
||
if (keyValueInformation) {
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
ZwClose(handle);
|
||
}
|
||
|
||
//
|
||
// Open System\CCS\Enum\HTREE\ROOT\0 and append the new device instance
|
||
// path to its AttachedComponents value key.
|
||
//
|
||
|
||
status = IopOpenRegistryKey(&handle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
goto local_exit;
|
||
}
|
||
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_VALUE_HTREE_ROOT_0);
|
||
status = IopOpenRegistryKey(&hTreeHandle,
|
||
handle,
|
||
&unicodeKeyName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
ZwClose(handle);
|
||
if (!NT_SUCCESS(status)) {
|
||
goto local_exit;
|
||
}
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_ATTACHEDCOMPONENTS);
|
||
IopRemoveStringFromValueKey(hTreeHandle,REGSTR_VALUE_ATTACHEDCOMPONENTS, KeyName);
|
||
IopAppendStringToValueKey(hTreeHandle, REGSTR_VALUE_ATTACHEDCOMPONENTS, KeyName, TRUE);
|
||
ZwClose(hTreeHandle);
|
||
#if 0
|
||
//
|
||
// Read AttachedComponent value entry
|
||
//
|
||
|
||
status = IopGetRegistryValue(hTreeHandle,
|
||
REGSTR_VALUE_ATTACHEDCOMPONENTS,
|
||
&keyValueInformation
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
if (keyValueInformation->Type == REG_MULTI_SZ) {
|
||
tmpValue = keyValueInformation->DataLength;
|
||
if (tmpValue > 2 * sizeof(UNICODE_NULL)) {
|
||
pc1= (PUCHAR)KEY_VALUE_DATA(keyValueInformation);
|
||
multiLength = tmpValue + KeyName->Length + sizeof(WCHAR);
|
||
pc2 = (PUCHAR)ExAllocatePool(PagedPool, multiLength);
|
||
if (!pc2) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
ExFreePool(keyValueInformation);
|
||
ZwClose(hTreeHandle);
|
||
goto local_exit;
|
||
}
|
||
tmpValue -= sizeof(WCHAR); // don't copy the terminating UNICODE_NULL
|
||
RtlMoveMemory(pc2, pc1, tmpValue);
|
||
pc1 = pc2;
|
||
pc2 += tmpValue;
|
||
RtlMoveMemory(pc2, KeyName->Buffer, KeyName->Length);
|
||
pc2 += KeyName->Length;
|
||
p = (PWSTR)pc2;
|
||
*p = UNICODE_NULL;
|
||
p++;
|
||
*p = UNICODE_NULL;
|
||
}
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
|
||
if (multiLength == 0) {
|
||
multiLength = KeyName->Length + 2 * sizeof(UNICODE_NULL);
|
||
pc1 = pc2 = (PUCHAR) ExAllocatePool(PagedPool, multiLength);
|
||
if (!pc1) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
ZwClose(hTreeHandle);
|
||
goto local_exit;
|
||
}
|
||
RtlMoveMemory(pc1, KeyName->Buffer, KeyName->Length);
|
||
pc2 += KeyName->Length;
|
||
p = (PWSTR)pc2;
|
||
*p = UNICODE_NULL;
|
||
p++;
|
||
*p = UNICODE_NULL;
|
||
}
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_ATTACHEDCOMPONENTS);
|
||
ZwSetValueKey(
|
||
hTreeHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_MULTI_SZ,
|
||
pc1,
|
||
multiLength
|
||
);
|
||
ExFreePool(pc1);
|
||
ZwClose(hTreeHandle);
|
||
#endif
|
||
#if 0
|
||
//
|
||
// Create a device instance under CCS\Control\Class\Guid Unknown
|
||
//
|
||
|
||
status = IopOpenRegistryKey(&handle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetControlClass,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_VALUE_UNKNOWN_CLASS_GUID);
|
||
status = IopOpenRegistryKeyPersist(&guidHandle,
|
||
handle,
|
||
&unicodeKeyName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE,
|
||
NULL
|
||
);
|
||
ZwClose(handle);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Next try to read the "NextInstance" value entry to determine the
|
||
// instance number for the new node.
|
||
//
|
||
|
||
status = IopGetRegistryValue (guidHandle,
|
||
REGSTR_VALUE_NEXT_INSTANCE,
|
||
&keyValueInformation);
|
||
instance = 0;
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
instance = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
|
||
//
|
||
// Update NextInstance= for the madeup key
|
||
// BUGBUG What about if SetValueKey fails??
|
||
//
|
||
|
||
instance++;
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NEXT_INSTANCE);
|
||
ZwSetValueKey(guidHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&instance,
|
||
sizeof(instance)
|
||
);
|
||
|
||
instance--;
|
||
PiUlongToInstanceKeyUnicodeString(&unicodeInstanceName,
|
||
unicodeBuffer + sizeof(WCHAR),
|
||
20 - sizeof(WCHAR),
|
||
instance
|
||
);
|
||
status = IopOpenRegistryKeyPersist(&handle,
|
||
guidHandle,
|
||
&unicodeInstanceName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE,
|
||
NULL
|
||
);
|
||
ZwClose(guidHandle);
|
||
if (NT_SUCCESS(status)) {
|
||
*(PWSTR)unicodeBuffer = OBJ_NAME_PATH_SEPARATOR;
|
||
unicodeInstanceName.Buffer = (PWSTR)unicodeBuffer;
|
||
unicodeInstanceName.Length += sizeof(WCHAR);
|
||
unicodeInstanceName.MaximumLength += sizeof(WCHAR);
|
||
PiWstrToUnicodeString(&rootKeyName, REGSTR_VALUE_UNKNOWN_CLASS_GUID);
|
||
IopConcatenateUnicodeStrings(&unicodeKeyName, &rootKeyName, &unicodeInstanceName);
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_DRIVER);
|
||
ZwSetValueKey(*ReturnedHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
unicodeKeyName.Buffer,
|
||
unicodeKeyName.Length + sizeof(UNICODE_NULL)
|
||
);
|
||
ZwClose(handle);
|
||
RtlFreeUnicodeString(&unicodeKeyName);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// Create new value entry under ServiceKeyName\Enum to reflect the newly
|
||
// added made-up device instance node.
|
||
//
|
||
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
KeLeaveCriticalRegion();
|
||
releaseResource = FALSE;
|
||
|
||
status = PpDeviceRegistration(
|
||
KeyName,
|
||
TRUE
|
||
);
|
||
|
||
if (ResourceOwned) {
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
||
}
|
||
local_exit:
|
||
RtlFreeUnicodeString(&tmpKeyName);
|
||
if (!NT_SUCCESS( status )) {
|
||
|
||
//
|
||
// There is no registry key for the ServiceKeyName information.
|
||
//
|
||
|
||
ZwClose(*ReturnedHandle);
|
||
RtlFreeUnicodeString(KeyName);
|
||
}
|
||
local_exit0:
|
||
if (releaseResource) {
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopRemoveStringFromValueKey (
|
||
IN HANDLE Handle,
|
||
IN PWSTR ValueName,
|
||
IN PUNICODE_STRING String
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine remove a string from a value entry specified by ValueName
|
||
under an already opened registry handle. Note, this routine will not
|
||
delete the ValueName entry even it becomes empty after the removal.
|
||
|
||
Parameters:
|
||
|
||
Handle - Supplies the handle to a registry key whose value entry will
|
||
be modified.
|
||
|
||
ValueName - Supplies a unicode string to specify the value entry.
|
||
|
||
String - Supplies a unicode string to remove from value entry.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
UNICODE_STRING unicodeString;
|
||
PWSTR nextString, currentString;
|
||
ULONG length, leftLength;
|
||
NTSTATUS status;
|
||
BOOLEAN found = FALSE;
|
||
|
||
if (String == NULL || String->Length / sizeof(WCHAR) == 0) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Read registry value entry data
|
||
//
|
||
|
||
status = IopGetRegistryValue(Handle, ValueName, &keyValueInformation);
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
return status;
|
||
} else if ((keyValueInformation->Type != REG_MULTI_SZ) ||
|
||
(keyValueInformation->DataLength == 0)) {
|
||
|
||
ExFreePool(keyValueInformation);
|
||
return (keyValueInformation->Type == REG_MULTI_SZ) ? STATUS_SUCCESS
|
||
: STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Scan through the multi_sz string to find the matching string
|
||
// and remove it.
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
currentString = (PWSTR)KEY_VALUE_DATA(keyValueInformation);
|
||
leftLength = keyValueInformation->DataLength;
|
||
while (!found && leftLength >= String->Length + sizeof(WCHAR)) {
|
||
unicodeString.Buffer = currentString;
|
||
length = wcslen( currentString ) * sizeof( WCHAR );
|
||
unicodeString.Length = (USHORT)length;
|
||
length += sizeof(UNICODE_NULL);
|
||
unicodeString.MaximumLength = (USHORT)length;
|
||
nextString = currentString + length / sizeof(WCHAR);
|
||
leftLength -= length;
|
||
|
||
if (RtlEqualUnicodeString(&unicodeString, String, TRUE)) {
|
||
found = TRUE;
|
||
RtlMoveMemory(currentString, nextString, leftLength);
|
||
RtlInitUnicodeString(&unicodeString, ValueName);
|
||
status = ZwSetValueKey(
|
||
Handle,
|
||
&unicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_MULTI_SZ,
|
||
KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength - length
|
||
);
|
||
break;
|
||
} else {
|
||
currentString = nextString;
|
||
}
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopAppendStringToValueKey (
|
||
IN HANDLE Handle,
|
||
IN PWSTR ValueName,
|
||
IN PUNICODE_STRING String,
|
||
IN BOOLEAN Create
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine appends a string to a value entry specified by ValueName
|
||
under an already opened registry handle. If the ValueName is not present
|
||
and Create is TRUE, a new value entry will be created using the name
|
||
ValueName.
|
||
|
||
Parameters:
|
||
|
||
Handle - Supplies the handle to a registry key whose value entry will
|
||
be modified.
|
||
|
||
ValueName - Supplies a pointer to a string to specify the value entry.
|
||
|
||
String - Supplies a unicode string to append to the value entry.
|
||
|
||
Create - Supplies a BOOLEAN variable to indicate if the ValueName
|
||
value entry should be created if it is not present.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
PWSTR destinationString, p;
|
||
UNICODE_STRING unicodeValueName;
|
||
ULONG size;
|
||
NTSTATUS status;
|
||
|
||
if ( !String || (String->Length < sizeof(WCHAR)) ) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Read registry value entry data
|
||
//
|
||
|
||
status = IopGetRegistryValue(Handle, ValueName, &keyValueInformation);
|
||
|
||
if(!NT_SUCCESS( status )) {
|
||
if (status == STATUS_OBJECT_NAME_NOT_FOUND && Create) {
|
||
|
||
//
|
||
// if no valid entry exists and user said ok to create one
|
||
//
|
||
|
||
keyValueInformation = NULL;
|
||
} else {
|
||
return status;
|
||
}
|
||
} else if(keyValueInformation->Type != REG_MULTI_SZ) {
|
||
|
||
ExFreePool(keyValueInformation);
|
||
|
||
if(Create) {
|
||
keyValueInformation = NULL;
|
||
} else {
|
||
return STATUS_INVALID_PARAMETER_2;
|
||
}
|
||
|
||
} else if(keyValueInformation->DataLength < sizeof(WCHAR)) {
|
||
|
||
ExFreePool(keyValueInformation);
|
||
keyValueInformation = NULL;
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer to hold new data for the specified key value entry
|
||
// Make sure the buffer is at least an empty MULTI_SZ big.
|
||
//
|
||
|
||
if (keyValueInformation) {
|
||
size = keyValueInformation->DataLength + String->Length + sizeof (UNICODE_NULL);
|
||
} else {
|
||
size = String->Length + 2 * sizeof(UNICODE_NULL);
|
||
}
|
||
|
||
destinationString = p = (PWSTR)ExAllocatePool(PagedPool, size);
|
||
if (destinationString == NULL) {
|
||
if (keyValueInformation) {
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Copy the existing data to our newly allocated buffer, if any
|
||
//
|
||
|
||
if (keyValueInformation) {
|
||
|
||
//
|
||
// Note we need to remove a UNICODE_NULL because the
|
||
// MULTI_SZ has two terminating UNICODE_NULL.
|
||
//
|
||
|
||
RtlMoveMemory(p,
|
||
KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength - sizeof(WCHAR)
|
||
);
|
||
p += keyValueInformation->DataLength / sizeof(WCHAR) - 1;
|
||
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
|
||
//
|
||
// Append the user specified unicode string to our buffer
|
||
//
|
||
RtlMoveMemory(p,
|
||
String->Buffer,
|
||
String->Length
|
||
);
|
||
p += String->Length / sizeof(WCHAR);
|
||
*p = UNICODE_NULL;
|
||
p++;
|
||
*p = UNICODE_NULL;
|
||
|
||
//
|
||
// Finally write the data to the specified registy value entry
|
||
//
|
||
|
||
RtlInitUnicodeString(&unicodeValueName, ValueName);
|
||
status = ZwSetValueKey(
|
||
Handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_MULTI_SZ,
|
||
destinationString,
|
||
size
|
||
);
|
||
|
||
ExFreePool(destinationString);
|
||
return status;
|
||
}
|
||
|
||
BOOLEAN
|
||
IopConcatenateUnicodeStrings (
|
||
OUT PUNICODE_STRING Destination,
|
||
IN PUNICODE_STRING String1,
|
||
IN PUNICODE_STRING String2 OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns a buffer containing the concatenation of the
|
||
two specified strings. Since String2 is optional, this function may
|
||
also be used to make a copy of a unicode string. Paged pool space
|
||
is allocated for the destination string. Caller must release the
|
||
space once done with it.
|
||
|
||
Parameters:
|
||
|
||
Destination - Supplies a variable to receive the handle of the
|
||
newly created key.
|
||
|
||
String1 - Supplies a pointer to the frist UNICODE_STRING.
|
||
|
||
String2 - Supplies an optional pointer to the second UNICODE_STRING.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG length;
|
||
PWSTR buffer;
|
||
|
||
length = String1->Length + sizeof(UNICODE_NULL);
|
||
if(ARGUMENT_PRESENT(String2)) {
|
||
length += String2->Length;
|
||
}
|
||
buffer = (PWSTR)ExAllocatePool(PagedPool, length);
|
||
if (!buffer) {
|
||
return FALSE;
|
||
}
|
||
Destination->Buffer = buffer;
|
||
Destination->Length = (USHORT)length - sizeof(UNICODE_NULL);
|
||
Destination->MaximumLength = (USHORT)length;
|
||
RtlMoveMemory (Destination->Buffer, String1->Buffer, String1->Length);
|
||
if(ARGUMENT_PRESENT(String2)) {
|
||
RtlMoveMemory((PUCHAR)Destination->Buffer + String1->Length,
|
||
String2->Buffer,
|
||
String2->Length
|
||
);
|
||
}
|
||
buffer[length / sizeof(WCHAR) - 1] = UNICODE_NULL;
|
||
return TRUE;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopPrepareDriverLoading (
|
||
IN PUNICODE_STRING KeyName,
|
||
IN HANDLE KeyHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine first checks if the driver is loadable. If its a
|
||
PnP driver, it will always be loaded (we trust it to do the right
|
||
things.) If it is a legacy driver, we need to check if its device
|
||
has been disabled. Once we decide to load the driver, the Enum
|
||
subkey of the service node will be checked for duplicates, if any.
|
||
|
||
Parameters:
|
||
|
||
KeyName - Supplies a pointer to the driver's service key unicode string
|
||
|
||
KeyHandle - Supplies a handle to the driver service node in the registry
|
||
that describes the driver to be loaded.
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the load operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL;
|
||
ULONG i, j, count = 0, totalCount, configSize, actualLength;
|
||
HANDLE serviceEnumHandle = NULL, sysEnumXxxHandle, controlHandle;
|
||
UNICODE_STRING unicodeKeyName, unicodeValueName, unicodeInstanceName;
|
||
UCHAR unicodeBuffer[20];
|
||
STRING keyString;
|
||
HAL_BUS_INFORMATION busInfo1, busInfo2;
|
||
ULONG deviceFlags;
|
||
PUCHAR configuration1 = NULL, configuration2 = NULL;
|
||
BOOLEAN IsPlugPlayDriver = FALSE;
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
||
|
||
//
|
||
// First clear the NtDevicePaths= value entry under the service key
|
||
// (Yes, we want to clear it even the driver won't get loaded.)
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_NTDEVICEPATHS);
|
||
ZwDeleteValueKey(KeyHandle, &unicodeValueName);
|
||
|
||
//
|
||
// Check should this driver be loaded. If it is a PnP driver and has at least
|
||
// one device instance enabled, it will be loaded. If it is a legacy driver,
|
||
// we need to check if its device has been disabled.
|
||
// Only PlugPlay drivers have PlugPlayServiceType defined.
|
||
//
|
||
|
||
status = IopGetRegistryValue(KeyHandle, REGSTR_VALUE_PLUGPLAY_SERVICE_TYPE, &keyValueInformation);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
//
|
||
// BUGBUG - For SUR, we load the driver only if device instance list is not
|
||
// empty and at least one device instance is enabled.
|
||
//
|
||
|
||
if (!IopIsDeviceInstanceEnabled(KeyName, KeyHandle)) {
|
||
ExFreePool(keyValueInformation);
|
||
status = STATUS_PLUGPLAY_NO_DEVICE;
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// End of SUR support
|
||
//
|
||
|
||
IsPlugPlayDriver = TRUE;
|
||
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
if (!IsPlugPlayDriver) {
|
||
|
||
//
|
||
// If this is a legacy driver, then we need to check if its
|
||
// device has been disabled. (There should be NO more than one device
|
||
// instance for legacy driver.)
|
||
//
|
||
|
||
if (!IopIsDeviceInstanceEnabled(KeyName, KeyHandle)) {
|
||
|
||
//
|
||
// If the device is not enabled, it may be because there is no device
|
||
// instance. If this is the case, we need to create a madeup key as
|
||
// the device instance for the legacy driver.
|
||
//
|
||
|
||
//
|
||
// First open registry ServiceKeyName\Enum branch
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_KEY_ENUM);
|
||
status = IopOpenRegistryKey(&serviceEnumHandle,
|
||
KeyHandle,
|
||
&unicodeKeyName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE
|
||
);
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Find out how many device instances listed in the ServiceName's
|
||
// Enum key.
|
||
//
|
||
|
||
status = IopGetRegistryValue ( serviceEnumHandle,
|
||
REGSTR_VALUE_COUNT,
|
||
&keyValueInformation
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
count = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
} else if (status != STATUS_OBJECT_PATH_NOT_FOUND &&
|
||
status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
|
||
ZwClose(serviceEnumHandle);
|
||
goto exit;
|
||
}
|
||
|
||
if (count == 0) {
|
||
|
||
//
|
||
// If there is no Enum key or instance under Enum for the
|
||
// legacy driver we will create a madeup node for it.
|
||
//
|
||
|
||
status = IopCreateMadeupNode(KeyName,
|
||
&sysEnumXxxHandle,
|
||
&unicodeKeyName,
|
||
&i,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ZwClose(serviceEnumHandle);
|
||
goto exit;
|
||
}
|
||
RtlFreeUnicodeString(&unicodeKeyName);
|
||
|
||
//
|
||
// Set DN_STARTED to the StatusFlag of the madeup key
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_STATUSFLAGS);
|
||
i = DN_STARTED;
|
||
ZwSetValueKey(
|
||
sysEnumXxxHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&i,
|
||
sizeof(i)
|
||
);
|
||
|
||
//
|
||
// Create and set Control\ActiveService value
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_CONTROL);
|
||
status = IopOpenRegistryKey(&controlHandle,
|
||
sysEnumXxxHandle,
|
||
&unicodeValueName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VAL_ACTIVESERVICE);
|
||
ZwSetValueKey(
|
||
controlHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
KeyName->Buffer,
|
||
KeyName->Length + sizeof(UNICODE_NULL)
|
||
);
|
||
|
||
ZwClose(controlHandle);
|
||
}
|
||
|
||
ZwClose(sysEnumXxxHandle);
|
||
count++;
|
||
} else {
|
||
ZwClose(serviceEnumHandle);
|
||
status = STATUS_PLUGPLAY_NO_DEVICE;
|
||
goto exit;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Next try to identify the duplicated entries under the ServiceKey\Enum key
|
||
// and remove them. (In case of legacy driver, the Enum key should have zero
|
||
// or one instance only.)
|
||
//
|
||
|
||
if (serviceEnumHandle == NULL) {
|
||
|
||
//
|
||
// If the ServiceName\Enum key is not open yet, open it.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_KEY_ENUM);
|
||
status = IopOpenRegistryKey(&serviceEnumHandle,
|
||
KeyHandle,
|
||
&unicodeKeyName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE
|
||
);
|
||
PNP_ASSERT(NT_SUCCESS(status),
|
||
"PrepareDriverLoading:Can NOT open service Enum key");
|
||
if (!NT_SUCCESS( status )) {
|
||
|
||
//
|
||
// We want to let the driver to be loaded even though we can
|
||
// not eliminate the duplicates.
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
goto exit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Find out how many device instances listed in the ServiceName's
|
||
// Enum key.
|
||
//
|
||
|
||
if (count == 0) {
|
||
status = IopGetRegistryValue ( serviceEnumHandle,
|
||
REGSTR_VALUE_COUNT,
|
||
&keyValueInformation
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
ZwClose(serviceEnumHandle);
|
||
status = STATUS_SUCCESS;
|
||
goto exit;
|
||
} else {
|
||
count = 0;
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
count = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there are more than 1 device instances in the service's Enum
|
||
// branch, we need to eliminate the duplicates.
|
||
//
|
||
|
||
totalCount = count;
|
||
if (count > 1) {
|
||
keyString.Buffer = NULL;
|
||
|
||
//
|
||
// For each instance, we check if it is a duplicate of any other instance
|
||
//
|
||
|
||
for (i = 0; i < count; i++) {
|
||
PiUlongToUnicodeString(&unicodeInstanceName, unicodeBuffer, 20, i);
|
||
status = IopGetRegistryValue(serviceEnumHandle,
|
||
unicodeInstanceName.Buffer,
|
||
&keyValueInformation
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
ZwClose(serviceEnumHandle);
|
||
status = STATUS_SUCCESS;
|
||
goto exit;
|
||
}
|
||
|
||
if(keyValueInformation->Type == REG_SZ) {
|
||
IopRegistryDataToUnicodeString(&unicodeKeyName,
|
||
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength
|
||
);
|
||
} else {
|
||
unicodeKeyName.Length = 0;
|
||
}
|
||
|
||
if (unicodeKeyName.Length) {
|
||
if (keyString.Buffer) {
|
||
RtlFreeAnsiString(&keyString);
|
||
keyString.Buffer = NULL;
|
||
}
|
||
RtlUnicodeStringToAnsiString(&keyString, &unicodeKeyName, TRUE);
|
||
ExFreePool(keyValueInformation);
|
||
} else {
|
||
|
||
//
|
||
// If the device instance key does not contain a string to
|
||
// system Enum tree, we will simply remove the instance entry.
|
||
//
|
||
|
||
ExFreePool(keyValueInformation);
|
||
ZwDeleteValueKey(serviceEnumHandle, &unicodeInstanceName);
|
||
continue;
|
||
}
|
||
|
||
if (strstr(keyString.Buffer, "Root\\")) {
|
||
|
||
//
|
||
// if the instance of the device is located under System\CCS\Enum\Root
|
||
// we need to check if it is a duplicate of other instance.
|
||
// Otherwise, it is found by enumerator. It won't be a duplicate.
|
||
//
|
||
|
||
if (!configuration1) {
|
||
configSize = PNP_SCRATCH_BUFFER_SIZE;
|
||
|
||
retryAlloc1:
|
||
configuration1 = ExAllocatePool(PagedPool, configSize);
|
||
if (!configuration1) {
|
||
continue;
|
||
}
|
||
}
|
||
status = IopQueryDeviceConfiguration (
|
||
KeyName,
|
||
i,
|
||
&busInfo1,
|
||
&deviceFlags,
|
||
(PCM_RESOURCE_LIST)configuration1,
|
||
configSize,
|
||
&actualLength
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
if (status == STATUS_BUFFER_OVERFLOW ||
|
||
status == STATUS_BUFFER_TOO_SMALL) {
|
||
configSize = actualLength;
|
||
ExFreePool(configuration1);
|
||
goto retryAlloc1;
|
||
} else {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check current instance against all the instances except self.
|
||
//
|
||
|
||
for (j = 0; j < count; j++) {
|
||
if (j == i) {
|
||
continue;
|
||
}
|
||
if (!configuration2) {
|
||
configSize = PNP_SCRATCH_BUFFER_SIZE;
|
||
|
||
retryAlloc2:
|
||
configuration2 = ExAllocatePool(PagedPool, configSize);
|
||
if (!configuration2) {
|
||
continue;
|
||
}
|
||
}
|
||
status = IopQueryDeviceConfiguration (
|
||
KeyName,
|
||
j,
|
||
&busInfo2,
|
||
&deviceFlags,
|
||
(PCM_RESOURCE_LIST)configuration2,
|
||
configSize,
|
||
&actualLength
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
if (status == STATUS_BUFFER_OVERFLOW ||
|
||
status == STATUS_BUFFER_TOO_SMALL) {
|
||
ExFreePool(configuration2);
|
||
configSize = actualLength;
|
||
goto retryAlloc2;
|
||
} else {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (IopIsDuplicatedDevices((PCM_RESOURCE_LIST)&configuration1,
|
||
(PCM_RESOURCE_LIST)&configuration2,
|
||
&busInfo1,
|
||
&busInfo2)) {
|
||
|
||
//
|
||
// instance i is a duplicate of instance j
|
||
// we remember this by deleting the instance i
|
||
//
|
||
|
||
IopMarkDuplicateDevice (KeyName, i, KeyName, j);
|
||
|
||
status = ZwDeleteValueKey(
|
||
serviceEnumHandle,
|
||
&unicodeInstanceName
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
totalCount--;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (configuration2) {
|
||
ExFreePool(configuration2);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Clean up allocated pool space
|
||
//
|
||
|
||
if (configuration1) {
|
||
ExFreePool(configuration1);
|
||
}
|
||
if (keyString.Buffer) {
|
||
RtlFreeAnsiString(&keyString);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, we need to physically reorganize the instances under the
|
||
// ServiceKey\Enum key to make them contiguous.
|
||
//
|
||
|
||
if (totalCount != count) {
|
||
j = 0;
|
||
i = 0;
|
||
while (i < count) {
|
||
PiUlongToUnicodeString(&unicodeInstanceName, unicodeBuffer, 20, i);
|
||
status = IopGetRegistryValue(serviceEnumHandle,
|
||
unicodeInstanceName.Buffer,
|
||
&keyValueInformation
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
if (i != j) {
|
||
|
||
//
|
||
// Need to change the instance i to instance j
|
||
//
|
||
|
||
ZwDeleteValueKey(serviceEnumHandle, &unicodeInstanceName);
|
||
|
||
PiUlongToUnicodeString(&unicodeInstanceName, unicodeBuffer, 20, j);
|
||
ZwSetValueKey (serviceEnumHandle,
|
||
&unicodeInstanceName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
(PVOID)KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength
|
||
);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
j++;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
//
|
||
// Don't forget to update the "Count=" and "NextInstance=" value entries
|
||
//
|
||
|
||
PiWstrToUnicodeString( &unicodeValueName, REGSTR_VALUE_COUNT);
|
||
|
||
ZwSetValueKey(serviceEnumHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&j,
|
||
sizeof (j)
|
||
);
|
||
PiWstrToUnicodeString( &unicodeValueName, REGSTR_VALUE_NEXT_INSTANCE);
|
||
|
||
ZwSetValueKey(serviceEnumHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&j,
|
||
sizeof (j)
|
||
);
|
||
}
|
||
ZwClose(serviceEnumHandle);
|
||
status = STATUS_SUCCESS;
|
||
exit:
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
KeLeaveCriticalRegion();
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopServiceInstanceToDeviceInstance (
|
||
IN HANDLE ServiceKeyHandle OPTIONAL,
|
||
IN PUNICODE_STRING ServiceKeyName OPTIONAL,
|
||
IN ULONG ServiceInstanceOrdinal,
|
||
OUT PUNICODE_STRING DeviceInstanceRegistryPath OPTIONAL,
|
||
OUT PHANDLE DeviceInstanceHandle OPTIONAL,
|
||
IN ACCESS_MASK DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reads the service node enum entry to find the desired device instance
|
||
under the System\Enum tree. It then optionally returns the registry path of the
|
||
specified device instance (relative to HKLM\System\Enum) and an open handle
|
||
to that registry key.
|
||
|
||
It is the caller's responsibility to close the handle returned if
|
||
DeviceInstanceHandle is supplied, and also to free the (PagedPool) memory
|
||
allocated for the unicode string buffer of DeviceInstanceRegistryPath, if
|
||
supplied.
|
||
|
||
Parameters:
|
||
|
||
ServiceKeyHandle - Optionally, supplies a handle to the driver service node in the
|
||
registry that controls this device instance. If this argument is not specified,
|
||
then ServiceKeyName is used to specify the service entry.
|
||
|
||
ServiceKeyName - Optionally supplies the name of the service entry that controls
|
||
the device instance. This must be specified if ServiceKeyHandle isn't given.
|
||
|
||
ServiceInstanceOrdinal - Supplies the instance value under the service entry's
|
||
volatile Enum subkey that references the desired device instance.
|
||
|
||
DeviceInstanceRegistryPath - Optionally, supplies a pointer to a unicode string
|
||
that will be initialized with the registry path (relative to HKLM\System\Enum)
|
||
to the device instance key.
|
||
|
||
DeviceInstanceHandle - Optionally, supplies a pointer to a variable that will
|
||
receive a handle to the opened device instance registry key.
|
||
|
||
DesiredAccess - If DeviceInstanceHandle is specified (i.e., the device instance
|
||
key is to be opened), then this variable specifies the access that is needed
|
||
to this key.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating whether the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
WCHAR unicodeBuffer[20];
|
||
UNICODE_STRING unicodeKeyName;
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
||
//
|
||
// Open registry ServiceKeyName\Enum branch
|
||
//
|
||
if(ARGUMENT_PRESENT(ServiceKeyHandle)) {
|
||
|
||
PiWstrToUnicodeString(&unicodeKeyName, REGSTR_KEY_ENUM);
|
||
status = IopOpenRegistryKey(&handle,
|
||
ServiceKeyHandle,
|
||
&unicodeKeyName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
} else {
|
||
|
||
status = IopOpenServiceEnumKeys(ServiceKeyName,
|
||
KEY_READ,
|
||
NULL,
|
||
&handle,
|
||
FALSE
|
||
);
|
||
}
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
|
||
//
|
||
// There is no registry key for the ServiceKeyName\Enum information.
|
||
//
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Read a path to System\Enum hardware tree branch specified by the service
|
||
// instance ordinal
|
||
//
|
||
|
||
swprintf(unicodeBuffer, REGSTR_VALUE_STANDARD_ULONG_FORMAT, ServiceInstanceOrdinal);
|
||
status = IopGetRegistryValue ( handle,
|
||
unicodeBuffer,
|
||
&keyValueInformation
|
||
);
|
||
|
||
ZwClose(handle);
|
||
if (!NT_SUCCESS( status )) {
|
||
return status;
|
||
} else {
|
||
if(keyValueInformation->Type == REG_SZ) {
|
||
IopRegistryDataToUnicodeString(&unicodeKeyName,
|
||
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength
|
||
);
|
||
if(!unicodeKeyName.Length) {
|
||
status = STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
} else {
|
||
status = STATUS_INVALID_PLUGPLAY_DEVICE_PATH;
|
||
}
|
||
|
||
if(!NT_SUCCESS(status)) {
|
||
goto PrepareForReturn;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the DeviceInstanceHandle argument was specified, open the device instance
|
||
// key under HKLM\System\CurrentControlSet\Enum
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(DeviceInstanceHandle)) {
|
||
|
||
status = IopOpenRegistryKey(&handle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
|
||
if (NT_SUCCESS( status )) {
|
||
|
||
status = IopOpenRegistryKey (DeviceInstanceHandle,
|
||
handle,
|
||
&unicodeKeyName,
|
||
DesiredAccess,
|
||
FALSE
|
||
);
|
||
ZwClose(handle);
|
||
}
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
goto PrepareForReturn;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the DeviceInstanceRegistryPath argument was specified, then store a
|
||
// copy of the device instance path in the supplied unicode string variable.
|
||
//
|
||
if (ARGUMENT_PRESENT(DeviceInstanceRegistryPath)) {
|
||
|
||
if (!IopConcatenateUnicodeStrings(DeviceInstanceRegistryPath,
|
||
&unicodeKeyName,
|
||
NULL)) {
|
||
|
||
if(ARGUMENT_PRESENT(DeviceInstanceHandle)) {
|
||
ZwClose(*DeviceInstanceHandle);
|
||
}
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
PrepareForReturn:
|
||
|
||
ExFreePool(keyValueInformation);
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopOpenRegistryKeyPersist(
|
||
OUT PHANDLE Handle,
|
||
IN HANDLE BaseHandle OPTIONAL,
|
||
IN PUNICODE_STRING KeyName,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN Create,
|
||
OUT PULONG Disposition OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens or creates a PERSIST (non-volatile) registry key using the name
|
||
passed in based at the BaseHandle node. This name may specify a key
|
||
that is actually a registry path, in which case each intermediate subkey
|
||
will be created (if Create is TRUE).
|
||
|
||
NOTE: Creating a registry path (i.e., more than one of the keys in the path
|
||
do not presently exist) requires that a BaseHandle be specified.
|
||
|
||
Arguments:
|
||
|
||
Handle - Pointer to the handle which will contain the registry key that
|
||
was opened.
|
||
|
||
BaseHandle - Optional handle to the base path from which the key must be opened.
|
||
If KeyName specifies a registry path that must be created, then this parameter
|
||
must be specified, and KeyName must be a relative path.
|
||
|
||
KeyName - Name of the Key that must be opened/created (possibly a registry path)
|
||
|
||
DesiredAccess - Specifies the desired access that the caller needs to
|
||
the key.
|
||
|
||
Create - Determines if the key is to be created if it does not exist.
|
||
|
||
Disposition - If Create is TRUE, this optional pointer receives a ULONG indicating
|
||
whether the key was newly created:
|
||
|
||
REG_CREATED_NEW_KEY - A new Registry Key was created
|
||
REG_OPENED_EXISTING_KEY - An existing Registry Key was opened
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
ULONG disposition, baseHandleIndex = 0, keyHandleIndex = 1, closeBaseHandle;
|
||
HANDLE handles[2];
|
||
BOOLEAN continueParsing;
|
||
PWCHAR pathEndPtr, pathCurPtr, pathBeginPtr;
|
||
ULONG pathComponentLength;
|
||
UNICODE_STRING unicodeString;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
InitializeObjectAttributes(&objectAttributes,
|
||
KeyName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
BaseHandle,
|
||
(PSECURITY_DESCRIPTOR) NULL
|
||
);
|
||
if(Create) {
|
||
//
|
||
// Attempt to create the path as specified. We have to try it this
|
||
// way first, because it allows us to create a key without a BaseHandle
|
||
// (if only the last component of the registry path is not present).
|
||
//
|
||
status = ZwCreateKey(&(handles[keyHandleIndex]),
|
||
DesiredAccess,
|
||
&objectAttributes,
|
||
0,
|
||
(PUNICODE_STRING) NULL,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&disposition
|
||
);
|
||
|
||
if(!((status == STATUS_OBJECT_NAME_NOT_FOUND) && ARGUMENT_PRESENT(BaseHandle))) {
|
||
//
|
||
// Then either we succeeded, or failed, but there's nothing we can do
|
||
// about it. In either case, prepare to return.
|
||
//
|
||
goto PrepareForReturn;
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// Simply attempt to open the path, as specified.
|
||
//
|
||
return ZwOpenKey(Handle,
|
||
DesiredAccess,
|
||
&objectAttributes
|
||
);
|
||
}
|
||
|
||
//
|
||
// If we get to here, then there must be more than one element of the
|
||
// registry path that does not currently exist. We will now parse the
|
||
// specified path, extracting each component and doing a ZwCreateKey on it.
|
||
//
|
||
handles[baseHandleIndex] = NULL;
|
||
handles[keyHandleIndex] = BaseHandle;
|
||
closeBaseHandle = 0;
|
||
continueParsing = TRUE;
|
||
pathBeginPtr = KeyName->Buffer;
|
||
pathEndPtr = (PWCHAR)((PCHAR)pathBeginPtr + KeyName->Length);
|
||
status = STATUS_SUCCESS;
|
||
|
||
while(continueParsing) {
|
||
//
|
||
// There's more to do, so close the previous base handle (if necessary),
|
||
// and replace it with the current key handle.
|
||
//
|
||
if(closeBaseHandle > 1) {
|
||
ZwClose(handles[baseHandleIndex]);
|
||
}
|
||
baseHandleIndex = keyHandleIndex;
|
||
keyHandleIndex = (keyHandleIndex + 1) & 1; // toggle between 0 and 1.
|
||
handles[keyHandleIndex] = NULL;
|
||
|
||
//
|
||
// Extract next component out of the specified registry path.
|
||
//
|
||
for(pathCurPtr = pathBeginPtr;
|
||
((pathCurPtr < pathEndPtr) && (*pathCurPtr != OBJ_NAME_PATH_SEPARATOR));
|
||
pathCurPtr++);
|
||
|
||
if(pathComponentLength = (PCHAR)pathCurPtr - (PCHAR)pathBeginPtr) {
|
||
//
|
||
// Then we have a non-empty path component (key name). Attempt
|
||
// to create this key.
|
||
//
|
||
unicodeString.Buffer = pathBeginPtr;
|
||
unicodeString.Length = unicodeString.MaximumLength = (USHORT)pathComponentLength;
|
||
|
||
InitializeObjectAttributes(&objectAttributes,
|
||
&unicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
handles[baseHandleIndex],
|
||
(PSECURITY_DESCRIPTOR) NULL
|
||
);
|
||
status = ZwCreateKey(&(handles[keyHandleIndex]),
|
||
DesiredAccess,
|
||
&objectAttributes,
|
||
0,
|
||
(PUNICODE_STRING) NULL,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&disposition
|
||
);
|
||
if(NT_SUCCESS(status)) {
|
||
//
|
||
// Increment the closeBaseHandle value, which basically tells us whether
|
||
// the BaseHandle passed in has been 'shifted out' of our way, so that
|
||
// we should start closing our base handles when we're finished with them.
|
||
//
|
||
closeBaseHandle++;
|
||
} else {
|
||
continueParsing = FALSE;
|
||
continue;
|
||
}
|
||
} else {
|
||
//
|
||
// Either a path separator ('\') was included at the beginning of
|
||
// the path, or we hit 2 consecutive separators.
|
||
//
|
||
status = STATUS_INVALID_PARAMETER;
|
||
continueParsing = FALSE;
|
||
continue;
|
||
}
|
||
|
||
if((pathCurPtr == pathEndPtr) ||
|
||
((pathBeginPtr = pathCurPtr + 1) == pathEndPtr)) {
|
||
//
|
||
// Then we've reached the end of the path
|
||
//
|
||
continueParsing = FALSE;
|
||
}
|
||
}
|
||
|
||
if(closeBaseHandle > 1) {
|
||
ZwClose(handles[baseHandleIndex]);
|
||
}
|
||
|
||
PrepareForReturn:
|
||
|
||
if(NT_SUCCESS(status)) {
|
||
*Handle = handles[keyHandleIndex];
|
||
|
||
if(ARGUMENT_PRESENT(Disposition)) {
|
||
*Disposition = disposition;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopOpenServiceEnumKeys (
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
OUT PHANDLE ServiceHandle OPTIONAL,
|
||
OUT PHANDLE ServiceEnumHandle OPTIONAL,
|
||
IN BOOLEAN CreateEnum
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens the HKEY_LOCAL_MACHINE\CurrentControlSet\Services\
|
||
ServiceKeyName and its Enum subkey and returns handles for both key.
|
||
It is caller's responsibility to close the returned handles.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
||
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
||
that caused the driver to load. This is the RegistryPath parameter
|
||
to the DriverEntry routine.
|
||
|
||
DesiredAccess - Specifies the desired access to the keys.
|
||
|
||
ServiceHandle - Supplies a variable to receive a handle to ServiceKeyName.
|
||
A NULL ServiceHandle indicates caller does not want need the handle to
|
||
the ServiceKeyName.
|
||
|
||
ServiceEnumHandle - Supplies a variable to receive a handle to ServiceKeyName\Enum.
|
||
A NULL ServiceEnumHandle indicates caller does not need the handle to
|
||
the ServiceKeyName\Enum.
|
||
|
||
CreateEnum - Supplies a BOOLEAN variable to indicate should the Enum subkey be
|
||
created if not present.
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE handle, serviceHandle, enumHandle;
|
||
UNICODE_STRING enumName;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Open System\CurrentControlSet\Services
|
||
//
|
||
|
||
status = IopOpenRegistryKey(&handle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetServices,
|
||
DesiredAccess,
|
||
FALSE
|
||
);
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Open the registry ServiceKeyName key.
|
||
//
|
||
|
||
status = IopOpenRegistryKey(&serviceHandle,
|
||
handle,
|
||
ServiceKeyName,
|
||
DesiredAccess,
|
||
FALSE
|
||
);
|
||
|
||
ZwClose(handle);
|
||
if (!NT_SUCCESS( status )) {
|
||
|
||
//
|
||
// There is no registry key for the ServiceKeyName information.
|
||
//
|
||
|
||
return status;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(ServiceEnumHandle) || CreateEnum) {
|
||
|
||
//
|
||
// Open registry ServiceKeyName\Enum branch if caller wants
|
||
// the handle or wants to create it.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&enumName, REGSTR_KEY_ENUM);
|
||
status = IopOpenRegistryKey(&enumHandle,
|
||
serviceHandle,
|
||
&enumName,
|
||
DesiredAccess,
|
||
CreateEnum
|
||
);
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
|
||
//
|
||
// There is no registry key for the ServiceKeyName\Enum information.
|
||
//
|
||
|
||
ZwClose(serviceHandle);
|
||
return status;
|
||
}
|
||
if (ARGUMENT_PRESENT(ServiceEnumHandle)) {
|
||
*ServiceEnumHandle = enumHandle;
|
||
} else {
|
||
ZwClose(enumHandle);
|
||
}
|
||
}
|
||
|
||
//
|
||
// if caller wants to have the ServiceKey handle, we return it. Otherwise
|
||
// we close it.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(ServiceHandle)) {
|
||
*ServiceHandle = serviceHandle;
|
||
} else {
|
||
ZwClose(serviceHandle);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopGetDeviceInstanceCsConfigFlags(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
IN ULONG Instance,
|
||
OUT PULONG CsConfigFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves the csconfig flags for the specified device
|
||
which is specified by the instance number under ServiceKeyName\Enum.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
||
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
||
that caused the driver to load.
|
||
|
||
Instance - Supplies the instance value under ServiceKeyName\Enum key
|
||
|
||
CsConfigFlags - Supplies a variable to receive the device's CsConfigFlags
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
||
*CsConfigFlags = 0;
|
||
|
||
status = IopOpenCurrentHwProfileDeviceInstanceKey(&handle,
|
||
ServiceKeyName,
|
||
Instance,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
if(NT_SUCCESS(status)) {
|
||
status = IopGetRegistryValue(handle,
|
||
REGSTR_VALUE_CSCONFIG_FLAGS,
|
||
&keyValueInformation
|
||
);
|
||
if(NT_SUCCESS(status)) {
|
||
if((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
*CsConfigFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
ZwClose(handle);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopSetDeviceInstanceCsConfigFlags(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
IN ULONG Instance,
|
||
IN ULONG CsConfigFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the csconfig flags for the specified device
|
||
which is specified by the instance number under ServiceKeyName\Enum.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
||
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
||
that caused the driver to load. This is the RegistryPath parameter
|
||
to the DriverEntry routine.
|
||
|
||
Instance - Supplies the instance value under ServiceKeyName\Enum key
|
||
|
||
CsConfigFlags - Supplies the device instance's new CsConfigFlags
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE handle;
|
||
NTSTATUS status;
|
||
UNICODE_STRING tempUnicodeString;
|
||
|
||
status = IopOpenCurrentHwProfileDeviceInstanceKey(&handle,
|
||
ServiceKeyName,
|
||
Instance,
|
||
KEY_READ | KEY_WRITE,
|
||
TRUE
|
||
);
|
||
if(NT_SUCCESS(status)) {
|
||
|
||
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_VALUE_CSCONFIG_FLAGS);
|
||
status = ZwSetValueKey(handle,
|
||
&tempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&CsConfigFlags,
|
||
sizeof(CsConfigFlags)
|
||
);
|
||
ZwClose(handle);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopOpenCurrentHwProfileDeviceInstanceKey(
|
||
OUT PHANDLE Handle,
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
IN ULONG Instance,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN Create
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the csconfig flags for the specified device
|
||
which is specified by the instance number under ServiceKeyName\Enum.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
||
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
||
that caused the driver to load. This is the RegistryPath parameter
|
||
to the DriverEntry routine.
|
||
|
||
Instance - Supplies the instance value under ServiceKeyName\Enum key
|
||
|
||
DesiredAccess - Specifies the desired access that the caller needs to
|
||
the key.
|
||
|
||
Create - Determines if the key is to be created if it does not exist.
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING tempUnicodeString;
|
||
HANDLE profileHandle, profileEnumHandle, tmpHandle;
|
||
|
||
//
|
||
// See if we can open current hardware profile
|
||
//
|
||
status = IopOpenRegistryKey(&profileHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetHardwareProfilesCurrent,
|
||
KEY_READ,
|
||
Create
|
||
);
|
||
if(NT_SUCCESS(status)) {
|
||
//
|
||
// Now, we must open the System\CCS\Enum key under this.
|
||
//
|
||
//
|
||
// Open system\CurrentControlSet under current hardware profile key
|
||
//
|
||
|
||
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_PATH_CURRENTCONTROLSET);
|
||
status = IopOpenRegistryKey(&tmpHandle,
|
||
profileHandle,
|
||
&tempUnicodeString,
|
||
DesiredAccess,
|
||
FALSE
|
||
);
|
||
ZwClose(profileHandle);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
PiWstrToUnicodeString(&tempUnicodeString, REGSTR_KEY_ENUM);
|
||
status = IopOpenRegistryKey(&profileEnumHandle,
|
||
tmpHandle,
|
||
&tempUnicodeString,
|
||
KEY_READ,
|
||
Create
|
||
);
|
||
ZwClose(tmpHandle);
|
||
if(NT_SUCCESS(status)) {
|
||
|
||
status = IopServiceInstanceToDeviceInstance(NULL,
|
||
ServiceKeyName,
|
||
Instance,
|
||
&tempUnicodeString,
|
||
NULL,
|
||
0
|
||
);
|
||
if(NT_SUCCESS(status)) {
|
||
status = IopOpenRegistryKey(Handle,
|
||
profileEnumHandle,
|
||
&tempUnicodeString,
|
||
DesiredAccess,
|
||
Create
|
||
);
|
||
RtlFreeUnicodeString(&tempUnicodeString);
|
||
}
|
||
ZwClose(profileEnumHandle);
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopApplyFunctionToSubKeys(
|
||
IN HANDLE BaseHandle OPTIONAL,
|
||
IN PUNICODE_STRING KeyName OPTIONAL,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN IgnoreNonCriticalErrors,
|
||
IN PIOP_SUBKEY_CALLBACK_ROUTINE SubKeyCallbackRoutine,
|
||
IN OUT PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine enumerates all subkeys under the specified key, and calls
|
||
the specified callback routine for each subkey.
|
||
|
||
Arguments:
|
||
|
||
BaseHandle - Optional handle to the base registry path. If KeyName is also
|
||
specified, then KeyName represents a subkey under this path. If KeyName
|
||
is not specified, the subkeys are enumerated under this handle. If this
|
||
parameter is not specified, then the full path to the base key must be
|
||
given in KeyName.
|
||
|
||
KeyName - Optional name of the key whose subkeys are to be enumerated.
|
||
|
||
DesiredAccess - Specifies the desired access that the callback routine
|
||
needs to the subkeys. If no desired access is specified (i.e.,
|
||
DesiredAccess is zero), then no handle will be opened for the
|
||
subkeys, and the callback will be passed a NULL for its SubKeyHandle
|
||
parameter.
|
||
|
||
IgnoreNonCriticalErrors - Specifies whether this function should
|
||
immediately terminate on all errors, or only on critical ones.
|
||
An example of a non-critical error is when an enumerated subkey
|
||
cannot be opened for the desired access.
|
||
|
||
SubKeyCallbackRoutine - Supplies a pointer to a function that will
|
||
be called for each subkey found under the
|
||
specified key. The prototype of the function
|
||
is as follows:
|
||
|
||
typedef BOOLEAN (*PIOP_SUBKEY_CALLBACK_ROUTINE) (
|
||
IN HANDLE SubKeyHandle,
|
||
IN PUNICODE_STRING SubKeyName,
|
||
IN OUT PVOID Context
|
||
);
|
||
|
||
where SubKeyHandle is the handle to an enumerated subkey under the
|
||
specified key, SubKeyName is its name, and Context is a pointer to
|
||
user-defined data.
|
||
|
||
This function should return TRUE to continue enumeration, or
|
||
FALSE to terminate it.
|
||
|
||
Context - Supplies a pointer to user-defined data that will be passed
|
||
in to the callback routine at each subkey invocation.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating whether the subkeys were successfully
|
||
enumerated. Note that this does not provide information on the
|
||
success or failure of the callback routine--if desired, this
|
||
information should be stored in the Context structure.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
BOOLEAN CloseHandle = FALSE, ContinueEnumeration;
|
||
HANDLE Handle, SubKeyHandle;
|
||
ULONG i, RequiredBufferLength;
|
||
PKEY_BASIC_INFORMATION KeyInformation = NULL;
|
||
// Use an initial key name buffer size large enough for a 20-character key
|
||
// (+ terminating NULL)
|
||
ULONG KeyInformationLength = sizeof(KEY_BASIC_INFORMATION) + (20 * sizeof(WCHAR));
|
||
UNICODE_STRING SubKeyName;
|
||
|
||
if(ARGUMENT_PRESENT(KeyName)) {
|
||
|
||
Status = IopOpenRegistryKey(&Handle,
|
||
BaseHandle,
|
||
KeyName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
} else {
|
||
CloseHandle = TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
Handle = BaseHandle;
|
||
}
|
||
|
||
//
|
||
// Enumerate the subkeys until we run out of them.
|
||
//
|
||
i = 0;
|
||
SubKeyHandle = NULL;
|
||
|
||
while(TRUE) {
|
||
|
||
if(!KeyInformation) {
|
||
|
||
KeyInformation = (PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool,
|
||
KeyInformationLength
|
||
);
|
||
if(!KeyInformation) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
}
|
||
|
||
Status = ZwEnumerateKey(Handle,
|
||
i,
|
||
KeyBasicInformation,
|
||
KeyInformation,
|
||
KeyInformationLength,
|
||
&RequiredBufferLength
|
||
);
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
if(Status == STATUS_BUFFER_OVERFLOW) {
|
||
//
|
||
// Try again with larger buffer.
|
||
//
|
||
ExFreePool(KeyInformation);
|
||
KeyInformation = NULL;
|
||
KeyInformationLength = RequiredBufferLength;
|
||
continue;
|
||
|
||
} else if(Status == STATUS_NO_MORE_ENTRIES) {
|
||
//
|
||
// break out of loop
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
} else {
|
||
//
|
||
// This is a non-critical error.
|
||
//
|
||
if(IgnoreNonCriticalErrors) {
|
||
goto ContinueWithNextSubKey;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Initialize a unicode string with this key name. Note that this string
|
||
// WILL NOT be NULL-terminated.
|
||
//
|
||
SubKeyName.Length = SubKeyName.MaximumLength = (USHORT)KeyInformation->NameLength;
|
||
SubKeyName.Buffer = KeyInformation->Name;
|
||
|
||
//
|
||
// If DesiredAccess is non-zero, open a handle to this subkey.
|
||
//
|
||
if(DesiredAccess) {
|
||
Status = IopOpenRegistryKey(&SubKeyHandle,
|
||
Handle,
|
||
&SubKeyName,
|
||
DesiredAccess,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
//
|
||
// This is a non-critical error.
|
||
//
|
||
if(IgnoreNonCriticalErrors) {
|
||
goto ContinueWithNextSubKey;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Invoke the supplied callback function for this subkey.
|
||
//
|
||
ContinueEnumeration = SubKeyCallbackRoutine(SubKeyHandle, &SubKeyName, Context);
|
||
|
||
if(DesiredAccess) {
|
||
ZwClose(SubKeyHandle);
|
||
}
|
||
|
||
if(!ContinueEnumeration) {
|
||
//
|
||
// Enumeration has been aborted.
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
}
|
||
|
||
ContinueWithNextSubKey:
|
||
|
||
i++;
|
||
}
|
||
|
||
if(KeyInformation) {
|
||
ExFreePool(KeyInformation);
|
||
}
|
||
|
||
if(CloseHandle) {
|
||
ZwClose(Handle);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopRegMultiSzToUnicodeStrings(
|
||
IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
|
||
OUT PUNICODE_STRING *UnicodeStringList,
|
||
OUT PULONG UnicodeStringCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a KEY_VALUE_FULL_INFORMATION structure containing
|
||
a REG_MULTI_SZ value, and allocates an array of UNICODE_STRINGs,
|
||
initializing each one to a copy of one of the strings in the value entry.
|
||
All the resulting UNICODE_STRINGs will be NULL terminated
|
||
(MaximumLength = Length + sizeof(UNICODE_NULL)).
|
||
|
||
It is the responsibility of the caller to free the buffers for each
|
||
unicode string, as well as the buffer containing the UNICODE_STRING
|
||
array. This may be done by calling IopFreeUnicodeStringList.
|
||
|
||
Arguments:
|
||
|
||
KeyValueInformation - Supplies the buffer containing the REG_MULTI_SZ
|
||
value entry data.
|
||
|
||
UnicodeStringList - Receives a pointer to an array of UNICODE_STRINGs, each
|
||
initialized with a copy of one of the strings in the REG_MULTI_SZ.
|
||
|
||
UnicodeStringCount - Receives the number of strings in the
|
||
UnicodeStringList.
|
||
|
||
Returns:
|
||
|
||
NT status code indicating whether the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PWCHAR p, BufferEnd, StringStart;
|
||
ULONG StringCount, i, StringLength;
|
||
|
||
//
|
||
// First, make sure this is really a REG_MULTI_SZ value.
|
||
//
|
||
if(KeyValueInformation->Type != REG_MULTI_SZ) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Make a preliminary pass through the buffer to count the number of strings
|
||
// There will always be at least one string returned (possibly empty).
|
||
//
|
||
StringCount = 0;
|
||
p = (PWCHAR)KEY_VALUE_DATA(KeyValueInformation);
|
||
BufferEnd = (PWCHAR)((PUCHAR)p + KeyValueInformation->DataLength);
|
||
while(p != BufferEnd) {
|
||
if(!*p) {
|
||
StringCount++;
|
||
if(((p + 1) == BufferEnd) || !*(p + 1)) {
|
||
break;
|
||
}
|
||
}
|
||
p++;
|
||
}
|
||
if(p == BufferEnd) {
|
||
StringCount++;
|
||
}
|
||
|
||
*UnicodeStringList = ExAllocatePool(PagedPool, sizeof(UNICODE_STRING) * StringCount);
|
||
if(!(*UnicodeStringList)) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Now, make a second pass through the buffer making copies of each string.
|
||
//
|
||
i = 0;
|
||
StringStart = p = (PWCHAR)KEY_VALUE_DATA(KeyValueInformation);
|
||
while(p != BufferEnd) {
|
||
if(!*p) {
|
||
StringLength = ((PUCHAR)p - (PUCHAR)StringStart) + sizeof(UNICODE_NULL);
|
||
(*UnicodeStringList)[i].Buffer = ExAllocatePool(PagedPool, StringLength);
|
||
|
||
if(!((*UnicodeStringList)[i].Buffer)) {
|
||
IopFreeUnicodeStringList(*UnicodeStringList, i);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlMoveMemory((*UnicodeStringList)[i].Buffer, StringStart, StringLength);
|
||
|
||
(*UnicodeStringList)[i].Length =
|
||
((*UnicodeStringList)[i].MaximumLength = (USHORT)StringLength)
|
||
- sizeof(UNICODE_NULL);
|
||
|
||
i++;
|
||
|
||
if(((p + 1) == BufferEnd) || !*(p + 1)) {
|
||
break;
|
||
} else {
|
||
StringStart = p + 1;
|
||
}
|
||
}
|
||
p++;
|
||
}
|
||
if(p == BufferEnd) {
|
||
StringLength = (PUCHAR)p - (PUCHAR)StringStart;
|
||
(*UnicodeStringList)[i].Buffer = ExAllocatePool(PagedPool,
|
||
StringLength + sizeof(UNICODE_NULL)
|
||
);
|
||
if(!((*UnicodeStringList)[i].Buffer)) {
|
||
IopFreeUnicodeStringList(*UnicodeStringList, i);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
if(StringLength) {
|
||
RtlMoveMemory((*UnicodeStringList)[i].Buffer, StringStart, StringLength);
|
||
}
|
||
(*UnicodeStringList)[i].Buffer[CB_TO_CWC(StringLength)] = UNICODE_NULL;
|
||
|
||
(*UnicodeStringList)[i].MaximumLength =
|
||
((*UnicodeStringList)[i].Length = (USHORT)StringLength)
|
||
+ sizeof(UNICODE_NULL);
|
||
}
|
||
|
||
*UnicodeStringCount = StringCount;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopApplyFunctionToServiceInstances(
|
||
IN HANDLE ServiceKeyHandle OPTIONAL,
|
||
IN PUNICODE_STRING ServiceKeyName OPTIONAL,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN IgnoreNonCriticalErrors,
|
||
IN PIOP_SUBKEY_CALLBACK_ROUTINE DevInstCallbackRoutine,
|
||
IN OUT PVOID Context,
|
||
OUT PULONG ServiceInstanceOrdinal OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine enumerates all device instances referenced by the instance
|
||
ordinal entries under a service's volatile Enum key, and calls
|
||
the specified callback routine for each instance's corresponding subkey
|
||
under HKLM\System\Enum.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyHandle - Optional handle to the service entry. If this parameter
|
||
is not specified, then the service key name must be given in
|
||
ServiceKeyName (if both parameters are specified, then ServiceKeyHandle
|
||
is used, and ServiceKeyName is ignored).
|
||
|
||
ServiceKeyName - Optional name of the service entry key (under
|
||
HKLM\CurrentControlSet\Services). If this parameter is not specified,
|
||
then ServiceKeyHandle must contain a handle to the desired service key.
|
||
|
||
DesiredAccess - Specifies the desired access that the callback routine
|
||
needs to the enumerated device instance keys. If no desired access is
|
||
specified (i.e., DesiredAccess is zero), then no handle will be opened
|
||
for the device instance keys, and the callback will be passed a NULL for
|
||
its DeviceInstanceHandle parameter.
|
||
|
||
IgnoreNonCriticalErrors - Specifies whether this function should
|
||
immediately terminate on all errors, or only on critical ones.
|
||
An example of a non-critical error is when an enumerated device instance
|
||
key cannot be opened for the desired access.
|
||
|
||
DevInstCallbackRoutine - Supplies a pointer to a function that will
|
||
be called for each device instance key referenced by a service instance
|
||
entry under the service's volatile Enum subkey. The prototype of the
|
||
function is as follows:
|
||
|
||
typedef BOOLEAN (*PIOP_SUBKEY_CALLBACK_ROUTINE) (
|
||
IN HANDLE DeviceInstanceHandle,
|
||
IN PUNICODE_STRING DeviceInstancePath,
|
||
IN OUT PVOID Context
|
||
);
|
||
|
||
where DeviceInstanceHandle is the handle to an enumerated device instance
|
||
key, DeviceInstancePath is the registry path (relative to
|
||
HKLM\System\Enum) to this device instance, and Context is a pointer to
|
||
user-defined data.
|
||
|
||
This function should return TRUE to continue enumeration, or
|
||
FALSE to terminate it.
|
||
|
||
Context - Supplies a pointer to user-defined data that will be passed
|
||
in to the callback routine at each device instance key invocation.
|
||
|
||
ServiceInstanceOrdinal - Optionally, receives the service instance ordinal
|
||
that terminated the enumeration, or the total number of instances enumerated
|
||
if the enumeration completed without being aborted.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating whether the device instance keys were successfully
|
||
enumerated. Note that this does not provide information on the success or
|
||
failure of the callback routine--if desired, this information should be
|
||
stored in the Context structure.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE ServiceEnumHandle, SystemEnumHandle, DeviceInstanceHandle;
|
||
UNICODE_STRING TempUnicodeString;
|
||
ULONG ServiceInstanceCount, i;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
WCHAR ValueNameString[20];
|
||
BOOLEAN ContinueEnumeration;
|
||
|
||
//
|
||
// First, open up the volatile Enum subkey under the specified service entry.
|
||
//
|
||
if(ARGUMENT_PRESENT(ServiceKeyHandle)) {
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_KEY_ENUM);
|
||
Status = IopOpenRegistryKey(&ServiceEnumHandle,
|
||
ServiceKeyHandle,
|
||
&TempUnicodeString,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
} else {
|
||
Status = IopOpenServiceEnumKeys(ServiceKeyName,
|
||
KEY_READ,
|
||
NULL,
|
||
&ServiceEnumHandle,
|
||
FALSE
|
||
);
|
||
}
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Find out how many instances are referenced in the service's Enum key.
|
||
//
|
||
ServiceInstanceCount = 0; // assume none.
|
||
|
||
Status = IopGetRegistryValue(ServiceEnumHandle,
|
||
REGSTR_VALUE_COUNT,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
|
||
if((KeyValueInformation->Type == REG_DWORD) &&
|
||
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
ServiceInstanceCount = *(PULONG)KEY_VALUE_DATA(KeyValueInformation);
|
||
|
||
}
|
||
ExFreePool(KeyValueInformation);
|
||
|
||
} else if(Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
goto PrepareForReturn;
|
||
} else {
|
||
//
|
||
// If 'Count' value entry not found, consider this to mean there are simply
|
||
// no device instance controlled by this service.
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Now, enumerate each service instance, and call the specified callback function
|
||
// for the corresponding device instance.
|
||
//
|
||
// Make sure 'i' is initialized to zero even if there's nothing to enumerate.
|
||
//
|
||
i = 0;
|
||
|
||
if(ServiceInstanceCount) {
|
||
|
||
if(DesiredAccess) {
|
||
Status = IopOpenRegistryKey(&SystemEnumHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn;
|
||
}
|
||
} else {
|
||
//
|
||
// Set DeviceInstanceHandle to NULL, since we won't be opening up the
|
||
// device instance keys.
|
||
//
|
||
DeviceInstanceHandle = NULL;
|
||
}
|
||
|
||
for(; i < ServiceInstanceCount; i++) { // i was initialized to zero above.
|
||
|
||
swprintf(ValueNameString, REGSTR_VALUE_STANDARD_ULONG_FORMAT, i);
|
||
Status = IopGetRegistryValue(ServiceEnumHandle,
|
||
ValueNameString,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
ContinueEnumeration = TRUE;
|
||
|
||
if(KeyValueInformation->Type == REG_SZ) {
|
||
IopRegistryDataToUnicodeString(&TempUnicodeString,
|
||
(PWSTR)KEY_VALUE_DATA(KeyValueInformation),
|
||
KeyValueInformation->DataLength
|
||
);
|
||
} else {
|
||
TempUnicodeString.Length = 0;
|
||
}
|
||
|
||
if(TempUnicodeString.Length) {
|
||
//
|
||
// We have retrieved a (non-empty) string for this service instance.
|
||
// If the user specified a non-zero value for the DesiredAccess
|
||
// parameter, we will attempt to open up the corresponding device
|
||
// instance key under HKLM\System\Enum.
|
||
//
|
||
if(DesiredAccess) {
|
||
Status = IopOpenRegistryKey(&DeviceInstanceHandle,
|
||
SystemEnumHandle,
|
||
&TempUnicodeString,
|
||
DesiredAccess,
|
||
FALSE
|
||
);
|
||
}
|
||
|
||
if(NT_SUCCESS(Status)) {
|
||
//
|
||
// Invoke the specified callback routine for this device instance.
|
||
//
|
||
ContinueEnumeration = DevInstCallbackRoutine(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
Context
|
||
);
|
||
if(DesiredAccess) {
|
||
ZwClose(DeviceInstanceHandle);
|
||
}
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_PLUGPLAY_DEVICE_PATH;
|
||
}
|
||
|
||
ExFreePool(KeyValueInformation);
|
||
|
||
if(!ContinueEnumeration) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
if(IgnoreNonCriticalErrors) {
|
||
continue;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(DesiredAccess) {
|
||
ZwClose(SystemEnumHandle);
|
||
}
|
||
}
|
||
|
||
if(ARGUMENT_PRESENT(ServiceInstanceOrdinal)) {
|
||
*ServiceInstanceOrdinal = i;
|
||
}
|
||
|
||
PrepareForReturn:
|
||
|
||
ZwClose(ServiceEnumHandle);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopMarkDuplicateDevice (
|
||
IN PUNICODE_STRING TargetKeyName,
|
||
IN ULONG TargetInstance,
|
||
IN PUNICODE_STRING SourceKeyName,
|
||
IN ULONG SourceInstance
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine marks the device instance specified by TargetKeyName and TargetInstance
|
||
as DuplicateOf the device specified by SourceKeyName and SourceInstance.
|
||
|
||
Arguments:
|
||
|
||
TargetKeyName - supplies a pointer to the name of service key which will be marked
|
||
as duplicate.
|
||
|
||
TargetInstance - the instance number of the target device.
|
||
|
||
SourceKeyName - supplies a pointer to the name of service key.
|
||
|
||
SourceInstance - the instance number of the source device.
|
||
|
||
|
||
Returns:
|
||
|
||
NTSTATUS code.
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE handle;
|
||
NTSTATUS status;
|
||
UNICODE_STRING sourceDeviceString, unicodeValueName;
|
||
|
||
//
|
||
// Open the handle of the target device instance.
|
||
//
|
||
|
||
status = IopServiceInstanceToDeviceInstance(
|
||
NULL,
|
||
TargetKeyName,
|
||
TargetInstance,
|
||
NULL,
|
||
&handle,
|
||
0
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Get the name of the source device instance
|
||
//
|
||
|
||
status = IopServiceInstanceToDeviceInstance(
|
||
NULL,
|
||
SourceKeyName,
|
||
SourceInstance,
|
||
&sourceDeviceString,
|
||
NULL,
|
||
0
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Write the name of the source device to the DuplicateOf value entry of
|
||
// target device key.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_DUPLICATEOF);
|
||
status = ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
&sourceDeviceString,
|
||
sourceDeviceString.Length + sizeof(WCHAR)
|
||
);
|
||
return status;
|
||
}
|
||
|
||
BOOLEAN
|
||
IopIsDuplicatedDevices(
|
||
IN PCM_RESOURCE_LIST Configuration1,
|
||
IN PCM_RESOURCE_LIST Configuration2,
|
||
IN PHAL_BUS_INFORMATION BusInfo1 OPTIONAL,
|
||
IN PHAL_BUS_INFORMATION BusInfo2 OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine compares two set of configurations and bus information to
|
||
determine if the resources indicate the same device. If BusInfo1 and
|
||
BusInfo2 both are absent, it means caller wants to compare the raw
|
||
resources.
|
||
|
||
Arguments:
|
||
|
||
Configuration1 - Supplies a pointer to the first set of resource.
|
||
|
||
Configuration2 - Supplies a pointer to the second set of resource.
|
||
|
||
BusInfo1 - Supplies a pointer to the first set of bus information.
|
||
|
||
BusInfo2 - Supplies a pointer to the second set of bus information.
|
||
|
||
Return Value:
|
||
|
||
returns TRUE if the two set of resources indicate the same device;
|
||
otherwise a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
PHYSICAL_ADDRESS port1, port1Translated, port2, port2Translated;
|
||
ULONG port1IoSpace = 1, port2IoSpace = 1;
|
||
PCM_PARTIAL_RESOURCE_LIST resourceList;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptors;
|
||
ULONG i;
|
||
BOOLEAN flag;
|
||
|
||
//
|
||
// The BusInfo for both resources must be both present or not present.
|
||
//
|
||
|
||
if ((ARGUMENT_PRESENT(BusInfo1) && !ARGUMENT_PRESENT(BusInfo2)) ||
|
||
(!ARGUMENT_PRESENT(BusInfo1) && ARGUMENT_PRESENT(BusInfo2))) {
|
||
|
||
//
|
||
// Unable to determine.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Next check resources used by the two devices.
|
||
// Currently, we *only* check the Io ports.
|
||
//
|
||
|
||
if (Configuration1->Count == 0 || Configuration2->Count == 0) {
|
||
|
||
//
|
||
// If any one of the configuration data is empty, we assume
|
||
// the devices are not duplicates.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Get the I/O Resource in the first configuration and translate it.
|
||
//
|
||
|
||
resourceList = &Configuration1->List[0].PartialResourceList;
|
||
port1.QuadPart = 0;
|
||
for (i = 0, descriptors = resourceList->PartialDescriptors;
|
||
i < resourceList->Count;
|
||
i++, descriptors++) {
|
||
|
||
if (descriptors->Type == CmResourceTypePort) {
|
||
port1 = descriptors->u.Port.Start;
|
||
}
|
||
}
|
||
if (port1.QuadPart != 0 && ARGUMENT_PRESENT(BusInfo1)) {
|
||
flag = HalTranslateBusAddress (
|
||
BusInfo1->BusType,
|
||
BusInfo1->BusNumber,
|
||
port1,
|
||
&port1IoSpace,
|
||
&port1Translated
|
||
);
|
||
if (flag == FALSE) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Get the I/O resource in the second configuration and tanslate it.
|
||
//
|
||
|
||
resourceList = &Configuration2->List[0].PartialResourceList;
|
||
port2.QuadPart = 0;
|
||
for (i = 0, descriptors = resourceList->PartialDescriptors;
|
||
i < resourceList->Count;
|
||
i++, descriptors++) {
|
||
|
||
if (descriptors->Type == CmResourceTypePort) {
|
||
port2 = descriptors->u.Port.Start;
|
||
}
|
||
}
|
||
if (port2.QuadPart != 0 && BusInfo2) {
|
||
flag = HalTranslateBusAddress (
|
||
BusInfo2->BusType,
|
||
BusInfo2->BusNumber,
|
||
port2,
|
||
&port2IoSpace,
|
||
&port2Translated
|
||
);
|
||
if (flag == FALSE) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if ((port1Translated.QuadPart == port2Translated.QuadPart) &&
|
||
(port1IoSpace == port2IoSpace)) {
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
IopFreeUnicodeStringList(
|
||
IN PUNICODE_STRING UnicodeStringList,
|
||
IN ULONG StringCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the buffer for each UNICODE_STRING in the specified list
|
||
(there are StringCount of them), and then frees the memory used for the
|
||
string list itself.
|
||
|
||
Arguments:
|
||
|
||
UnicodeStringList - Supplies a pointer to an array of UNICODE_STRINGs.
|
||
|
||
StringCount - Supplies the number of strings in the UnicodeStringList array.
|
||
|
||
Returns:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
|
||
if(UnicodeStringList) {
|
||
for(i = 0; i < StringCount; i++) {
|
||
if(UnicodeStringList[i].Buffer) {
|
||
ExFreePool(UnicodeStringList[i].Buffer);
|
||
}
|
||
}
|
||
ExFreePool(UnicodeStringList);
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
IopDriverLoadingFailed(
|
||
IN HANDLE ServiceHandle OPTIONAL,
|
||
IN PUNICODE_STRING ServiceName OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked when driver failed to start. All the device
|
||
instances controlled by this driver/service are marked as failing to
|
||
start.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyHandle - Optionally, supplies a handle to the driver service node in the
|
||
registry that controls this device instance. If this argument is not specified,
|
||
then ServiceKeyName is used to specify the service entry.
|
||
|
||
ServiceKeyName - Optionally supplies the name of the service entry that controls
|
||
the device instance. This must be specified if ServiceKeyHandle isn't given.
|
||
|
||
Returns:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
BOOLEAN IsPlugPlayDriver = FALSE, closeHandle = FALSE;
|
||
HANDLE handle, serviceEnumHandle, controlHandle;
|
||
HANDLE sysEnumHandle = NULL, hTreeHandle = NULL;
|
||
ULONG deviceFlags, count, newCount, i, j;
|
||
UNICODE_STRING unicodeValueName, deviceInstanceName;
|
||
WCHAR unicodeBuffer[20];
|
||
|
||
//
|
||
// Open registry ServiceKeyName\Enum branch
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(ServiceHandle)) {
|
||
status = IopOpenServiceEnumKeys(ServiceName,
|
||
KEY_READ,
|
||
&ServiceHandle,
|
||
&serviceEnumHandle,
|
||
FALSE
|
||
);
|
||
closeHandle = TRUE;
|
||
} else {
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_ENUM);
|
||
status = IopOpenRegistryKey(&serviceEnumHandle,
|
||
ServiceHandle,
|
||
&unicodeValueName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
}
|
||
if (!NT_SUCCESS( status )) {
|
||
|
||
//
|
||
// No Service Enum key? no device instance. Return FALSE.
|
||
//
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Find out how many device instances listed in the ServiceName's
|
||
// Enum key.
|
||
//
|
||
|
||
status = IopGetRegistryValue ( serviceEnumHandle,
|
||
REGSTR_VALUE_COUNT,
|
||
&keyValueInformation
|
||
);
|
||
count = 0;
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
count = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
if (count == 0) {
|
||
ZwClose(serviceEnumHandle);
|
||
if (closeHandle) {
|
||
ZwClose(ServiceHandle);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Open HTREE\ROOT\0 key so later we can remove device instance key
|
||
// from its AttachedComponents value name.
|
||
//
|
||
|
||
status = IopOpenRegistryKey(&sysEnumHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_HTREE_ROOT_0);
|
||
status = IopOpenRegistryKey(&hTreeHandle,
|
||
sysEnumHandle,
|
||
&unicodeValueName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
}
|
||
|
||
//
|
||
// Walk through each registered device instance to mark its Problem and
|
||
// StatusFlags as fail to start and reset its ActiveService
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
||
|
||
newCount = count;
|
||
for (i = 0; i < count; i++) {
|
||
status = IopServiceInstanceToDeviceInstance (
|
||
ServiceHandle,
|
||
ServiceName,
|
||
i,
|
||
&deviceInstanceName,
|
||
&handle,
|
||
KEY_ALL_ACCESS
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_KEY_CONTROL);
|
||
controlHandle = NULL;
|
||
status = IopOpenRegistryKey(&controlHandle,
|
||
handle,
|
||
&unicodeValueName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
status = IopGetRegistryValue(controlHandle,
|
||
REGSTR_VALUE_NEWLY_CREATED,
|
||
&keyValueInformation);
|
||
if (NT_SUCCESS(status)) {
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
if ((status != STATUS_OBJECT_NAME_NOT_FOUND) &&
|
||
(status != STATUS_OBJECT_PATH_NOT_FOUND)) {
|
||
|
||
//
|
||
// Remove the instance value name from service enum key
|
||
//
|
||
|
||
PiUlongToUnicodeString(&unicodeValueName, unicodeBuffer, 20, i);
|
||
status = ZwDeleteValueKey (serviceEnumHandle, &unicodeValueName);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// If we can successfaully remove the instance value entry
|
||
// from service enum key, we then remove the device instance key
|
||
// Otherwise, we go thru normal path to mark driver loading failed
|
||
// in the device instance key.
|
||
//
|
||
|
||
newCount--;
|
||
|
||
ZwDeleteKey(controlHandle);
|
||
ZwDeleteKey(handle);
|
||
|
||
//
|
||
// Remove the device instance name from HTREE\ROOT\0 AttachedComponents
|
||
// value name.
|
||
//
|
||
|
||
if (hTreeHandle) {
|
||
IopRemoveStringFromValueKey (
|
||
hTreeHandle,
|
||
REGSTR_VALUE_ATTACHEDCOMPONENTS,
|
||
&deviceInstanceName
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// We also want to delete the ROOT\LEGACY_<driver> key
|
||
//
|
||
|
||
if (sysEnumHandle) {
|
||
deviceInstanceName.Length -= 5 * sizeof(WCHAR);
|
||
deviceInstanceName.Buffer[deviceInstanceName.Length / sizeof(WCHAR)] =
|
||
UNICODE_NULL;
|
||
status = IopOpenRegistryKey(&handle,
|
||
sysEnumHandle,
|
||
&deviceInstanceName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
ZwDeleteKey(handle);
|
||
}
|
||
}
|
||
|
||
ExFreePool(deviceInstanceName.Buffer);
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update the following value names:
|
||
// Problem = CM_PROB_FAILED_START
|
||
// StatusFlags = ~DN_STARTED + DN_HAS_PROBLEM
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_PROBLEM);
|
||
deviceFlags = CM_PROB_FAILED_START;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&deviceFlags,
|
||
sizeof(deviceFlags)
|
||
);
|
||
|
||
status = IopGetRegistryValue(handle,
|
||
REGSTR_VALUE_STATUSFLAGS,
|
||
&keyValueInformation);
|
||
deviceFlags = 0;
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VALUE_STATUSFLAGS);
|
||
deviceFlags &= ~DN_STARTED;
|
||
deviceFlags |= DN_HAS_PROBLEM;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&deviceFlags,
|
||
sizeof(deviceFlags)
|
||
);
|
||
//
|
||
// Reset Control\ActiveService value name.
|
||
//
|
||
|
||
if (controlHandle) {
|
||
PiWstrToUnicodeString(&unicodeValueName, REGSTR_VAL_ACTIVESERVICE);
|
||
ZwDeleteValueKey(controlHandle, &unicodeValueName);
|
||
ZwClose(controlHandle);
|
||
}
|
||
|
||
ZwClose(handle);
|
||
ExFreePool(deviceInstanceName.Buffer);
|
||
}
|
||
}
|
||
|
||
//
|
||
// If some instance value entry is deleted, we need to update the count of instance
|
||
// value entries and rearrange the instance value entries under service enum key.
|
||
//
|
||
|
||
if (newCount != count) {
|
||
if (newCount != 0) {
|
||
j = 0;
|
||
i = 0;
|
||
while (i < count) {
|
||
PiUlongToUnicodeString(&unicodeValueName, unicodeBuffer, 20, i);
|
||
status = IopGetRegistryValue(serviceEnumHandle,
|
||
unicodeValueName.Buffer,
|
||
&keyValueInformation
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
if (i != j) {
|
||
|
||
//
|
||
// Need to change the instance i to instance j
|
||
//
|
||
|
||
ZwDeleteValueKey(serviceEnumHandle, &unicodeValueName);
|
||
|
||
PiUlongToUnicodeString(&unicodeValueName, unicodeBuffer, 20, j);
|
||
ZwSetValueKey (serviceEnumHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
(PVOID)KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength
|
||
);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
j++;
|
||
}
|
||
i++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Don't forget to update the "Count=" and "NextInstance=" value entries
|
||
//
|
||
|
||
PiWstrToUnicodeString( &unicodeValueName, REGSTR_VALUE_COUNT);
|
||
|
||
ZwSetValueKey(serviceEnumHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&newCount,
|
||
sizeof (newCount)
|
||
);
|
||
PiWstrToUnicodeString( &unicodeValueName, REGSTR_VALUE_NEXT_INSTANCE);
|
||
|
||
ZwSetValueKey(serviceEnumHandle,
|
||
&unicodeValueName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&newCount,
|
||
sizeof (newCount)
|
||
);
|
||
}
|
||
ZwClose(serviceEnumHandle);
|
||
if (closeHandle) {
|
||
ZwClose(ServiceHandle);
|
||
}
|
||
if (hTreeHandle) {
|
||
ZwClose(hTreeHandle);
|
||
}
|
||
if (sysEnumHandle) {
|
||
ZwClose(sysEnumHandle);
|
||
}
|
||
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
KeLeaveCriticalRegion();
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
BOOLEAN
|
||
IopIsDeviceInstanceEnabled(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
IN HANDLE ServiceHandle OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks if any of the devices instances is turned on for the specified
|
||
service. This routine is used for Pnp Driver only and is temporay function to support
|
||
SUR.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyName - Specifies the service key unicode name
|
||
|
||
ServiceHandle - Optionally supplies a handle to the service key to be checked.
|
||
|
||
Returns:
|
||
|
||
A BOOLEAN value.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
HANDLE serviceEnumHandle, handle, controlHandle;
|
||
ULONG i, count, deviceFlags, statusFlagsValue;
|
||
ULONG ConfigFlags;
|
||
UNICODE_STRING unicodeName;
|
||
BOOLEAN enabled, setProblem, closeHandle = FALSE;
|
||
|
||
//
|
||
// Open registry ServiceKeyName\Enum branch
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(ServiceHandle)) {
|
||
status = IopOpenServiceEnumKeys(ServiceKeyName,
|
||
KEY_READ,
|
||
&ServiceHandle,
|
||
&serviceEnumHandle,
|
||
FALSE
|
||
);
|
||
closeHandle = TRUE;
|
||
} else {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_ENUM);
|
||
status = IopOpenRegistryKey(&serviceEnumHandle,
|
||
ServiceHandle,
|
||
&unicodeName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
}
|
||
if (!NT_SUCCESS( status )) {
|
||
|
||
//
|
||
// No Service Enum key? no device instance. Return FALSE.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Find out how many device instances listed in the ServiceName's
|
||
// Enum key.
|
||
//
|
||
|
||
status = IopGetRegistryValue ( serviceEnumHandle,
|
||
REGSTR_VALUE_COUNT,
|
||
&keyValueInformation
|
||
);
|
||
ZwClose(serviceEnumHandle);
|
||
count = 0;
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
count = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
if (count == 0) {
|
||
if (closeHandle) {
|
||
ZwClose(ServiceHandle);
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Walk through each registered device instance to check it is enabled.
|
||
//
|
||
|
||
enabled = FALSE;
|
||
for (i = 0; i < count; i++) {
|
||
|
||
//
|
||
// Get device instance handle. If it fails, we will skip this device
|
||
// instance.
|
||
//
|
||
|
||
status = IopServiceInstanceToDeviceInstance (
|
||
ServiceHandle,
|
||
NULL,
|
||
i,
|
||
NULL,
|
||
&handle,
|
||
KEY_ALL_ACCESS
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Check if the device instance has been disabled.
|
||
// First check global flag: CONFIGFLAG and then CSCONFIGFLAG.
|
||
//
|
||
|
||
ConfigFlags = deviceFlags = 0;
|
||
status = IopGetRegistryValue(handle,
|
||
REGSTR_VALUE_CONFIG_FLAGS,
|
||
&keyValueInformation);
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
ConfigFlags = deviceFlags = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
if (!(deviceFlags & CONFIGFLAG_DISABLED)) {
|
||
deviceFlags = 0;
|
||
status = IopGetDeviceInstanceCsConfigFlags(
|
||
ServiceKeyName,
|
||
i,
|
||
&deviceFlags
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
if ((deviceFlags & CSCONFIGFLAG_DISABLED) ||
|
||
(deviceFlags & CSCONFIGFLAG_DO_NOT_CREATE)) {
|
||
deviceFlags = CONFIGFLAG_DISABLED;
|
||
} else {
|
||
deviceFlags = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, we need to set the STATUSFLAGS of the device instance to
|
||
// indicate if the driver is successfully started.
|
||
//
|
||
|
||
if (deviceFlags & CONFIGFLAG_DISABLED) {
|
||
|
||
//
|
||
// According to current h/w profile, this driver should
|
||
// not be started. So, we need to update the device instance
|
||
// flags to reflect the status.
|
||
|
||
statusFlagsValue = 0;
|
||
} else {
|
||
|
||
//
|
||
// Mark that the driver has at least a device instance to work with.
|
||
//
|
||
|
||
enabled = TRUE;
|
||
|
||
//
|
||
// We need to check and see if this device's ConfigFlags says it needs
|
||
// to be reinstalled. If so, then we want to set the device's problem
|
||
// to CM_PROB_REINSTALL, and set the StatusFlags' DN_HAS_PROBLEM to notify
|
||
// the Device Manager about it.
|
||
//
|
||
statusFlagsValue = (ConfigFlags & CONFIGFLAG_REINSTALL) ? DN_HAS_PROBLEM : 0;
|
||
|
||
//
|
||
// Initialize the StatusFlags to indicate that the device successfully
|
||
// started. Also the device instance's "ActiveService" in volatile Contol key
|
||
// will be set to the ServiceKeyName to mark the current service key. If
|
||
// this turns out to not be the case, then we reset the flag
|
||
// later on in IopDriverLoadingFailed.
|
||
//
|
||
|
||
statusFlagsValue |= DN_STARTED;
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
||
status = IopOpenRegistryKey(&controlHandle,
|
||
handle,
|
||
&unicodeName,
|
||
KEY_ALL_ACCESS,
|
||
TRUE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_ACTIVESERVICE);
|
||
ZwSetValueKey(
|
||
controlHandle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
ServiceKeyName->Buffer,
|
||
ServiceKeyName->Length + sizeof(UNICODE_NULL)
|
||
);
|
||
|
||
ZwClose(controlHandle);
|
||
}
|
||
|
||
|
||
}
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_STATUSFLAGS);
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&statusFlagsValue,
|
||
sizeof(statusFlagsValue)
|
||
);
|
||
|
||
//
|
||
// Clear Problem value, unless we need reinstall.
|
||
//
|
||
|
||
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_PROBLEM);
|
||
statusFlagsValue = (statusFlagsValue & DN_HAS_PROBLEM) ? CM_PROB_REINSTALL : 0;
|
||
ZwSetValueKey(
|
||
handle,
|
||
&unicodeName,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&statusFlagsValue,
|
||
sizeof(statusFlagsValue)
|
||
);
|
||
ZwClose(handle);
|
||
}
|
||
|
||
if (closeHandle) {
|
||
ZwClose(ServiceHandle);
|
||
}
|
||
return enabled;
|
||
}
|
||
#if _PNP_POWER_
|
||
|
||
ULONG
|
||
IopDetermineResourceListSize(
|
||
IN PCM_RESOURCE_LIST ResourceList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines size of the passed in ResourceList
|
||
structure.
|
||
|
||
Arguments:
|
||
|
||
Configuration1 - Supplies a pointer to the resource list.
|
||
|
||
Return Value:
|
||
|
||
size of the resource list structure.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG totalSize, listSize, descriptorSize, i, j;
|
||
PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDesc;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
|
||
|
||
if (!ResourceList) {
|
||
totalSize = 0;
|
||
} else {
|
||
totalSize = FIELD_OFFSET(CM_RESOURCE_LIST, List);
|
||
fullResourceDesc = &ResourceList->List[0];
|
||
for (i = 0; i < ResourceList->Count; i++) {
|
||
listSize = FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR,
|
||
PartialResourceList) +
|
||
FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST,
|
||
PartialDescriptors);
|
||
partialDescriptor = &fullResourceDesc->PartialResourceList.PartialDescriptors[0];
|
||
for (j = 0; j < fullResourceDesc->PartialResourceList.Count; j++) {
|
||
descriptorSize = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
|
||
if (partialDescriptor->Type == CmResourceTypeDeviceSpecific) {
|
||
descriptorSize += partialDescriptor->u.DeviceSpecificData.DataSize;
|
||
}
|
||
listSize += descriptorSize;
|
||
partialDescriptor = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)
|
||
((PUCHAR)partialDescriptor + descriptorSize);
|
||
}
|
||
totalSize += listSize;
|
||
fullResourceDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)
|
||
((PUCHAR)fullResourceDesc + listSize);
|
||
}
|
||
}
|
||
return totalSize;
|
||
}
|
||
|
||
PDRIVER_OBJECT
|
||
IopReferenceDriverObjectByName (
|
||
IN PUNICODE_STRING DriverName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine marks the device instance specified by TargetKeyName and TargetInstance
|
||
as DuplicateOf the device specified by SourceKeyName and SourceInstance.
|
||
|
||
Arguments:
|
||
|
||
TargetKeyName - supplies a pointer to the name of service key which will be marked
|
||
as duplicate.
|
||
|
||
TargetInstance - the instance number of the target device.
|
||
|
||
SourceKeyName - supplies a pointer to the name of service key.
|
||
|
||
SourceInstance - the instance number of the source device.
|
||
|
||
|
||
Returns:
|
||
|
||
NTSTATUS code.
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
HANDLE driverHandle;
|
||
NTSTATUS status;
|
||
PDRIVER_OBJECT driverObject;
|
||
|
||
//
|
||
// Make sure the driver name is valid.
|
||
//
|
||
|
||
if (DriverName->Length == 0) {
|
||
return NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(&objectAttributes,
|
||
DriverName,
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
status = ObOpenObjectByName(&objectAttributes,
|
||
IoDriverObjectType,
|
||
KernelMode,
|
||
NULL,
|
||
FILE_READ_ATTRIBUTES,
|
||
NULL,
|
||
&driverHandle
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Now reference the driver object.
|
||
//
|
||
|
||
status = ObReferenceObjectByHandle(driverHandle,
|
||
0,
|
||
IoDriverObjectType,
|
||
KernelMode,
|
||
&driverObject,
|
||
NULL
|
||
);
|
||
NtClose(driverHandle);
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
return driverObject;
|
||
} else {
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
PDEVICE_HANDLER_OBJECT
|
||
IopReferenceDeviceHandler (
|
||
IN INTERFACE_TYPE InterfaceType,
|
||
IN ULONG BusNumber,
|
||
IN ULONG SlotNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns a reference to a device handler object. The caller
|
||
must dereference the device handler object by doing an ObDereferenceObject.
|
||
|
||
Arguments:
|
||
|
||
InterfaceType - supplies the interface type of the bus which the device/slot resides.
|
||
|
||
BusNumber - bus number to specify the bus.
|
||
|
||
SlotNumber - the slot number the device located.
|
||
|
||
Returns:
|
||
|
||
A reference to the desired device handler object.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_HANDLER_OBJECT deviceHandler = NULL;
|
||
PBUS_HANDLER busHandler;
|
||
|
||
busHandler = HalReferenceHandlerForBus (InterfaceType,
|
||
BusNumber
|
||
);
|
||
if (!busHandler) {
|
||
return deviceHandler;
|
||
}
|
||
deviceHandler = busHandler->ReferenceDeviceHandler (
|
||
busHandler,
|
||
busHandler,
|
||
SlotNumber
|
||
);
|
||
HalDereferenceBusHandler (busHandler);
|
||
return deviceHandler;
|
||
|
||
}
|
||
#endif // _PNP_POWER_
|