NT4/private/ntos/io/pnpsubs.c
2020-09-30 17:12:29 +02:00

3808 lines
123 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 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_