Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1544 lines
44 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
misc.c
Abstract:
This file contains pnp isa bus extender support routines.
Author:
Shie-Lin Tzong (shielint) 27-Jusly-1995
Environment:
Kernel mode only.
Revision History:
--*/
#include "busp.h"
#include "pnpisa.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,PipDecompressEisaId)
#pragma alloc_text(INIT,PipOpenRegistryKey)
#pragma alloc_text(INIT,PipOpenRegistryKeyPersist)
#pragma alloc_text(INIT,PipGetRegistryValue)
#pragma alloc_text(INIT,PipOpenCurrentHwProfileDeviceInstanceKey)
#pragma alloc_text(INIT,PipGetDeviceInstanceCsConfigFlags)
#pragma alloc_text(INIT,PipRemoveStringFromValueKey)
#pragma alloc_text(INIT,PipAppendStringToValueKey)
#pragma alloc_text(INIT,PipServiceInstanceToDeviceInstance)
#pragma alloc_text(INIT,PipLogError)
#pragma alloc_text(INIT,PipApplyFunctionToSubKeys)
#if DBG
#pragma alloc_text(INIT,PipDebugPrint)
#pragma alloc_text(INIT,PipDumpIoResourceDescriptor)
#pragma alloc_text(INIT,PipDumpIoResourceList)
#pragma alloc_text(INIT,PipDumpCmResourceDescriptor)
#pragma alloc_text(INIT,PipDumpCmResourceList)
#endif
#endif
VOID
PipDecompressEisaId(
IN ULONG CompressedId,
IN PUCHAR EisaId
)
/*++
Routine Description:
This routine decompressed compressed Eisa Id and returns the Id to caller
specified character buffer.
Arguments:
CompressedId - supplies the compressed Eisa Id.
EisaId - supplies a 8-char buffer to receive the decompressed Eisa Id.
Return Value:
None.
--*/
{
USHORT c1, c2;
LONG i;
PAGED_CODE();
CompressedId &= 0xffffff7f; // remove the reserved bit (bit 7 of byte 0)
c1 = c2 = (USHORT)CompressedId;
c1 = (c1 & 0xff) << 8;
c2 = (c2 & 0xff00) >> 8;
c1 |= c2;
for (i = 2; i >= 0; i--) {
*(EisaId + i) = (UCHAR)(c1 & 0x1f) + 0x40;
c1 >>= 5;
}
EisaId += 3;
c1 = c2 = (USHORT)(CompressedId >> 16);
c1 = (c1 & 0xff) << 8;
c2 = (c2 & 0xff00) >> 8;
c1 |= c2;
sprintf (EisaId, "%04x", c1);
}
NTSTATUS
PipOpenRegistryKey(
OUT PHANDLE Handle,
IN HANDLE BaseHandle OPTIONAL,
IN PUNICODE_STRING KeyName,
IN ACCESS_MASK DesiredAccess,
IN BOOLEAN Create
)
/*++
Routine Description:
Opens or creates a VOLATILE registry key using the name passed in based
at the BaseHandle node.
Arguments:
Handle - Pointer to the handle which will contain the registry key that
was opened.
BaseHandle - Handle to the base path from which the key must be opened.
KeyName - Name of the Key that must be opened/created.
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:
The function value is the final status of the operation.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
ULONG disposition;
PAGED_CODE();
//
// Initialize the object for the key.
//
InitializeObjectAttributes( &objectAttributes,
KeyName,
OBJ_CASE_INSENSITIVE,
BaseHandle,
(PSECURITY_DESCRIPTOR) NULL );
//
// Create the key or open it, as appropriate based on the caller's
// wishes.
//
if (Create) {
return ZwCreateKey( Handle,
DesiredAccess,
&objectAttributes,
0,
(PUNICODE_STRING) NULL,
REG_OPTION_VOLATILE,
&disposition );
} else {
return ZwOpenKey( Handle,
DesiredAccess,
&objectAttributes );
}
}
NTSTATUS
PipOpenRegistryKeyPersist(
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
PipGetRegistryValue(
IN HANDLE KeyHandle,
IN PWSTR ValueName,
OUT PKEY_VALUE_FULL_INFORMATION *Information
)
/*++
Routine Description:
This routine is invoked to retrieve the data for a registry key's value.
This is done by querying the value of the key with a zero-length buffer
to determine the size of the value, and then allocating a buffer and
actually querying the value into the buffer.
It is the responsibility of the caller to free the buffer.
Arguments:
KeyHandle - Supplies the key handle whose value is to be queried
ValueName - Supplies the null-terminated Unicode name of the value.
Information - Returns a pointer to the allocated data buffer.
Return Value:
The function value is the final status of the query operation.
--*/
{
UNICODE_STRING unicodeString;
NTSTATUS status;
PKEY_VALUE_FULL_INFORMATION infoBuffer;
ULONG keyValueLength;
PAGED_CODE();
*Information = NULL;
RtlInitUnicodeString( &unicodeString, ValueName );
//
// Figure out how big the data value is so that a buffer of the
// appropriate size can be allocated.
//
status = ZwQueryValueKey( KeyHandle,
&unicodeString,
KeyValueFullInformation,
(PVOID) NULL,
0,
&keyValueLength );
if (status != STATUS_BUFFER_OVERFLOW &&
status != STATUS_BUFFER_TOO_SMALL) {
return status;
}
//
// Allocate a buffer large enough to contain the entire key data value.
//
infoBuffer = ExAllocatePool( NonPagedPool, keyValueLength );
if (!infoBuffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Query the data for the key value.
//
status = ZwQueryValueKey( KeyHandle,
&unicodeString,
KeyValueFullInformation,
infoBuffer,
keyValueLength,
&keyValueLength );
if (!NT_SUCCESS( status )) {
ExFreePool( infoBuffer );
return status;
}
//
// Everything worked, so simply return the address of the allocated
// buffer to the caller, who is now responsible for freeing it.
//
*Information = infoBuffer;
return STATUS_SUCCESS;
}
NTSTATUS
PipOpenCurrentHwProfileDeviceInstanceKey(
OUT PHANDLE Handle,
IN PUNICODE_STRING DeviceInstanceName,
IN ACCESS_MASK DesiredAccess
)
/*++
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 unicodeString;
HANDLE profileEnumHandle;
//
// See if we can open the device instance key of current hardware profile
//
RtlInitUnicodeString (
&unicodeString,
L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\HARDWARE PROFILES\\CURRENT\\SYSTEM\\CURRENTCONTROLSET\\ENUM"
);
status = PipOpenRegistryKey(&profileEnumHandle,
NULL,
&unicodeString,
KEY_READ,
FALSE
);
if (NT_SUCCESS(status)) {
status = PipOpenRegistryKey(Handle,
profileEnumHandle,
DeviceInstanceName,
DesiredAccess,
FALSE
);
ZwClose(profileEnumHandle);
}
return status;
}
NTSTATUS
PipGetDeviceInstanceCsConfigFlags(
IN PUNICODE_STRING DeviceInstance,
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 = PipOpenCurrentHwProfileDeviceInstanceKey(&handle,
DeviceInstance,
KEY_READ
);
if(NT_SUCCESS(status)) {
status = PipGetRegistryValue(handle,
L"CsConfigFlags",
&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
PipRemoveStringFromValueKey (
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 = PipGetRegistryValue(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
PipAppendStringToValueKey (
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 = PipGetRegistryValue(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;
}
NTSTATUS
PipServiceInstanceToDeviceInstance (
IN PUNICODE_STRING RegistryPath,
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:
RegistryPath - Supplies the name of the service entry that controls
the device instance. This is the registry path passed to DriverEntry.
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, unicodeString;
NTSTATUS status;
HANDLE handle, handlex;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
PWSTR buffer;
//
// Open registry ServiceKeyName\Enum branch
//
status = PipOpenRegistryKey(&handle,
NULL,
RegistryPath,
KEY_ALL_ACCESS,
FALSE
);
if (!NT_SUCCESS(status)) {
DebugPrint((DEBUG_MESSAGE, "PnPIsa: Unable to open Service RegistryPath\n"));
return status;
}
RtlInitUnicodeString(&unicodeKeyName, L"ENUM");
status = PipOpenRegistryKey(&handlex,
handle,
&unicodeKeyName,
KEY_READ,
FALSE
);
ZwClose(handle);
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, L"%u", ServiceInstanceOrdinal);
status = PipGetRegistryValue ( handlex,
unicodeBuffer,
&keyValueInformation
);
ZwClose(handlex);
if (!NT_SUCCESS( status )) {
return status;
} else {
if (keyValueInformation->Type == REG_SZ) {
unicodeKeyName.Buffer = (PWSTR)KEY_VALUE_DATA(keyValueInformation);
unicodeKeyName.MaximumLength = (USHORT)keyValueInformation->DataLength;
unicodeKeyName.Length = unicodeKeyName.MaximumLength - sizeof(UNICODE_NULL);
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)) {
RtlInitUnicodeString(&unicodeString, L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\ENUM");
status = PipOpenRegistryKey(&handle,
NULL,
&unicodeString,
KEY_READ,
FALSE
);
if (NT_SUCCESS( status )) {
status = PipOpenRegistryKey (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)) {
buffer = (PWSTR)ExAllocatePool(PagedPool, unicodeKeyName.MaximumLength);
if (!buffer) {
if(ARGUMENT_PRESENT(DeviceInstanceHandle)) {
ZwClose(*DeviceInstanceHandle);
}
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlMoveMemory(buffer, unicodeKeyName.Buffer, unicodeKeyName.MaximumLength);
DeviceInstanceRegistryPath->Buffer = buffer;
DeviceInstanceRegistryPath->Length = unicodeKeyName.Length;
DeviceInstanceRegistryPath->MaximumLength = unicodeKeyName.MaximumLength;
}
}
PrepareForReturn:
ExFreePool(keyValueInformation);
return status;
}
VOID
PipLogError(
IN NTSTATUS ErrorCode,
IN ULONG UniqueErrorValue,
IN NTSTATUS FinalStatus,
IN PULONG DumpData,
IN ULONG DumpCount,
IN USHORT StringLength,
IN PWCHAR String
)
/*++
Routine Description:
This routine contains common code to write an error log entry. It is
called from other routines to avoid duplication of code. This routine
only allows caller to supply one insertion string to the error log.
Arguments:
ErrorCode - The error code for the error log packet.
UniqueErrorValue - The unique error value for the error log packet.
FinalStatus - The final status of the operation for the error log packet.
DumpData - Pointer to an array of dump data for the error log packet.
DumpCount - The number of entries in the dump data array.
StringLength - The length of insertion string *NOT* including the NULL terminater.
String - The pointer to the insertion string
Return Value:
None.
--*/
{
PIO_ERROR_LOG_PACKET errorLogEntry;
ULONG i, size;
PUCHAR p;
size = sizeof(IO_ERROR_LOG_PACKET) + DumpCount * sizeof(ULONG) +
StringLength + sizeof(UNICODE_NULL) - sizeof(ULONG);
errorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(
PipDriverObject,
(UCHAR) size
);
if (errorLogEntry != NULL) {
RtlZeroMemory(errorLogEntry, size);
errorLogEntry->ErrorCode = ErrorCode;
errorLogEntry->DumpDataSize = (USHORT) (DumpCount * sizeof(ULONG));
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
errorLogEntry->FinalStatus = FinalStatus;
for (i = 0; i < DumpCount; i++)
errorLogEntry->DumpData[i] = DumpData[i];
if (String) {
errorLogEntry->NumberOfStrings = 1;
errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) +
DumpCount * sizeof(ULONG) - sizeof(ULONG);
p= (PUCHAR)errorLogEntry + errorLogEntry->StringOffset;
RtlMoveMemory(p, String, StringLength);
}
IoWriteErrorLogEntry(errorLogEntry);
}
}
NTSTATUS
PipApplyFunctionToSubKeys(
IN HANDLE BaseHandle OPTIONAL,
IN PUNICODE_STRING KeyName OPTIONAL,
IN ACCESS_MASK DesiredAccess,
IN BOOLEAN IgnoreNonCriticalErrors,
IN PPIP_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 indicate the Subkey has been deleted or
FALSE.
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, keyDeleted;
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 = PipOpenRegistryKey(&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) {
keyDeleted = FALSE;
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 = PipOpenRegistryKey(&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.
//
keyDeleted = SubKeyCallbackRoutine(SubKeyHandle, &SubKeyName, Context);
if(DesiredAccess) {
ZwClose(SubKeyHandle);
}
ContinueWithNextSubKey:
if (!keyDeleted) {
i++;
}
}
if(KeyInformation) {
ExFreePool(KeyInformation);
}
if(CloseHandle) {
ZwClose(Handle);
}
return Status;
}
#if DBG
VOID
PipDebugPrint (
ULONG Level,
PCCHAR DebugMessage,
...
)
/*++
Routine Description:
This routine displays debugging message or causes a break.
Arguments:
Level - supplies debugging levelcode. DEBUG_MESSAGE - displays message only.
DEBUG_BREAK - displays message and break.
DebugMessage - supplies a pointer to the debugging message.
Return Value:
None.
--*/
{
UCHAR Buffer[256];
va_list ap;
va_start(ap, DebugMessage);
vsprintf(Buffer, DebugMessage, ap);
DbgPrint(Buffer);
if (Level == DEBUG_BREAK) {
DbgBreakPoint();
}
va_end(ap);
}
VOID
PipDumpIoResourceDescriptor (
IN PUCHAR Indent,
IN PIO_RESOURCE_DESCRIPTOR Desc
)
/*++
Routine Description:
This routine processes a IO_RESOURCE_DESCRIPTOR and displays it.
Arguments:
Indent - # char of indentation.
Desc - supplies a pointer to the IO_RESOURCE_DESCRIPTOR to be displayed.
Return Value:
None.
--*/
{
UCHAR c = ' ';
if (Desc->Option == IO_RESOURCE_ALTERNATIVE) {
c = 'A';
} else if (Desc->Option == IO_RESOURCE_PREFERRED) {
c = 'P';
}
switch (Desc->Type) {
case CmResourceTypePort:
DbgPrint ("%sIO %c Min: %x:%08x, Max: %x:%08x, Algn: %x, Len %x\n",
Indent, c,
Desc->u.Port.MinimumAddress.HighPart, Desc->u.Port.MinimumAddress.LowPart,
Desc->u.Port.MaximumAddress.HighPart, Desc->u.Port.MaximumAddress.LowPart,
Desc->u.Port.Alignment,
Desc->u.Port.Length
);
break;
case CmResourceTypeMemory:
DbgPrint ("%sMEM %c Min: %x:%08x, Max: %x:%08x, Algn: %x, Len %x\n",
Indent, c,
Desc->u.Memory.MinimumAddress.HighPart, Desc->u.Memory.MinimumAddress.LowPart,
Desc->u.Memory.MaximumAddress.HighPart, Desc->u.Memory.MaximumAddress.LowPart,
Desc->u.Memory.Alignment,
Desc->u.Memory.Length
);
break;
case CmResourceTypeInterrupt:
DbgPrint ("%sINT %c Min: %x, Max: %x\n",
Indent, c,
Desc->u.Interrupt.MinimumVector,
Desc->u.Interrupt.MaximumVector
);
break;
case CmResourceTypeDma:
DbgPrint ("%sDMA %c Min: %x, Max: %x\n",
Indent, c,
Desc->u.Dma.MinimumChannel,
Desc->u.Dma.MaximumChannel
);
break;
}
}
VOID
PipDumpIoResourceList (
IN PIO_RESOURCE_REQUIREMENTS_LIST IoList
)
/*++
Routine Description:
This routine displays Io resource requirements list.
Arguments:
IoList - supplies a pointer to the Io resource requirements list to be displayed.
Return Value:
None.
--*/
{
PIO_RESOURCE_LIST resList;
PIO_RESOURCE_DESCRIPTOR resDesc;
ULONG listCount, count, i, j;
if (IoList == NULL) {
return;
}
DbgPrint("Pnp Bios IO Resource Requirements List for Slot %x -\n", IoList->SlotNumber);
DbgPrint(" List Count = %x, Bus Number = %x\n", IoList->AlternativeLists, IoList->BusNumber);
listCount = IoList->AlternativeLists;
resList = &IoList->List[0];
for (i = 0; i < listCount; i++) {
DbgPrint(" Version = %x, Revision = %x, Desc count = %x\n", resList->Version,
resList->Revision, resList->Count);
resDesc = &resList->Descriptors[0];
count = resList->Count;
for (j = 0; j < count; j++) {
PipDumpIoResourceDescriptor(" ", resDesc);
resDesc++;
}
resList = (PIO_RESOURCE_LIST) resDesc;
}
}
VOID
PipDumpCmResourceDescriptor (
IN PUCHAR Indent,
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Desc
)
/*++
Routine Description:
This routine processes a IO_RESOURCE_DESCRIPTOR and displays it.
Arguments:
Indent - # char of indentation.
Desc - supplies a pointer to the IO_RESOURCE_DESCRIPTOR to be displayed.
Return Value:
None.
--*/
{
switch (Desc->Type) {
case CmResourceTypePort:
DbgPrint ("%sIO Start: %x:%08x, Length: %x\n",
Indent,
Desc->u.Port.Start.HighPart, Desc->u.Port.Start.LowPart,
Desc->u.Port.Length
);
break;
case CmResourceTypeMemory:
DbgPrint ("%sMEM Start: %x:%08x, Length: %x\n",
Indent,
Desc->u.Memory.Start.HighPart, Desc->u.Memory.Start.LowPart,
Desc->u.Memory.Length
);
break;
case CmResourceTypeInterrupt:
DbgPrint ("%sINT Level: %x, Vector: %x, Affinity: %x\n",
Indent,
Desc->u.Interrupt.Level,
Desc->u.Interrupt.Vector,
Desc->u.Interrupt.Affinity
);
break;
case CmResourceTypeDma:
DbgPrint ("%sDMA Channel: %x, Port: %x\n",
Indent,
Desc->u.Dma.Channel,
Desc->u.Dma.Port
);
break;
}
}
VOID
PipDumpCmResourceList (
IN PCM_RESOURCE_LIST CmList
)
/*++
Routine Description:
This routine displays CM resource list.
Arguments:
CmList - supplies a pointer to CM resource list
Return Value:
None.
--*/
{
PCM_FULL_RESOURCE_DESCRIPTOR fullDesc;
PCM_PARTIAL_RESOURCE_LIST partialDesc;
PCM_PARTIAL_RESOURCE_DESCRIPTOR desc;
ULONG count, i;
if (CmList) {
fullDesc = &CmList->List[0];
DbgPrint("Pnp Bios Cm Resource List -\n");
DbgPrint(" List Count = %x, Bus Number = %x\n", CmList->Count, fullDesc->BusNumber);
partialDesc = &fullDesc->PartialResourceList;
DbgPrint(" Version = %x, Revision = %x, Desc count = %x\n", partialDesc->Version,
partialDesc->Revision, partialDesc->Count);
count = partialDesc->Count;
desc = &partialDesc->PartialDescriptors[0];
for (i = 0; i < count; i++) {
PipDumpCmResourceDescriptor(" ", desc);
desc++;
}
}
}
#endif