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.
4414 lines
137 KiB
4414 lines
137 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
devintrf.c
|
|
|
|
Abstract:
|
|
|
|
This module contains APIs and routines for handling Device Interfaces.
|
|
|
|
Author:
|
|
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "pnpmgrp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Guid related definitions
|
|
//
|
|
|
|
#define GUID_STRING_LENGTH 38
|
|
#define GUID_STRING_SIZE (GUID_STRING_LENGTH * sizeof(WCHAR))
|
|
|
|
//
|
|
// Definitions for IoGetDeviceInterfaces
|
|
//
|
|
|
|
#define INITIAL_INFO_BUFFER_SIZE 512
|
|
#define INFO_BUFFER_GROW_SIZE 64
|
|
#define INITIAL_SYMLINK_BUFFER_SIZE 1024
|
|
#define SYMLINK_BUFFER_GROW_SIZE 128
|
|
#define INITIAL_RETURN_BUFFER_SIZE 4096
|
|
#define RETURN_BUFFER_GROW_SIZE 512
|
|
|
|
//
|
|
// This should never have to grow, since it accomodates the maximum length of a
|
|
// device instance name.
|
|
//
|
|
#define INITIAL_DEVNODE_NAME_BUFFER_SIZE \
|
|
(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + \
|
|
(MAX_DEVICE_ID_LEN * sizeof(WCHAR)))
|
|
|
|
//
|
|
// Definitions for IoOpenDeviceInterfaceRegistryKey
|
|
//
|
|
|
|
#define KEY_STRING_PREFIX TEXT("##?#")
|
|
|
|
//
|
|
// Definitions for IoRegisterDeviceInterface
|
|
//
|
|
|
|
#define SEPERATOR_STRING TEXT("\\")
|
|
#define SEPERATOR_CHAR (L'\\')
|
|
#define ALT_SEPERATOR_CHAR (L'/')
|
|
#define REPLACED_SEPERATOR_STRING TEXT("#")
|
|
#define REPLACED_SEPERATOR_CHAR (L'#')
|
|
#define USER_SYMLINK_STRING_PREFIX TEXT("\\\\?\\")
|
|
#define KERNEL_SYMLINK_STRING_PREFIX TEXT("\\??\\")
|
|
#define GLOBAL_SYMLINK_STRING_PREFIX TEXT("\\GLOBAL??\\")
|
|
#define REFSTRING_PREFIX_CHAR (L'#')
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
|
|
NTSTATUS
|
|
IopAppendBuffer(
|
|
IN PBUFFER_INFO Info,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize
|
|
);
|
|
|
|
NTSTATUS
|
|
IopBuildSymbolicLinkStrings(
|
|
IN PUNICODE_STRING DeviceString,
|
|
IN PUNICODE_STRING GuidString,
|
|
IN PUNICODE_STRING ReferenceString OPTIONAL,
|
|
OUT PUNICODE_STRING UserString,
|
|
OUT PUNICODE_STRING KernelString
|
|
);
|
|
|
|
NTSTATUS
|
|
IopBuildGlobalSymbolicLinkString(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
OUT PUNICODE_STRING GlobalString
|
|
);
|
|
|
|
NTSTATUS
|
|
IopDeviceInterfaceKeysFromSymbolicLink(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE DeviceInterfaceClassKey OPTIONAL,
|
|
OUT PHANDLE DeviceInterfaceKey OPTIONAL,
|
|
OUT PHANDLE DeviceInterfaceInstanceKey OPTIONAL
|
|
);
|
|
|
|
NTSTATUS
|
|
IopDropReferenceString(
|
|
OUT PUNICODE_STRING OutString,
|
|
IN PUNICODE_STRING InString
|
|
);
|
|
|
|
NTSTATUS
|
|
IopOpenOrCreateDeviceInterfaceSubKeys(
|
|
OUT PHANDLE InterfaceKeyHandle OPTIONAL,
|
|
OUT PULONG InterfaceKeyDisposition OPTIONAL,
|
|
OUT PHANDLE InterfaceInstanceKeyHandle OPTIONAL,
|
|
OUT PULONG InterfaceInstanceDisposition OPTIONAL,
|
|
IN HANDLE InterfaceClassKeyHandle,
|
|
IN PUNICODE_STRING DeviceInterfaceName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN BOOLEAN Create
|
|
);
|
|
|
|
NTSTATUS
|
|
IopParseSymbolicLinkName(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
OUT PUNICODE_STRING PrefixString OPTIONAL,
|
|
OUT PUNICODE_STRING MungedPathString OPTIONAL,
|
|
OUT PUNICODE_STRING GuidString OPTIONAL,
|
|
OUT PUNICODE_STRING RefString OPTIONAL,
|
|
OUT PBOOLEAN RefStringPresent OPTIONAL,
|
|
OUT LPGUID Guid OPTIONAL
|
|
);
|
|
|
|
NTSTATUS
|
|
IopReplaceSeperatorWithPound(
|
|
OUT PUNICODE_STRING OutString,
|
|
IN PUNICODE_STRING InString
|
|
);
|
|
|
|
NTSTATUS
|
|
IopSetRegistryStringValue(
|
|
IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN PUNICODE_STRING ValueData
|
|
);
|
|
|
|
NTSTATUS
|
|
PiDeferSetInterfaceState(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PUNICODE_STRING SymbolicLinkName
|
|
);
|
|
|
|
NTSTATUS
|
|
PiRemoveDeferredSetInterfaceState(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PUNICODE_STRING SymbolicLinkName
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IoGetDeviceInterfaceAlias)
|
|
#pragma alloc_text(PAGE, IoGetDeviceInterfaces)
|
|
#pragma alloc_text(PAGE, IoOpenDeviceInterfaceRegistryKey)
|
|
#pragma alloc_text(PAGE, IoRegisterDeviceInterface)
|
|
#pragma alloc_text(PAGE, IoSetDeviceInterfaceState)
|
|
|
|
#pragma alloc_text(PAGE, IopAllocateBuffer)
|
|
#pragma alloc_text(PAGE, IopAllocateUnicodeString)
|
|
#pragma alloc_text(PAGE, IopAppendBuffer)
|
|
#pragma alloc_text(PAGE, IopBuildSymbolicLinkStrings)
|
|
#pragma alloc_text(PAGE, IopBuildGlobalSymbolicLinkString)
|
|
#pragma alloc_text(PAGE, IopDeviceInterfaceKeysFromSymbolicLink)
|
|
#pragma alloc_text(PAGE, IopDropReferenceString)
|
|
#pragma alloc_text(PAGE, IopFreeBuffer)
|
|
#pragma alloc_text(PAGE, IopGetDeviceInterfaces)
|
|
#pragma alloc_text(PAGE, IopOpenOrCreateDeviceInterfaceSubKeys)
|
|
#pragma alloc_text(PAGE, IopParseSymbolicLinkName)
|
|
#pragma alloc_text(PAGE, IopProcessSetInterfaceState)
|
|
#pragma alloc_text(PAGE, IopRegisterDeviceInterface)
|
|
#pragma alloc_text(PAGE, IopRemoveDeviceInterfaces)
|
|
#pragma alloc_text(PAGE, IopDisableDeviceInterfaces)
|
|
#pragma alloc_text(PAGE, IopReplaceSeperatorWithPound)
|
|
#pragma alloc_text(PAGE, IopResizeBuffer)
|
|
#pragma alloc_text(PAGE, IopSetRegistryStringValue)
|
|
#pragma alloc_text(PAGE, IopUnregisterDeviceInterface)
|
|
#pragma alloc_text(PAGE, IopDoDeferredSetInterfaceState)
|
|
#pragma alloc_text(PAGE, PiDeferSetInterfaceState)
|
|
#pragma alloc_text(PAGE, PiRemoveDeferredSetInterfaceState)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IopAllocateBuffer(
|
|
IN PBUFFER_INFO Info,
|
|
IN ULONG Size
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a buffer of Size bytes and initialises the BUFFER_INFO
|
|
structure so the current position is at the start of the buffer.
|
|
|
|
Parameters:
|
|
|
|
Info - Pointer to a buffer info structure to be used to manage the new
|
|
buffer
|
|
|
|
Size - The number of bytes to be allocated for the buffer
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Info);
|
|
|
|
Info->Buffer = ExAllocatePool(PagedPool, Size);
|
|
Info->Current = Info->Buffer;
|
|
if (Info->Buffer == NULL) {
|
|
|
|
Info->MaxSize = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Info->MaxSize = Size;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopResizeBuffer(
|
|
IN PBUFFER_INFO Info,
|
|
IN ULONG NewSize,
|
|
IN BOOLEAN CopyContents
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a new buffer of NewSize bytes and associates it with Info, freeing
|
|
the old buffer. It will optionally copy the data stored in the old buffer
|
|
into the new buffer and update the current position.
|
|
|
|
Parameters:
|
|
|
|
Info - Pointer to a buffer info structure to be used to manage the buffer
|
|
|
|
NewSize - The new size of the buffer in bytes
|
|
|
|
CopyContents - If TRUE indicates that the contents of the old buffer should
|
|
be copied to the new buffer
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG used;
|
|
PCHAR newBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Info);
|
|
|
|
newBuffer = ExAllocatePool(PagedPool, NewSize);
|
|
if (newBuffer == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (CopyContents) {
|
|
//
|
|
// Assert there is room in the buffer
|
|
//
|
|
used = (ULONG)(Info->Current - Info->Buffer);
|
|
ASSERT(used < NewSize);
|
|
|
|
RtlCopyMemory(newBuffer, Info->Buffer, used);
|
|
|
|
Info->Current = newBuffer + used;
|
|
|
|
} else {
|
|
|
|
Info->Current = newBuffer;
|
|
}
|
|
|
|
ExFreePool(Info->Buffer);
|
|
|
|
Info->Buffer = newBuffer;
|
|
Info->MaxSize = NewSize;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
IopFreeBuffer(
|
|
IN PBUFFER_INFO Info
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the buffer associated with Info and resets all Info fields
|
|
|
|
Parameters:
|
|
|
|
Info - Pointer to a buffer info structure to be used to manage the buffer
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Info);
|
|
|
|
//
|
|
// Free the buffer
|
|
//
|
|
if (Info->Buffer) {
|
|
|
|
ExFreePool(Info->Buffer);
|
|
}
|
|
|
|
//
|
|
// Zero out the info parameters so we can't accidently used the free buffer
|
|
//
|
|
Info->Buffer = NULL;
|
|
Info->Current = NULL;
|
|
Info->MaxSize = 0;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopAppendBuffer(
|
|
IN PBUFFER_INFO Info,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies the data to the end of the buffer, resizing if necessary. The
|
|
current position is set to the end of the data just added.
|
|
|
|
Parameters:
|
|
|
|
Info - Pointer to a buffer info structure to be used to manage the buffer
|
|
|
|
Data - Pointer to the data to be added to the buffer
|
|
|
|
DataSize - The size of the data pointed to by Data in bytes
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG free, used;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Info);
|
|
|
|
used = (ULONG)(Info->Current - Info->Buffer);
|
|
free = Info->MaxSize - used;
|
|
|
|
if (free < DataSize) {
|
|
|
|
status = IopResizeBuffer(Info, used + DataSize, TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the data into the buffer
|
|
//
|
|
RtlCopyMemory(Info->Current, Data, DataSize);
|
|
|
|
//
|
|
// Advance down the buffer
|
|
//
|
|
Info->Current += DataSize;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IopGetDeviceInterfaces(
|
|
IN CONST GUID *InterfaceClassGuid,
|
|
IN PUNICODE_STRING DevicePath OPTIONAL,
|
|
IN ULONG Flags,
|
|
IN BOOLEAN UserModeFormat,
|
|
OUT PWSTR *SymbolicLinkList,
|
|
OUT PULONG SymbolicLinkListSize OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API allows a WDM driver to get a list of paths that represent all
|
|
devices registered for the specified interface class.
|
|
|
|
Parameters:
|
|
|
|
InterfaceClassGuid - Supplies a pointer to a GUID representing the interface
|
|
class for whom a list of members is to be retrieved
|
|
|
|
DevicePath - Optionally, supplies a pointer to a unicode string containing
|
|
the enumeration path for a device for whom interfaces of the specified
|
|
class are to be re-trieved. If this parameter is not supplied, then
|
|
all interface devices (regardless of what physical device exposes them)
|
|
will be returned.
|
|
|
|
Flags - Supplies flags that modify the behavior of list retrieval.
|
|
The following flags are presently defined:
|
|
|
|
DEVICE_INTERFACE_INCLUDE_NONACTIVE -- If this flag is specified, then
|
|
all interface devices, whether currently active or not, will be
|
|
returned (potentially filtered based on the Physi-calDeviceObject,
|
|
if specified).
|
|
|
|
UserModeFormat - If TRUE the multi-sz returned will have user mode prefixes
|
|
(\\?\) otherwise they will have kernel mode prefixes (\??\).
|
|
|
|
SymbolicLinkList - Supplies the address of a character pointer, that on
|
|
success will contain a multi-sz list of \??\ symbolic link
|
|
names that provide the requested functionality. The caller is
|
|
responsible for freeing the memory via ExFreePool.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING tempString, defaultString;
|
|
UNICODE_STRING guidString, symLinkString, devnodeString;
|
|
BUFFER_INFO returnBuffer, infoBuffer, symLinkBuffer, devnodeNameBuffer;
|
|
PKEY_VALUE_FULL_INFORMATION pDefaultInfo = NULL;
|
|
ULONG keyIndex, instanceKeyIndex, resultSize;
|
|
HANDLE hDeviceClasses, hClass, hKey, hInstanceKey, hControl;
|
|
BOOLEAN defaultPresent = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialise out parameters
|
|
//
|
|
|
|
*SymbolicLinkList = NULL;
|
|
|
|
//
|
|
// Convert the GUID into a string
|
|
//
|
|
|
|
status = RtlStringFromGUID(InterfaceClassGuid, &guidString);
|
|
if (!NT_SUCCESS(status)) {
|
|
returnBuffer.Buffer = NULL;
|
|
returnBuffer.MaxSize = 0;
|
|
goto finalClean;
|
|
}
|
|
|
|
//
|
|
// Allocate initial buffers
|
|
//
|
|
|
|
status = IopAllocateBuffer(&returnBuffer,
|
|
INITIAL_RETURN_BUFFER_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean0;
|
|
}
|
|
|
|
status = IopAllocateBuffer(&infoBuffer,
|
|
INITIAL_INFO_BUFFER_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
status = IopAllocateBuffer(&symLinkBuffer,
|
|
INITIAL_SYMLINK_BUFFER_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean2;
|
|
}
|
|
|
|
status = IopAllocateBuffer(&devnodeNameBuffer,
|
|
INITIAL_DEVNODE_NAME_BUFFER_SIZE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean2a;
|
|
}
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry
|
|
// manipulation portion solves this problem
|
|
//
|
|
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Open HKLM\System\CurrentControlSet\Control\DeviceClasses key
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_FULL_PATH_DEVICE_CLASSES);
|
|
status = IopCreateRegistryKeyEx( &hDeviceClasses,
|
|
NULL,
|
|
&tempString,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean3;
|
|
}
|
|
|
|
//
|
|
// Open function class GUID key
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &hClass,
|
|
hDeviceClasses,
|
|
&guidString,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
ZwClose(hDeviceClasses);
|
|
|
|
if(status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_PATH_NOT_FOUND) {
|
|
|
|
//
|
|
// The path does not exist - return a single null character buffer
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto clean5;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
goto clean3;
|
|
}
|
|
|
|
//
|
|
// Get the default value if it exists
|
|
//
|
|
|
|
status = IopGetRegistryValue(hClass,
|
|
REGSTR_VAL_DEFAULT,
|
|
&pDefaultInfo
|
|
);
|
|
|
|
|
|
if (NT_SUCCESS(status)
|
|
&& pDefaultInfo->Type == REG_SZ
|
|
&& pDefaultInfo->DataLength >= sizeof(WCHAR)) {
|
|
|
|
//
|
|
// We have a default - construct a counted string from the default
|
|
//
|
|
|
|
defaultPresent = TRUE;
|
|
defaultString.Buffer = (PWSTR) KEY_VALUE_DATA(pDefaultInfo);
|
|
defaultString.Length = (USHORT) pDefaultInfo->DataLength - sizeof(UNICODE_NULL);
|
|
defaultString.MaximumLength = defaultString.Length;
|
|
|
|
//
|
|
// Open the device interface instance key for the default name.
|
|
//
|
|
status = IopOpenOrCreateDeviceInterfaceSubKeys(NULL,
|
|
NULL,
|
|
&hKey,
|
|
NULL,
|
|
hClass,
|
|
&defaultString,
|
|
KEY_READ,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
defaultPresent = FALSE;
|
|
ExFreePool(pDefaultInfo);
|
|
//
|
|
// Continue with the call but ignore the invalid default entry
|
|
//
|
|
} else {
|
|
|
|
//
|
|
// If we are just supposed to return live interfaces, then make sure this default
|
|
// interface is linked.
|
|
//
|
|
|
|
if (!(Flags & DEVICE_INTERFACE_INCLUDE_NONACTIVE)) {
|
|
|
|
defaultPresent = FALSE;
|
|
|
|
//
|
|
// Open the control subkey
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &hControl,
|
|
hKey,
|
|
&tempString,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Get the linked value
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_LINKED);
|
|
ASSERT(infoBuffer.MaxSize >= sizeof(KEY_VALUE_PARTIAL_INFORMATION));
|
|
status = ZwQueryValueKey(hControl,
|
|
&tempString,
|
|
KeyValuePartialInformation,
|
|
(PVOID) infoBuffer.Buffer,
|
|
infoBuffer.MaxSize,
|
|
&resultSize
|
|
);
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
ZwClose(hControl);
|
|
|
|
//
|
|
// We don't need to check the buffer was big enough because it starts
|
|
// off that way and doesn't get any smaller!
|
|
//
|
|
|
|
if (NT_SUCCESS(status)
|
|
&& (((PKEY_VALUE_PARTIAL_INFORMATION)(infoBuffer.Buffer))->Type == REG_DWORD)
|
|
&& (((PKEY_VALUE_PARTIAL_INFORMATION)(infoBuffer.Buffer))->DataLength == sizeof(ULONG))) {
|
|
|
|
defaultPresent = *(PULONG)(((PKEY_VALUE_PARTIAL_INFORMATION)(infoBuffer.Buffer))->Data)
|
|
? TRUE
|
|
: FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
ZwClose(hKey);
|
|
|
|
if(defaultPresent) {
|
|
//
|
|
// Add the default as the first entry in the return buffer and patch to usermode if necessary
|
|
//
|
|
status = IopAppendBuffer(&returnBuffer,
|
|
defaultString.Buffer,
|
|
defaultString.Length + sizeof(UNICODE_NULL)
|
|
);
|
|
|
|
if (!UserModeFormat) {
|
|
|
|
RtlCopyMemory(returnBuffer.Buffer,
|
|
KERNEL_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX)
|
|
);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// The default device interface isn't active--free the memory for the name buffer now.
|
|
//
|
|
ExFreePool(pDefaultInfo);
|
|
}
|
|
}
|
|
|
|
} else if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_PATH_NOT_FOUND) {
|
|
//
|
|
// Do nothing - there is no default
|
|
//
|
|
} else {
|
|
//
|
|
// An unexpected error occured - clean up
|
|
//
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ExFreePool(pDefaultInfo);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ZwClose(hClass);
|
|
goto clean4;
|
|
}
|
|
|
|
//
|
|
// Iterate through the subkeys under this interface class key.
|
|
//
|
|
keyIndex = 0;
|
|
ASSERT(infoBuffer.MaxSize >= sizeof(KEY_BASIC_INFORMATION));
|
|
while((status = ZwEnumerateKey(hClass,
|
|
keyIndex,
|
|
KeyBasicInformation,
|
|
(PVOID) infoBuffer.Buffer,
|
|
infoBuffer.MaxSize,
|
|
&resultSize
|
|
)) != STATUS_NO_MORE_ENTRIES) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
status = IopResizeBuffer(&infoBuffer, resultSize, FALSE);
|
|
continue;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
ZwClose(hClass);
|
|
goto clean4;
|
|
}
|
|
|
|
//
|
|
// Open up this interface key.
|
|
//
|
|
tempString.Length = (USHORT) ((PKEY_BASIC_INFORMATION)(infoBuffer.Buffer))->NameLength;
|
|
tempString.MaximumLength = tempString.Length;
|
|
tempString.Buffer = ((PKEY_BASIC_INFORMATION)(infoBuffer.Buffer))->Name;
|
|
|
|
//
|
|
// Open the associated key
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &hKey,
|
|
hClass,
|
|
&tempString,
|
|
KEY_READ
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// For some reason we couldn't open this key--skip it and move on.
|
|
//
|
|
keyIndex++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If we're filtering on a particular PDO, then retrieve the owning device
|
|
// instance name for this interface key, and make sure they match.
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_DEVICE_INSTANCE);
|
|
ASSERT(devnodeNameBuffer.MaxSize >= sizeof(KEY_VALUE_PARTIAL_INFORMATION));
|
|
while ((status = ZwQueryValueKey(hKey,
|
|
&tempString,
|
|
KeyValuePartialInformation,
|
|
devnodeNameBuffer.Buffer,
|
|
devnodeNameBuffer.MaxSize,
|
|
&resultSize
|
|
)) == STATUS_BUFFER_OVERFLOW) {
|
|
|
|
status = IopResizeBuffer(&devnodeNameBuffer, resultSize, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(hKey);
|
|
ZwClose(hClass);
|
|
goto clean4;
|
|
}
|
|
}
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (!(NT_SUCCESS(status)
|
|
&& ((PKEY_VALUE_PARTIAL_INFORMATION)(devnodeNameBuffer.Buffer))->Type == REG_SZ
|
|
&& ((PKEY_VALUE_PARTIAL_INFORMATION)(devnodeNameBuffer.Buffer))->DataLength > sizeof(WCHAR))) {
|
|
goto CloseInterfaceKeyAndContinue;
|
|
}
|
|
|
|
//
|
|
// Build counted string
|
|
//
|
|
|
|
devnodeString.Length = (USHORT) ((PKEY_VALUE_PARTIAL_INFORMATION)(devnodeNameBuffer.Buffer))->DataLength - sizeof(UNICODE_NULL);
|
|
devnodeString.MaximumLength = tempString.Length;
|
|
devnodeString.Buffer = (PWSTR) ((PKEY_VALUE_PARTIAL_INFORMATION)(devnodeNameBuffer.Buffer))->Data;
|
|
|
|
//
|
|
// Enumerate each interface instance subkey under this PDO's interface key.
|
|
//
|
|
instanceKeyIndex = 0;
|
|
ASSERT(infoBuffer.MaxSize >= sizeof(KEY_BASIC_INFORMATION));
|
|
while((status = ZwEnumerateKey(hKey,
|
|
instanceKeyIndex,
|
|
KeyBasicInformation,
|
|
(PVOID) infoBuffer.Buffer,
|
|
infoBuffer.MaxSize,
|
|
&resultSize
|
|
)) != STATUS_NO_MORE_ENTRIES) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
status = IopResizeBuffer(&infoBuffer, resultSize, FALSE);
|
|
continue;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
ZwClose(hKey);
|
|
ZwClose(hClass);
|
|
goto clean4;
|
|
}
|
|
|
|
//
|
|
// Open up this interface instance key.
|
|
//
|
|
tempString.Length = (USHORT) ((PKEY_BASIC_INFORMATION)(infoBuffer.Buffer))->NameLength;
|
|
tempString.MaximumLength = tempString.Length;
|
|
tempString.Buffer = ((PKEY_BASIC_INFORMATION)(infoBuffer.Buffer))->Name;
|
|
|
|
//
|
|
// Open the associated key
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &hInstanceKey,
|
|
hKey,
|
|
&tempString,
|
|
KEY_READ
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// For some reason we couldn't open this key--skip it and move on.
|
|
//
|
|
instanceKeyIndex++;
|
|
continue;
|
|
}
|
|
|
|
if (!(Flags & DEVICE_INTERFACE_INCLUDE_NONACTIVE)) {
|
|
|
|
//
|
|
// Open the control subkey
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &hControl,
|
|
hInstanceKey,
|
|
&tempString,
|
|
KEY_READ
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// We have no control subkey so can't be linked -
|
|
// continue enumerating the keys ignoring this one
|
|
//
|
|
goto CloseInterfaceInstanceKeyAndContinue;
|
|
}
|
|
|
|
//
|
|
// Get the linked value
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_LINKED);
|
|
ASSERT(infoBuffer.MaxSize >= sizeof(KEY_VALUE_PARTIAL_INFORMATION));
|
|
status = ZwQueryValueKey(hControl,
|
|
&tempString,
|
|
KeyValuePartialInformation,
|
|
(PVOID) infoBuffer.Buffer,
|
|
infoBuffer.MaxSize,
|
|
&resultSize
|
|
);
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
ZwClose(hControl);
|
|
|
|
//
|
|
// We don't need to check the buffer was big enough because it starts
|
|
// off that way and doesn't get any smaller!
|
|
//
|
|
|
|
if (!NT_SUCCESS(status)
|
|
|| (((PKEY_VALUE_PARTIAL_INFORMATION)(infoBuffer.Buffer))->Type != REG_DWORD)
|
|
|| (((PKEY_VALUE_PARTIAL_INFORMATION)(infoBuffer.Buffer))->DataLength != sizeof(ULONG))
|
|
|| !*(PULONG)(((PKEY_VALUE_PARTIAL_INFORMATION)(infoBuffer.Buffer))->Data)) {
|
|
|
|
//
|
|
// We are NOT linked so continue enumerating the keys ignoring this one
|
|
//
|
|
goto CloseInterfaceInstanceKeyAndContinue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the "SymbolicLink" value and place the information into the symLink buffer
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_SYMBOLIC_LINK);
|
|
ASSERT(symLinkBuffer.MaxSize >= sizeof(KEY_VALUE_PARTIAL_INFORMATION));
|
|
while ((status = ZwQueryValueKey(hInstanceKey,
|
|
&tempString,
|
|
KeyValuePartialInformation,
|
|
symLinkBuffer.Buffer,
|
|
symLinkBuffer.MaxSize,
|
|
&resultSize
|
|
)) == STATUS_BUFFER_OVERFLOW) {
|
|
|
|
status = IopResizeBuffer(&symLinkBuffer, resultSize, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(hInstanceKey);
|
|
ZwClose(hKey);
|
|
ZwClose(hClass);
|
|
goto clean4;
|
|
}
|
|
}
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (!(NT_SUCCESS(status)
|
|
&& ((PKEY_VALUE_PARTIAL_INFORMATION)(symLinkBuffer.Buffer))->Type == REG_SZ
|
|
&& ((PKEY_VALUE_PARTIAL_INFORMATION)(symLinkBuffer.Buffer))->DataLength > sizeof(WCHAR))) {
|
|
goto CloseInterfaceInstanceKeyAndContinue;
|
|
}
|
|
|
|
//
|
|
// Build counted string from value data
|
|
//
|
|
|
|
symLinkString.Length = (USHORT) ((PKEY_VALUE_PARTIAL_INFORMATION)(symLinkBuffer.Buffer))->DataLength - sizeof(UNICODE_NULL);
|
|
symLinkString.MaximumLength = symLinkString.Length;
|
|
symLinkString.Buffer = (PWSTR) ((PKEY_VALUE_PARTIAL_INFORMATION)(symLinkBuffer.Buffer))->Data;
|
|
|
|
//
|
|
// If we have a default, check this is not it
|
|
//
|
|
|
|
if (defaultPresent) {
|
|
|
|
if (RtlCompareUnicodeString(&defaultString, &symLinkString, TRUE) == 0) {
|
|
|
|
//
|
|
// We have already added the default to the beginning of the buffer so skip it
|
|
//
|
|
goto CloseInterfaceInstanceKeyAndContinue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we are only returning interfaces for a particular PDO then check
|
|
// this is from that PDO
|
|
//
|
|
if (ARGUMENT_PRESENT(DevicePath)) {
|
|
//
|
|
// Check if it is from the same PDO
|
|
//
|
|
if (RtlCompareUnicodeString(DevicePath, &devnodeString, TRUE) != 0) {
|
|
//
|
|
// If not then go onto the next key
|
|
//
|
|
goto CloseInterfaceInstanceKeyAndContinue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the symLink string to the return buffer including the NULL termination
|
|
//
|
|
|
|
status = IopAppendBuffer(&returnBuffer,
|
|
symLinkString.Buffer,
|
|
symLinkString.Length + sizeof(UNICODE_NULL)
|
|
);
|
|
|
|
ASSERT(((PWSTR) returnBuffer.Current)[-1] == UNICODE_NULL);
|
|
|
|
//
|
|
// If we are returning KM strings then patch the prefix
|
|
//
|
|
|
|
if (!UserModeFormat) {
|
|
|
|
RtlCopyMemory(returnBuffer.Current - (symLinkString.Length + sizeof(UNICODE_NULL)),
|
|
KERNEL_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX)
|
|
);
|
|
}
|
|
|
|
CloseInterfaceInstanceKeyAndContinue:
|
|
ZwClose(hInstanceKey);
|
|
instanceKeyIndex++;
|
|
}
|
|
|
|
CloseInterfaceKeyAndContinue:
|
|
ZwClose(hKey);
|
|
keyIndex++;
|
|
}
|
|
|
|
ZwClose(hClass);
|
|
|
|
clean5:
|
|
//
|
|
// We've got then all! Resize to leave space for a terminating NULL.
|
|
//
|
|
|
|
status = IopResizeBuffer(&returnBuffer,
|
|
(ULONG) (returnBuffer.Current - returnBuffer.Buffer + sizeof(UNICODE_NULL)),
|
|
TRUE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Terminate the buffer
|
|
//
|
|
*((PWSTR) returnBuffer.Current) = UNICODE_NULL;
|
|
}
|
|
|
|
clean4:
|
|
if (defaultPresent) {
|
|
ExFreePool(pDefaultInfo);
|
|
}
|
|
|
|
clean3:
|
|
PiUnlockPnpRegistry();
|
|
IopFreeBuffer(&devnodeNameBuffer);
|
|
|
|
clean2a:
|
|
IopFreeBuffer(&symLinkBuffer);
|
|
|
|
clean2:
|
|
IopFreeBuffer(&infoBuffer);
|
|
|
|
clean1:
|
|
if (!NT_SUCCESS(status)) {
|
|
IopFreeBuffer(&returnBuffer);
|
|
}
|
|
|
|
clean0:
|
|
RtlFreeUnicodeString(&guidString);
|
|
|
|
finalClean:
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
*SymbolicLinkList = (PWSTR) returnBuffer.Buffer;
|
|
|
|
if (ARGUMENT_PRESENT(SymbolicLinkListSize)) {
|
|
*SymbolicLinkListSize = returnBuffer.MaxSize;
|
|
}
|
|
|
|
} else {
|
|
|
|
*SymbolicLinkList = NULL;
|
|
|
|
if (ARGUMENT_PRESENT(SymbolicLinkListSize)) {
|
|
*SymbolicLinkListSize = 0;
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IoGetDeviceInterfaces(
|
|
IN CONST GUID *InterfaceClassGuid,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
|
|
IN ULONG Flags,
|
|
OUT PWSTR *SymbolicLinkList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API allows a WDM driver to get a list of paths that represent all
|
|
device interfaces registered for the specified interface class.
|
|
|
|
Parameters:
|
|
|
|
InterfaceClassGuid - Supplies a pointer to a GUID representing the interface
|
|
class for whom a list of members is to be retrieved
|
|
|
|
PhysicalDeviceObject - Optionally, supplies a pointer to the PDO for whom
|
|
interfaces of the specified class are to be re-trieved. If this
|
|
parameteris not supplied, then all interface devices (regardless of what
|
|
physical device exposes them) will be returned.
|
|
|
|
Flags - Supplies flags that modify the behavior of list retrieval.
|
|
The following flags are presently defined:
|
|
|
|
DEVICE_INTERFACE_INCLUDE_NONACTIVE -- If this flag is specified, then
|
|
all device interfaces, whether currently active or not, will be
|
|
returned (potentially filtered based on the PhysicalDeviceObject, if
|
|
specified).
|
|
|
|
SymbolicLinkList - Supplies the address of a character pointer, that on
|
|
success will contain a multi-sz list of \DosDevices\ symbolic link
|
|
names that provide the requested functionality. The caller is
|
|
responsible for freeing the memory via ExFreePool
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PUNICODE_STRING pDeviceName = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check we have a PDO and if so extract the instance path from it
|
|
//
|
|
if (ARGUMENT_PRESENT(PhysicalDeviceObject)) {
|
|
|
|
ASSERT_PDO(PhysicalDeviceObject);
|
|
|
|
pDeviceName = &PP_DO_TO_DN(PhysicalDeviceObject)->InstancePath;
|
|
}
|
|
|
|
status = IopGetDeviceInterfaces(
|
|
InterfaceClassGuid,
|
|
pDeviceName,
|
|
Flags,
|
|
FALSE,
|
|
SymbolicLinkList,
|
|
NULL);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IoSetDeviceInterfaceState(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN BOOLEAN Enable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This DDI allows a device class to activate and deactivate an association
|
|
previously registered using IoRegisterDeviceInterface
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to the symbolic link name which was
|
|
returned by IoRegisterDeviceInterface when the interface was registered,
|
|
or as returned by IoGetDeviceInterfaces.
|
|
|
|
Enable - If TRUE (non-zero), the interface will be enabled. If FALSE, it
|
|
will be disabled.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry
|
|
// manipulation portion solves this problem
|
|
//
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
status = IopProcessSetInterfaceState(SymbolicLinkName, Enable, TRUE);
|
|
|
|
PiUnlockPnpRegistry();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// If we failed to disable an interface (most likely because the
|
|
// interface keys have already been deleted) report success.
|
|
//
|
|
if (!Enable) {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IoOpenDeviceInterfaceRegistryKey(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE DeviceInterfaceKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will open the registry key where the data associated with a
|
|
specific device interface can be stored.
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to the symbolic link name which was
|
|
returned by IoRegisterDeviceInterface when the device class was
|
|
registered.
|
|
|
|
DesiredAccess - Supplies the access privileges to the key the caller wants.
|
|
|
|
DeviceInterfaceKey - Supplies a pointer to a handle which on success will
|
|
contain the handle to the requested registry key.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hKey;
|
|
UNICODE_STRING unicodeString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry
|
|
// manipulation portion solves this problem
|
|
//
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Open the interface device key
|
|
//
|
|
status = IopDeviceInterfaceKeysFromSymbolicLink(
|
|
SymbolicLinkName,
|
|
KEY_READ,
|
|
NULL,
|
|
NULL,
|
|
&hKey);
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Open the "Device Parameters" subkey.
|
|
//
|
|
PiWstrToUnicodeString(&unicodeString, REGSTR_KEY_DEVICEPARAMETERS);
|
|
status = IopCreateRegistryKeyEx(
|
|
DeviceInterfaceKey,
|
|
hKey,
|
|
&unicodeString,
|
|
DesiredAccess,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL);
|
|
|
|
ZwClose(hKey);
|
|
}
|
|
|
|
PiUnlockPnpRegistry();
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDeviceInterfaceKeysFromSymbolicLink(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE DeviceInterfaceClassKey OPTIONAL,
|
|
OUT PHANDLE DeviceInterfaceKey OPTIONAL,
|
|
OUT PHANDLE DeviceInterfaceInstanceKey OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will open the registry key where the data associated with the
|
|
device pointed to by SymbolicLinkName is stored. If the path does not exist
|
|
it will not be created.
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to the symbolic link name.
|
|
|
|
DesiredAccess - Supplies the access privto the function class instance key
|
|
the caller wants.
|
|
|
|
DeviceInterfaceClassKey - Optionally, supplies the address of a variable
|
|
that receives a handle to the device class key for the interface.
|
|
|
|
DeviceInterfaceKey - Optionally, supplies the address of a variable that
|
|
receives a handle to the device interface (parent) key.
|
|
|
|
DeviceInterfaceInstanceKey - Optionally, Supplies the address of a variable
|
|
that receives a handle to the device interface instance key (i.e., the
|
|
refstring-specific one).
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING guidString, tempString;
|
|
HANDLE hDeviceClasses, hFunctionClass;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check that the supplied symbolic link can be parsed to extract the device
|
|
// class guid string - note that this is also a way of verifying that the
|
|
// SymbolicLinkName string is valid.
|
|
//
|
|
status = IopParseSymbolicLinkName(SymbolicLinkName,
|
|
NULL,
|
|
NULL,
|
|
&guidString,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if(!NT_SUCCESS(status)){
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry
|
|
// manipulation portion solves this problem
|
|
//
|
|
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Open HKLM\System\CurrentControlSet\Control\DeviceClasses key
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_FULL_PATH_DEVICE_CLASSES);
|
|
status = IopOpenRegistryKeyEx( &hDeviceClasses,
|
|
NULL,
|
|
&tempString,
|
|
KEY_READ
|
|
);
|
|
|
|
if( !NT_SUCCESS(status) ){
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Open function class GUID key
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &hFunctionClass,
|
|
hDeviceClasses,
|
|
&guidString,
|
|
KEY_READ
|
|
);
|
|
|
|
if( !NT_SUCCESS(status) ){
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Open device interface instance key
|
|
//
|
|
status = IopOpenOrCreateDeviceInterfaceSubKeys(DeviceInterfaceKey,
|
|
NULL,
|
|
DeviceInterfaceInstanceKey,
|
|
NULL,
|
|
hFunctionClass,
|
|
SymbolicLinkName,
|
|
DesiredAccess,
|
|
FALSE
|
|
);
|
|
|
|
if((!NT_SUCCESS(status)) || (!ARGUMENT_PRESENT(DeviceInterfaceClassKey))) {
|
|
ZwClose(hFunctionClass);
|
|
} else {
|
|
*DeviceInterfaceClassKey = hFunctionClass;
|
|
}
|
|
|
|
clean2:
|
|
ZwClose(hDeviceClasses);
|
|
clean1:
|
|
PiUnlockPnpRegistry();
|
|
clean0:
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IoRegisterDeviceInterface(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN CONST GUID *InterfaceClassGuid,
|
|
IN PUNICODE_STRING ReferenceString OPTIONAL,
|
|
OUT PUNICODE_STRING SymbolicLinkName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This device driver interface allows a WDM driver to register a particular
|
|
interface of its underlying hardware (ie PDO) as a member of a function
|
|
class.
|
|
|
|
Parameters:
|
|
|
|
PhysicalDeviceObject - Supplies a pointer to the PDO for the P&P device
|
|
instance associated with the functionality being registered
|
|
|
|
InterfaceClassGuid - Supplies a pointer to the GUID representring the
|
|
functionality to be registered
|
|
|
|
ReferenceString - Optionally, supplies an additional context string which is
|
|
appended to the enumeration path of the device
|
|
|
|
SymbolicLinkName - Supplies a pointer to a string which on success will
|
|
contain the kernel mode path of the symbolic link used to open this
|
|
device.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_NODE pDeviceNode;
|
|
PUNICODE_STRING pDeviceString;
|
|
NTSTATUS status;
|
|
PWSTR pRefString;
|
|
USHORT count;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Until PartMgr/Disk stop registering non PDOs allow the system to boot.
|
|
//
|
|
// ASSERT_PDO(PhysicalDeviceObject);
|
|
//
|
|
|
|
//
|
|
// Ensure we have a PDO - only PDO's have a device node attached
|
|
//
|
|
|
|
pDeviceNode = PP_DO_TO_DN(PhysicalDeviceObject);
|
|
if (pDeviceNode) {
|
|
|
|
//
|
|
// Get the instance path string
|
|
//
|
|
pDeviceString = &pDeviceNode->InstancePath;
|
|
|
|
if (pDeviceNode->InstancePath.Length == 0) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// Make sure the ReferenceString does not contain any path seperator characters
|
|
//
|
|
if (ReferenceString) {
|
|
pRefString = ReferenceString->Buffer;
|
|
count = ReferenceString->Length / sizeof(WCHAR);
|
|
while (count--) {
|
|
if((*pRefString == SEPERATOR_CHAR) || (*pRefString == ALT_SEPERATOR_CHAR)) {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IopDbgPrint(( IOP_ERROR_LEVEL,
|
|
"IoRegisterDeviceInterface: Invalid RefString!! failed with status = %8.8X\n", status));
|
|
return status;
|
|
}
|
|
pRefString++;
|
|
}
|
|
}
|
|
|
|
return IopRegisterDeviceInterface(pDeviceString,
|
|
InterfaceClassGuid,
|
|
ReferenceString,
|
|
FALSE, // kernel-mode format
|
|
SymbolicLinkName
|
|
);
|
|
} else {
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
IopRegisterDeviceInterface(
|
|
IN PUNICODE_STRING DeviceInstanceName,
|
|
IN CONST GUID *InterfaceClassGuid,
|
|
IN PUNICODE_STRING ReferenceString OPTIONAL,
|
|
IN BOOLEAN UserModeFormat,
|
|
OUT PUNICODE_STRING SymbolicLinkName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the worker routine for IoRegisterDeviceInterface. It is also
|
|
called by the user-mode ConfigMgr (via an NtPlugPlayControl), which is why it
|
|
must take a device instance name instead of a PDO (since the device instance
|
|
may not currently be 'live'), and also why it must optionally return the user-
|
|
mode form of the interface device name (i.e., "\\?\" instead of "\??\").
|
|
|
|
Parameters:
|
|
|
|
DeviceInstanceName - Supplies the name of the device instance for which a
|
|
device interface is being registered.
|
|
|
|
InterfaceClassGuid - Supplies a pointer to the GUID representring the class
|
|
of the device interface being registered.
|
|
|
|
ReferenceString - Optionally, supplies an additional context string which is
|
|
appended to the enumeration path of the device
|
|
|
|
UserModeFormat - If non-zero, then the symbolic link name returned for the
|
|
interface device is in user-mode form (i.e., "\\?\"). If zero (FALSE),
|
|
it is in kernel-mode form (i.e., "\??\").
|
|
|
|
SymbolicLinkName - Supplies a pointer to a string which on success will contain
|
|
either the kernel-mode or user-mode path of the symbolic link used to open
|
|
this device.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING tempString, guidString, otherString;
|
|
PUNICODE_STRING pUserString, pKernelString;
|
|
HANDLE hTemp1, hTemp2, hInterfaceInstanceKey;
|
|
ULONG InterfaceDisposition, InterfaceInstanceDisposition;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Convert the class guid into string form
|
|
//
|
|
|
|
status = RtlStringFromGUID(InterfaceClassGuid, &guidString);
|
|
if( !NT_SUCCESS(status) ){
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Construct both flavors of symbolic link name (go ahead and store the form
|
|
// that the user wants in the SymbolicLinkName parameter they supplied--this
|
|
// saves us from having to copy the appropriate string over to their string
|
|
// later).
|
|
//
|
|
if(UserModeFormat) {
|
|
pUserString = SymbolicLinkName;
|
|
pKernelString = &otherString;
|
|
} else {
|
|
pKernelString = SymbolicLinkName;
|
|
pUserString = &otherString;
|
|
}
|
|
|
|
status = IopBuildSymbolicLinkStrings(DeviceInstanceName,
|
|
&guidString,
|
|
ReferenceString,
|
|
pUserString,
|
|
pKernelString
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry manipulation
|
|
// portion solves this problem
|
|
//
|
|
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Open HKLM\System\CurrentControlSet\Control\DeviceClasses key into hTemp1
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_FULL_PATH_DEVICE_CLASSES);
|
|
status = IopCreateRegistryKeyEx( &hTemp1,
|
|
NULL,
|
|
&tempString,
|
|
KEY_CREATE_SUB_KEY,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
|
|
if( !NT_SUCCESS(status) ){
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Open/create function class GUID key into hTemp2
|
|
//
|
|
|
|
status = IopCreateRegistryKeyEx( &hTemp2,
|
|
hTemp1,
|
|
&guidString,
|
|
KEY_CREATE_SUB_KEY,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
ZwClose(hTemp1);
|
|
|
|
if( !NT_SUCCESS(status) ){
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Now open/create the two-level device interface hierarchy underneath this
|
|
// interface class key.
|
|
//
|
|
status = IopOpenOrCreateDeviceInterfaceSubKeys(&hTemp1,
|
|
&InterfaceDisposition,
|
|
&hInterfaceInstanceKey,
|
|
&InterfaceInstanceDisposition,
|
|
hTemp2,
|
|
pUserString,
|
|
KEY_WRITE | DELETE,
|
|
TRUE
|
|
);
|
|
|
|
ZwClose(hTemp2);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Create the device instance value under the device interface key
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_DEVICE_INSTANCE);
|
|
status = IopSetRegistryStringValue(hTemp1,
|
|
&tempString,
|
|
DeviceInstanceName
|
|
);
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean3;
|
|
}
|
|
|
|
//
|
|
// Create symbolic link value under interface instance subkey
|
|
//
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_SYMBOLIC_LINK);
|
|
status = IopSetRegistryStringValue(hInterfaceInstanceKey,
|
|
&tempString,
|
|
pUserString
|
|
);
|
|
|
|
clean3:
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Since we failed to register the device interface, delete any keys
|
|
// that were newly created in the attempt.
|
|
//
|
|
if(InterfaceInstanceDisposition == REG_CREATED_NEW_KEY) {
|
|
ZwDeleteKey(hInterfaceInstanceKey);
|
|
}
|
|
|
|
if(InterfaceDisposition == REG_CREATED_NEW_KEY) {
|
|
ZwDeleteKey(hTemp1);
|
|
}
|
|
}
|
|
|
|
ZwClose(hInterfaceInstanceKey);
|
|
ZwClose(hTemp1);
|
|
|
|
clean2:
|
|
PiUnlockPnpRegistry();
|
|
RtlFreeUnicodeString(&otherString);
|
|
if (!NT_SUCCESS(status)) {
|
|
RtlFreeUnicodeString(SymbolicLinkName);
|
|
}
|
|
|
|
clean1:
|
|
RtlFreeUnicodeString(&guidString);
|
|
clean0:
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopUnregisterDeviceInterface(
|
|
IN PUNICODE_STRING SymbolicLinkName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the interface instance subkey of
|
|
ReferenceString from the interface for DeviceInstanceName to the
|
|
given InterfaceClassGuid. If the interface instance specified by
|
|
the Reference String portion of SymbolicLinkName is the only
|
|
instance of the interface, the interface subkey is removed from
|
|
the device class key as well.
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to a unicode string which
|
|
contains the symbolic link name of the device to unregister.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
HANDLE hInterfaceClassKey=NULL, hInterfaceKey=NULL,
|
|
hInterfaceInstanceKey=NULL, hControl=NULL;
|
|
UNICODE_STRING tempString, mungedPathString, guidString, refString;
|
|
BOOLEAN refStringPresent;
|
|
GUID guid;
|
|
UNICODE_STRING interfaceKeyName, instanceKeyName;
|
|
ULONG linked, remainingSubKeys;
|
|
USHORT length;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
PKEY_FULL_INFORMATION keyInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check that the supplied symbolic link can be parsed - note that this is
|
|
// also a way of verifying that the SymbolicLinkName string is valid.
|
|
//
|
|
status = IopParseSymbolicLinkName(SymbolicLinkName,
|
|
NULL,
|
|
&mungedPathString,
|
|
&guidString,
|
|
&refString,
|
|
&refStringPresent,
|
|
&guid);
|
|
if (!NT_SUCCESS(status)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Allocate a unicode string for the interface instance key name.
|
|
// (includes the REFSTRING_PREFIX_CHAR, and ReferenceString, if present)
|
|
//
|
|
length = sizeof(WCHAR) + refString.Length;
|
|
status = IopAllocateUnicodeString(&instanceKeyName,
|
|
length);
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Set the MaximumLength of the Buffer, and append the
|
|
// REFSTRING_PREFIX_CHAR to it.
|
|
//
|
|
*instanceKeyName.Buffer = REFSTRING_PREFIX_CHAR;
|
|
instanceKeyName.Length = sizeof(WCHAR);
|
|
instanceKeyName.MaximumLength = length + sizeof(UNICODE_NULL);
|
|
|
|
//
|
|
// Append the ReferenceString to the prefix char, if necessary.
|
|
//
|
|
if (refStringPresent) {
|
|
RtlAppendUnicodeStringToString(&instanceKeyName, &refString);
|
|
}
|
|
|
|
instanceKeyName.Buffer[instanceKeyName.Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
//
|
|
// Allocate a unicode string for the interface key name.
|
|
// (includes KEY_STRING_PREFIX, mungedPathString, separating '#'
|
|
// char, and the guidString)
|
|
//
|
|
length = IopConstStringSize(KEY_STRING_PREFIX) + mungedPathString.Length +
|
|
sizeof(WCHAR) + guidString.Length;
|
|
|
|
status = IopAllocateUnicodeString(&interfaceKeyName,
|
|
length);
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
interfaceKeyName.MaximumLength = length + sizeof(UNICODE_NULL);
|
|
|
|
//
|
|
// Copy the symbolic link name (without refString) to the interfaceKeyNam
|
|
//
|
|
RtlCopyMemory(interfaceKeyName.Buffer, SymbolicLinkName->Buffer, length);
|
|
interfaceKeyName.Length = length;
|
|
interfaceKeyName.Buffer[interfaceKeyName.Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
//
|
|
// Replace the "\??\" or "\\?\" symbolic link name prefix with "##?#"
|
|
//
|
|
RtlCopyMemory(interfaceKeyName.Buffer,
|
|
KEY_STRING_PREFIX,
|
|
IopConstStringSize(KEY_STRING_PREFIX));
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry manipulation
|
|
// portion solves this problem
|
|
//
|
|
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Get class, interface, and instance handles
|
|
//
|
|
status = IopDeviceInterfaceKeysFromSymbolicLink(SymbolicLinkName,
|
|
KEY_ALL_ACCESS,
|
|
&hInterfaceClassKey,
|
|
&hInterfaceKey,
|
|
&hInterfaceInstanceKey
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Determine whether this interface is currently "enabled"
|
|
//
|
|
linked = 0;
|
|
PiWstrToUnicodeString(&tempString, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &hControl,
|
|
hInterfaceInstanceKey,
|
|
&tempString,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Check the "linked" value under the "Control" subkey of this
|
|
// interface instance
|
|
//
|
|
keyValueInformation=NULL;
|
|
status = IopGetRegistryValue(hControl,
|
|
REGSTR_VAL_LINKED,
|
|
&keyValueInformation);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
if (keyValueInformation->Type == REG_DWORD &&
|
|
keyValueInformation->DataLength == sizeof(ULONG)) {
|
|
|
|
linked = *((PULONG) KEY_VALUE_DATA(keyValueInformation));
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
}
|
|
|
|
ZwClose(hControl);
|
|
hControl = NULL;
|
|
}
|
|
|
|
//
|
|
// Ignore any status code returned while attempting to retieve the
|
|
// state of the device. The value of linked will tell us if we
|
|
// need to disable the interface instance first.
|
|
//
|
|
// If no instance "Control" subkey or "linked" value was present
|
|
// (status == STATUS_OBJECT_NAME_NOT_FOUND), this interface instance
|
|
// is not currently enabled -- ok to delete.
|
|
//
|
|
// If the attempt to retrieve these values failed with some other error,
|
|
// any attempt to disable the interface will also likely fail,
|
|
// so we'll just have to delete this instance anyways.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
|
|
if (linked) {
|
|
//
|
|
// Disabled the active interface before unregistering it, ignore any
|
|
// status returned, we'll delete this interface instance key anyways.
|
|
//
|
|
IoSetDeviceInterfaceState(SymbolicLinkName, FALSE);
|
|
}
|
|
|
|
//
|
|
// Recursively delete the interface instance key, if it exists.
|
|
//
|
|
ZwClose(hInterfaceInstanceKey);
|
|
hInterfaceInstanceKey = NULL;
|
|
IopDeleteKeyRecursive (hInterfaceKey, instanceKeyName.Buffer);
|
|
|
|
//
|
|
// Find out how many subkeys to the interface key remain.
|
|
//
|
|
status = IopGetRegistryKeyInformation(hInterfaceKey,
|
|
&keyInformation);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean3;
|
|
}
|
|
|
|
remainingSubKeys = keyInformation->SubKeys;
|
|
|
|
ExFreePool(keyInformation);
|
|
|
|
//
|
|
// See if a volatile "Control" subkey exists under this interface key
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &hControl,
|
|
hInterfaceKey,
|
|
&tempString,
|
|
KEY_READ
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwClose(hControl);
|
|
hControl = NULL;
|
|
}
|
|
if ((remainingSubKeys==0) ||
|
|
((remainingSubKeys==1) && (NT_SUCCESS(status)))) {
|
|
//
|
|
// If the interface key has no subkeys, or the only the remaining subkey
|
|
// is the volatile interface "Control" subkey, then there are no more
|
|
// instances to this interface. We should delete the interface key
|
|
// itself also.
|
|
//
|
|
ZwClose(hInterfaceKey);
|
|
hInterfaceKey = NULL;
|
|
|
|
IopDeleteKeyRecursive (hInterfaceClassKey, interfaceKeyName.Buffer);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
|
|
clean3:
|
|
if (hControl) {
|
|
ZwClose(hControl);
|
|
}
|
|
if (hInterfaceInstanceKey) {
|
|
ZwClose(hInterfaceInstanceKey);
|
|
}
|
|
if (hInterfaceKey) {
|
|
ZwClose(hInterfaceKey);
|
|
}
|
|
if (hInterfaceClassKey) {
|
|
ZwClose(hInterfaceClassKey);
|
|
}
|
|
|
|
clean2:
|
|
PiUnlockPnpRegistry();
|
|
|
|
RtlFreeUnicodeString(&interfaceKeyName);
|
|
|
|
clean1:
|
|
RtlFreeUnicodeString(&instanceKeyName);
|
|
|
|
clean0:
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopRemoveDeviceInterfaces(
|
|
IN PUNICODE_STRING DeviceInstancePath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks all device class keys under
|
|
HKLM\SYSTEM\CCS\Control\DeviceClasses for interfaces for which the
|
|
DeviceInstance value matches the supplied DeviceInstancePath. Instances of
|
|
such device interfaces are unregistered, and the device interface subkey
|
|
itself is removed.
|
|
|
|
Note that a lock on the registry must have already been acquired,
|
|
by the caller of this routine.
|
|
|
|
Parameters:
|
|
|
|
DeviceInterfacePath - Supplies a pointer to a unicode string which
|
|
contains the DeviceInterface name of the device for which
|
|
interfaces to are to be removed.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was
|
|
successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hDeviceClasses=NULL, hClassGUID=NULL, hInterface=NULL;
|
|
UNICODE_STRING tempString, guidString, interfaceString, deviceInstanceString;
|
|
ULONG resultSize, classIndex, interfaceIndex;
|
|
ULONG symbolicLinkListSize;
|
|
PWCHAR symbolicLinkList, symLink;
|
|
BUFFER_INFO classInfoBuffer, interfaceInfoBuffer;
|
|
PKEY_VALUE_FULL_INFORMATION deviceInstanceInfo;
|
|
BOOLEAN deletedInterface;
|
|
GUID classGUID;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate initial buffers
|
|
//
|
|
status = IopAllocateBuffer(&classInfoBuffer,
|
|
INITIAL_INFO_BUFFER_SIZE);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean0;
|
|
}
|
|
|
|
status = IopAllocateBuffer(&interfaceInfoBuffer,
|
|
INITIAL_INFO_BUFFER_SIZE);
|
|
if (!NT_SUCCESS(status)) {
|
|
IopFreeBuffer(&classInfoBuffer);
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Open HKLM\System\CurrentControlSet\Control\DeviceClasses
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_FULL_PATH_DEVICE_CLASSES);
|
|
status = IopOpenRegistryKeyEx( &hDeviceClasses,
|
|
NULL,
|
|
&tempString,
|
|
KEY_READ
|
|
);
|
|
if(!NT_SUCCESS(status)){
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Enumerate all device classes
|
|
//
|
|
classIndex = 0;
|
|
ASSERT(classInfoBuffer.MaxSize >= sizeof(KEY_BASIC_INFORMATION));
|
|
while((status = ZwEnumerateKey(hDeviceClasses,
|
|
classIndex,
|
|
KeyBasicInformation,
|
|
(PVOID) classInfoBuffer.Buffer,
|
|
classInfoBuffer.MaxSize,
|
|
&resultSize
|
|
)) != STATUS_NO_MORE_ENTRIES) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
status = IopResizeBuffer(&classInfoBuffer, resultSize, FALSE);
|
|
continue;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Get the key name for this device class
|
|
//
|
|
guidString.Length = (USHORT)((PKEY_BASIC_INFORMATION)(classInfoBuffer.Buffer))->NameLength;
|
|
guidString.MaximumLength = guidString.Length;
|
|
guidString.Buffer = ((PKEY_BASIC_INFORMATION)(classInfoBuffer.Buffer))->Name;
|
|
|
|
//
|
|
// Open the key for this device class
|
|
//
|
|
status = IopOpenRegistryKeyEx( &hClassGUID,
|
|
hDeviceClasses,
|
|
&guidString,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Couldn't open key for this device class -- skip it and move on.
|
|
//
|
|
goto CloseClassKeyAndContinue;
|
|
}
|
|
|
|
//
|
|
// Enumerate all device interfaces for this device class
|
|
//
|
|
interfaceIndex = 0;
|
|
ASSERT(interfaceInfoBuffer.MaxSize >= sizeof(KEY_BASIC_INFORMATION));
|
|
while((status = ZwEnumerateKey(hClassGUID,
|
|
interfaceIndex,
|
|
KeyBasicInformation,
|
|
(PVOID) interfaceInfoBuffer.Buffer,
|
|
interfaceInfoBuffer.MaxSize,
|
|
&resultSize
|
|
)) != STATUS_NO_MORE_ENTRIES) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
status = IopResizeBuffer(&interfaceInfoBuffer, resultSize, FALSE);
|
|
continue;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// This interface key has not yet been deleted
|
|
//
|
|
deletedInterface = FALSE;
|
|
|
|
//
|
|
// Create a NULL-terminated unicode string for the interface key name
|
|
//
|
|
status = IopAllocateUnicodeString(&interfaceString,
|
|
(USHORT)((PKEY_BASIC_INFORMATION)(interfaceInfoBuffer.Buffer))->NameLength);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
interfaceString.Length = (USHORT)((PKEY_BASIC_INFORMATION)(interfaceInfoBuffer.Buffer))->NameLength;
|
|
interfaceString.MaximumLength = interfaceString.Length + sizeof(UNICODE_NULL);
|
|
RtlCopyMemory(interfaceString.Buffer,
|
|
((PKEY_BASIC_INFORMATION)(interfaceInfoBuffer.Buffer))->Name,
|
|
interfaceString.Length);
|
|
interfaceString.Buffer[interfaceString.Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
//
|
|
// Open the device interface key
|
|
//
|
|
status = IopOpenRegistryKeyEx( &hInterface,
|
|
hClassGUID,
|
|
&interfaceString,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Couldn't open the device interface key -- skip it and move on.
|
|
//
|
|
hInterface = NULL;
|
|
goto CloseInterfaceKeyAndContinue;
|
|
}
|
|
|
|
//
|
|
// Get the DeviceInstance value for this interface key
|
|
//
|
|
status = IopGetRegistryValue(hInterface,
|
|
REGSTR_VAL_DEVICE_INSTANCE,
|
|
&deviceInstanceInfo);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
//
|
|
// Couldn't get the DeviceInstance for this interface --
|
|
// skip it and move on.
|
|
//
|
|
goto CloseInterfaceKeyAndContinue;
|
|
}
|
|
|
|
if((deviceInstanceInfo->Type == REG_SZ) &&
|
|
(deviceInstanceInfo->DataLength != 0)) {
|
|
|
|
IopRegistryDataToUnicodeString(&deviceInstanceString,
|
|
(PWSTR)KEY_VALUE_DATA(deviceInstanceInfo),
|
|
deviceInstanceInfo->DataLength);
|
|
|
|
} else {
|
|
//
|
|
// DeviceInstance value is invalid -- skip it and move on.
|
|
//
|
|
ExFreePool(deviceInstanceInfo);
|
|
goto CloseInterfaceKeyAndContinue;
|
|
|
|
}
|
|
|
|
//
|
|
// Compare the DeviceInstance of this interface to DeviceInstancePath
|
|
//
|
|
if (RtlEqualUnicodeString(&deviceInstanceString, DeviceInstancePath, TRUE)) {
|
|
|
|
ZwClose(hInterface);
|
|
hInterface = NULL;
|
|
|
|
//
|
|
// Retrieve all instances of this device interface
|
|
// (active and non-active)
|
|
//
|
|
RtlGUIDFromString(&guidString, &classGUID);
|
|
|
|
status = IopGetDeviceInterfaces(&classGUID,
|
|
DeviceInstancePath,
|
|
DEVICE_INTERFACE_INCLUDE_NONACTIVE,
|
|
FALSE, // kernel-mode format
|
|
&symbolicLinkList,
|
|
&symbolicLinkListSize);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Iterate through all instances of the interface
|
|
//
|
|
symLink = symbolicLinkList;
|
|
while(*symLink != UNICODE_NULL) {
|
|
|
|
RtlInitUnicodeString(&tempString, symLink);
|
|
|
|
//
|
|
// Unregister this instance of the interface. Since we are
|
|
// removing the device, ignore any returned status, since
|
|
// there isn't anything we can do about interfaces which
|
|
// fail unregistration.
|
|
//
|
|
IopUnregisterDeviceInterface(&tempString);
|
|
|
|
symLink += ((tempString.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR));
|
|
}
|
|
ExFreePool(symbolicLinkList);
|
|
}
|
|
|
|
//
|
|
// Recursively delete the interface key, if it still exists.
|
|
// While IopUnregisterDeviceInterface will itself delete the
|
|
// interface key if no interface instance subkeys remain, if any
|
|
// of the above calls to IopUnregisterDeviceInterface failed to
|
|
// delete an interface instance key, subkeys will remain, and
|
|
// the interface key will not have been deleted. We'll catch
|
|
// that here.
|
|
//
|
|
status = IopOpenRegistryKeyEx( &hInterface,
|
|
hClassGUID,
|
|
&interfaceString,
|
|
KEY_READ
|
|
);
|
|
if(NT_SUCCESS(status)){
|
|
if (NT_SUCCESS(IopDeleteKeyRecursive(hClassGUID,
|
|
interfaceString.Buffer))) {
|
|
deletedInterface = TRUE;
|
|
}
|
|
ZwDeleteKey(hInterface);
|
|
ZwClose(hInterface);
|
|
hInterface = NULL;
|
|
} else if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
//
|
|
// Interface was already deleted by IopUnregisterDeviceInterface
|
|
//
|
|
deletedInterface = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free allocated key info structure
|
|
//
|
|
ExFreePool(deviceInstanceInfo);
|
|
|
|
CloseInterfaceKeyAndContinue:
|
|
|
|
if (hInterface != NULL) {
|
|
ZwClose(hInterface);
|
|
hInterface = NULL;
|
|
}
|
|
|
|
RtlFreeUnicodeString(&interfaceString);
|
|
|
|
//
|
|
// Only increment the enumeration index for non-deleted keys
|
|
//
|
|
if (!deletedInterface) {
|
|
interfaceIndex++;
|
|
}
|
|
|
|
}
|
|
|
|
CloseClassKeyAndContinue:
|
|
|
|
if (hClassGUID != NULL) {
|
|
ZwClose(hClassGUID);
|
|
hClassGUID = NULL;
|
|
}
|
|
classIndex++;
|
|
}
|
|
|
|
clean1:
|
|
if (hInterface) {
|
|
ZwClose(hInterface);
|
|
}
|
|
if (hClassGUID) {
|
|
ZwClose(hClassGUID);
|
|
}
|
|
if (hDeviceClasses) {
|
|
ZwClose(hDeviceClasses);
|
|
}
|
|
|
|
IopFreeBuffer(&interfaceInfoBuffer);
|
|
IopFreeBuffer(&classInfoBuffer);
|
|
|
|
clean0:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IopDisableDeviceInterfaces(
|
|
IN PUNICODE_STRING DeviceInstancePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine disables all enabled device interfaces for a given device
|
|
instance. This is typically done after a device has been removed, in case
|
|
the driver did not disable the interfaces for that device, as it should
|
|
have.
|
|
|
|
Note that this routine acquires a lock on the registry.
|
|
|
|
Parameters:
|
|
|
|
DeviceInterfacePath - Supplies a pointer to a unicode string which contains
|
|
the DeviceInterface name of the device for which
|
|
interfaces to are to be disabled.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
UNICODE_STRING tempString, guidString;
|
|
HANDLE hDeviceClasses = NULL;
|
|
ULONG classIndex, resultSize;
|
|
BUFFER_INFO classInfoBuffer;
|
|
GUID classGuid;
|
|
PWCHAR symbolicLinkList, symLink;
|
|
ULONG symbolicLinkListSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate initial buffer to hold device class GUID subkeys.
|
|
//
|
|
status = IopAllocateBuffer(&classInfoBuffer,
|
|
sizeof(KEY_BASIC_INFORMATION) +
|
|
GUID_STRING_SIZE + sizeof(UNICODE_NULL));
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry manipulation
|
|
// portion solves this problem
|
|
//
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Open HKLM\System\CurrentControlSet\Control\DeviceClasses
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_FULL_PATH_DEVICE_CLASSES);
|
|
status = IopOpenRegistryKeyEx(&hDeviceClasses,
|
|
NULL,
|
|
&tempString,
|
|
KEY_READ
|
|
);
|
|
if (!NT_SUCCESS(status)){
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Enumerate all device classes
|
|
//
|
|
classIndex = 0;
|
|
ASSERT(classInfoBuffer.MaxSize >= sizeof(KEY_BASIC_INFORMATION));
|
|
while((status = ZwEnumerateKey(hDeviceClasses,
|
|
classIndex,
|
|
KeyBasicInformation,
|
|
(PVOID)classInfoBuffer.Buffer,
|
|
classInfoBuffer.MaxSize,
|
|
&resultSize
|
|
)) != STATUS_NO_MORE_ENTRIES) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portions of the structure.
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
status = IopResizeBuffer(&classInfoBuffer, resultSize, FALSE);
|
|
continue;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
ZwClose(hDeviceClasses);
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Get the key name for this device class
|
|
//
|
|
guidString.Length = (USHORT)((PKEY_BASIC_INFORMATION)(classInfoBuffer.Buffer))->NameLength;
|
|
guidString.MaximumLength = guidString.Length;
|
|
guidString.Buffer = ((PKEY_BASIC_INFORMATION)(classInfoBuffer.Buffer))->Name;
|
|
|
|
//
|
|
// Retrieve all enabled device interfaces for this device class that are
|
|
// exposed by the given device instance.
|
|
//
|
|
RtlGUIDFromString(&guidString, &classGuid);
|
|
|
|
status = IopGetDeviceInterfaces(&classGuid,
|
|
DeviceInstancePath,
|
|
0, // active interfaces only
|
|
FALSE, // kernel-mode format
|
|
&symbolicLinkList,
|
|
&symbolicLinkListSize);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Iterate through all enabled instances of this device interface
|
|
// members of this device interface class, exposed by the given
|
|
// device instance.
|
|
//
|
|
symLink = symbolicLinkList;
|
|
while(*symLink != UNICODE_NULL) {
|
|
|
|
RtlInitUnicodeString(&tempString, symLink);
|
|
|
|
IopDbgPrint((IOP_WARNING_LEVEL,
|
|
"IopDisableDeviceInterfaces: auto-disabling interface %Z for device instance %Z\n",
|
|
tempString,
|
|
DeviceInstancePath));
|
|
|
|
//
|
|
// Disable this device interface.
|
|
//
|
|
IoSetDeviceInterfaceState(&tempString, FALSE);
|
|
|
|
symLink += ((tempString.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR));
|
|
}
|
|
ExFreePool(symbolicLinkList);
|
|
}
|
|
classIndex++;
|
|
}
|
|
|
|
ZwClose(hDeviceClasses);
|
|
|
|
clean0:
|
|
|
|
IopFreeBuffer(&classInfoBuffer);
|
|
|
|
PiUnlockPnpRegistry();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IopOpenOrCreateDeviceInterfaceSubKeys(
|
|
OUT PHANDLE InterfaceKeyHandle OPTIONAL,
|
|
OUT PULONG InterfaceKeyDisposition OPTIONAL,
|
|
OUT PHANDLE InterfaceInstanceKeyHandle OPTIONAL,
|
|
OUT PULONG InterfaceInstanceDisposition OPTIONAL,
|
|
IN HANDLE InterfaceClassKeyHandle,
|
|
IN PUNICODE_STRING DeviceInterfaceName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN BOOLEAN Create
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API opens or creates a two-level registry hierarchy underneath the
|
|
specified interface class key for a particular device interface. The first
|
|
level is the (munged) symbolic link name (sans RefString). The second level
|
|
is the refstring, prepended with a '#' sign (if the device interface has no
|
|
refstring, then this key name is simply '#').
|
|
|
|
Parameters:
|
|
|
|
InterfaceKeyHandle - Optionally, supplies the address of a variable that
|
|
receives a handle to the interface key (1st level in the hierarchy).
|
|
|
|
InterfaceKeyDisposition - Optionally, supplies the address of a variable that
|
|
receives either REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY indicating
|
|
whether the interface key was newly-created.
|
|
|
|
InterfaceInstanceKeyHandle - Optionally, supplies the address of a variable
|
|
that receives a handle to the interface instance key (2nd level in the
|
|
hierarchy).
|
|
|
|
InterfaceInstanceDisposition - Optionally, supplies the address of a variable
|
|
that receives either REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY
|
|
indicating whether the interface instance key was newly-created.
|
|
|
|
InterfaceClassKeyHandle - Supplies a handle to the interface class key under
|
|
which the device interface keys are to be opened/created.
|
|
|
|
DeviceInterfaceName - Supplies the (user-mode or kernel-mode form) device
|
|
interface name.
|
|
|
|
DesiredAccess - Specifies the desired access that the caller needs to the keys.
|
|
|
|
Create - Determines if the keys are to be created if they do not exist.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING TempString, RefString;
|
|
WCHAR PoundCharBuffer;
|
|
HANDLE hTempInterface, hTempInterfaceInstance;
|
|
ULONG TempInterfaceDisposition;
|
|
BOOLEAN RefStringPresent=FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make a copy of the device interface name, since we're going to munge it.
|
|
//
|
|
status = IopAllocateUnicodeString(&TempString, DeviceInterfaceName->Length);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean0;
|
|
}
|
|
|
|
RtlCopyUnicodeString(&TempString, DeviceInterfaceName);
|
|
|
|
//
|
|
// Parse the SymbolicLinkName for the refstring component (if there is one).
|
|
// Note that this is also a way of verifying that the string is valid.
|
|
//
|
|
status = IopParseSymbolicLinkName(&TempString,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&RefString,
|
|
&RefStringPresent,
|
|
NULL);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
if(RefStringPresent) {
|
|
//
|
|
// Truncate the device interface name before the refstring separator char.
|
|
//
|
|
RefString.Buffer--;
|
|
RefString.Length += sizeof(WCHAR);
|
|
RefString.MaximumLength += sizeof(WCHAR);
|
|
TempString.MaximumLength = TempString.Length = (USHORT)((PUCHAR)RefString.Buffer - (PUCHAR)TempString.Buffer);
|
|
} else {
|
|
//
|
|
// Set up refstring to point to a temporary character buffer that will hold
|
|
// the single '#' used for the key name when no refstring is present.
|
|
//
|
|
RefString.Buffer = &PoundCharBuffer;
|
|
RefString.Length = RefString.MaximumLength = sizeof(PoundCharBuffer);
|
|
}
|
|
|
|
//
|
|
// Replace the "\??\" or "\\?\" symbolic link name prefix with ##?#
|
|
//
|
|
RtlCopyMemory(TempString.Buffer, KEY_STRING_PREFIX, IopConstStringSize(KEY_STRING_PREFIX));
|
|
|
|
//
|
|
// Munge the string
|
|
//
|
|
IopReplaceSeperatorWithPound(&TempString, &TempString);
|
|
|
|
//
|
|
// Now open/create this subkey under the interface class key.
|
|
//
|
|
|
|
if (Create) {
|
|
status = IopCreateRegistryKeyEx( &hTempInterface,
|
|
InterfaceClassKeyHandle,
|
|
&TempString,
|
|
DesiredAccess,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&TempInterfaceDisposition
|
|
);
|
|
} else {
|
|
status = IopOpenRegistryKeyEx( &hTempInterface,
|
|
InterfaceClassKeyHandle,
|
|
&TempString,
|
|
DesiredAccess
|
|
);
|
|
|
|
TempInterfaceDisposition = REG_OPENED_EXISTING_KEY;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Store a '#' as the first character of the RefString, and then we're ready to open the
|
|
// refstring subkey.
|
|
//
|
|
*RefString.Buffer = REFSTRING_PREFIX_CHAR;
|
|
|
|
//
|
|
// Now open/create the subkey under the interface key representing this interface instance
|
|
// (i.e., differentiated by refstring).
|
|
//
|
|
|
|
if (Create) {
|
|
status = IopCreateRegistryKeyEx( &hTempInterfaceInstance,
|
|
hTempInterface,
|
|
&RefString,
|
|
DesiredAccess,
|
|
REG_OPTION_NON_VOLATILE,
|
|
InterfaceInstanceDisposition
|
|
);
|
|
} else {
|
|
status = IopOpenRegistryKeyEx( &hTempInterfaceInstance,
|
|
hTempInterface,
|
|
&RefString,
|
|
DesiredAccess
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Store any requested return values in the caller-supplied buffers.
|
|
//
|
|
if (InterfaceKeyHandle) {
|
|
*InterfaceKeyHandle = hTempInterface;
|
|
} else {
|
|
ZwClose(hTempInterface);
|
|
}
|
|
if (InterfaceKeyDisposition) {
|
|
*InterfaceKeyDisposition = TempInterfaceDisposition;
|
|
}
|
|
if (InterfaceInstanceKeyHandle) {
|
|
*InterfaceInstanceKeyHandle = hTempInterfaceInstance;
|
|
} else {
|
|
ZwClose(hTempInterfaceInstance);
|
|
}
|
|
//
|
|
// (no need to set InterfaceInstanceDisposition--we already set it above)
|
|
//
|
|
} else {
|
|
//
|
|
// If the interface key was newly-created above, then delete it.
|
|
//
|
|
if (TempInterfaceDisposition == REG_CREATED_NEW_KEY) {
|
|
ZwDeleteKey(hTempInterface);
|
|
}
|
|
ZwClose(hTempInterface);
|
|
}
|
|
|
|
clean1:
|
|
RtlFreeUnicodeString(&TempString);
|
|
|
|
clean0:
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IoGetDeviceInterfaceAlias(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN CONST GUID *AliasInterfaceClassGuid,
|
|
OUT PUNICODE_STRING AliasSymbolicLinkName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API returns a symbolic link name (i.e., device interface) of a
|
|
particular interface class that 'aliases' the specified device interface.
|
|
Two device interfaces are considered aliases of each other if the
|
|
following two criteria are met:
|
|
|
|
1. Both interfaces are exposed by the same PDO (devnode).
|
|
2. Both interfaces share the same RefString.
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies the name of the device interface whose alias is
|
|
to be retrieved.
|
|
|
|
AliasInterfaceClassGuid - Supplies a pointer to the GUID representing the interface
|
|
class for which an alias is to be retrieved.
|
|
|
|
AliasSymbolicLinkName - Supplies a pointer to a string which, upon success,
|
|
will contain the name of the device interface in the specified class that
|
|
aliases the SymbolicLinkName interface. (This symbolic link name will be
|
|
returned in either kernel-mode or user-mode form, depeding upon the form
|
|
of the SymbolicLinkName path).
|
|
|
|
It is the caller's responsibility to free the buffer allocated for this
|
|
string via ExFreePool().
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hKey;
|
|
PKEY_VALUE_FULL_INFORMATION pDeviceInstanceInfo;
|
|
UNICODE_STRING deviceInstanceString, refString, guidString, otherString;
|
|
PUNICODE_STRING pUserString, pKernelString;
|
|
BOOLEAN refStringPresent, userModeFormat;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure we have a SymbolicLinkName to parse.
|
|
//
|
|
|
|
if ((!ARGUMENT_PRESENT(SymbolicLinkName)) ||
|
|
(SymbolicLinkName->Buffer == NULL)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// check that the input buffer really is big enough
|
|
//
|
|
|
|
ASSERT(IopConstStringSize(USER_SYMLINK_STRING_PREFIX) == IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX));
|
|
|
|
if (SymbolicLinkName->Length < (IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX)+GUID_STRING_SIZE+1)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Convert the class guid into string form
|
|
//
|
|
|
|
status = RtlStringFromGUID(AliasInterfaceClassGuid, &guidString);
|
|
if( !NT_SUCCESS(status) ){
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry manipulation
|
|
// portion solves this problem
|
|
//
|
|
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Open the (parent) device interface key--not the refstring-specific one.
|
|
//
|
|
|
|
status = IopDeviceInterfaceKeysFromSymbolicLink(SymbolicLinkName,
|
|
KEY_READ,
|
|
NULL,
|
|
&hKey,
|
|
NULL
|
|
);
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Get the name of the device instance that 'owns' this interface.
|
|
//
|
|
|
|
status = IopGetRegistryValue(hKey, REGSTR_VAL_DEVICE_INSTANCE, &pDeviceInstanceInfo);
|
|
|
|
ZwClose(hKey);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
if(pDeviceInstanceInfo->Type == REG_SZ) {
|
|
|
|
IopRegistryDataToUnicodeString(&deviceInstanceString,
|
|
(PWSTR)KEY_VALUE_DATA(pDeviceInstanceInfo),
|
|
pDeviceInstanceInfo->DataLength
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_PARAMETER_1;
|
|
goto clean2;
|
|
|
|
}
|
|
|
|
//
|
|
// Now parse out the refstring, so that we can construct the name of the interface device's
|
|
// alias. (NOTE: we have not yet verified that the alias actually exists, we're only
|
|
// constructing what its name would be, if it did exist.)
|
|
//
|
|
// Don't bother to check the return code. If this were a bad string, we'd have already
|
|
// failed above when we called IopDeviceInterfaceKeysFromSymbolicLink (since it also
|
|
// calls IopParseSymbolicLinkName internally.)
|
|
//
|
|
status = IopParseSymbolicLinkName(SymbolicLinkName,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&refString,
|
|
&refStringPresent,
|
|
NULL);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// Did the caller supply us with a user-mode or kernel-mode format path?
|
|
//
|
|
userModeFormat = (BOOLEAN)(IopConstStringSize(USER_SYMLINK_STRING_PREFIX) ==
|
|
RtlCompareMemory(SymbolicLinkName->Buffer,
|
|
USER_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(USER_SYMLINK_STRING_PREFIX)
|
|
));
|
|
|
|
if(userModeFormat) {
|
|
pUserString = AliasSymbolicLinkName;
|
|
pKernelString = &otherString;
|
|
} else {
|
|
pKernelString = AliasSymbolicLinkName;
|
|
pUserString = &otherString;
|
|
}
|
|
|
|
status = IopBuildSymbolicLinkStrings(&deviceInstanceString,
|
|
&guidString,
|
|
refStringPresent ? &refString : NULL,
|
|
pUserString,
|
|
pKernelString
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// OK, we now have the symbolic link name of the alias, but we don't yet
|
|
// know whether it actually exists. Check this by attempting to open the
|
|
// associated registry key.
|
|
//
|
|
status = IopDeviceInterfaceKeysFromSymbolicLink(AliasSymbolicLinkName,
|
|
KEY_READ,
|
|
NULL,
|
|
NULL,
|
|
&hKey
|
|
);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
//
|
|
// Alias exists--close the key handle.
|
|
//
|
|
ZwClose(hKey);
|
|
} else {
|
|
RtlFreeUnicodeString(AliasSymbolicLinkName);
|
|
}
|
|
|
|
RtlFreeUnicodeString(&otherString);
|
|
|
|
clean2:
|
|
ExFreePool(pDeviceInstanceInfo);
|
|
|
|
clean1:
|
|
PiUnlockPnpRegistry();
|
|
RtlFreeUnicodeString(&guidString);
|
|
|
|
clean0:
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopBuildSymbolicLinkStrings(
|
|
IN PUNICODE_STRING DeviceString,
|
|
IN PUNICODE_STRING GuidString,
|
|
IN PUNICODE_STRING ReferenceString OPTIONAL,
|
|
OUT PUNICODE_STRING UserString,
|
|
OUT PUNICODE_STRING KernelString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will construct various strings used in the registration of
|
|
function device class associations (IoRegisterDeviceClassAssociation).
|
|
The specific strings are detailed below
|
|
|
|
Parameters:
|
|
|
|
DeviceString - Supplies a pointer to the instance path of the device.
|
|
It is of the form <Enumerator>\<Device>\<Instance>.
|
|
|
|
GuidString - Supplies a pointer to the string representation of the
|
|
function class guid.
|
|
|
|
ReferenceString - Supplies a pointer to the reference string for the given
|
|
device to exhibit the given function. This is optional
|
|
|
|
UserString - Supplies a pointer to an uninitialised string which on success
|
|
will contain the string to be assigned to the "SymbolicLink" value under the
|
|
KeyString. It is of the format \\?\<MungedDeviceString>\<GuidString>\<Reference>
|
|
When no longer required it should be freed using RtlFreeUnicodeString.
|
|
|
|
KernelString - Supplies a pointer to an uninitialised string which on success
|
|
will contain the kernel mode path of the device and is of the format
|
|
\??\<MungedDeviceString>\<GuidString>\<Reference>. When no longer required it
|
|
should be freed using RtlFreeUnicodeString.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
USHORT length;
|
|
UNICODE_STRING mungedDeviceString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// The code is optimised to use the fact that \\.\ and \??\ are the same size - if
|
|
// these prefixes change then we need to change the code.
|
|
//
|
|
|
|
ASSERT(IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX) == IopConstStringSize(USER_SYMLINK_STRING_PREFIX));
|
|
|
|
//
|
|
// Calculate the lengths of the strings
|
|
//
|
|
|
|
length = IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX) + DeviceString->Length +
|
|
IopConstStringSize(REPLACED_SEPERATOR_STRING) + GuidString->Length;
|
|
|
|
if(ARGUMENT_PRESENT(ReferenceString) && (ReferenceString->Length != 0)) {
|
|
length += IopConstStringSize(SEPERATOR_STRING) + ReferenceString->Length;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the strings
|
|
//
|
|
|
|
status = IopAllocateUnicodeString(KernelString, length);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean0;
|
|
}
|
|
|
|
status = IopAllocateUnicodeString(UserString, length);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Allocate a temporary string to hold the munged device string
|
|
//
|
|
|
|
status = IopAllocateUnicodeString(&mungedDeviceString, DeviceString->Length);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Copy and munge the device string
|
|
//
|
|
|
|
status = IopReplaceSeperatorWithPound(&mungedDeviceString, DeviceString);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean3;
|
|
}
|
|
|
|
//
|
|
// Construct the user mode string
|
|
//
|
|
|
|
RtlAppendUnicodeToString(UserString, USER_SYMLINK_STRING_PREFIX);
|
|
RtlAppendUnicodeStringToString(UserString, &mungedDeviceString);
|
|
RtlAppendUnicodeToString(UserString, REPLACED_SEPERATOR_STRING);
|
|
RtlAppendUnicodeStringToString(UserString, GuidString);
|
|
|
|
if (ARGUMENT_PRESENT(ReferenceString) && (ReferenceString->Length != 0)) {
|
|
RtlAppendUnicodeToString(UserString, SEPERATOR_STRING);
|
|
RtlAppendUnicodeStringToString(UserString, ReferenceString);
|
|
}
|
|
|
|
ASSERT( UserString->Length == length );
|
|
|
|
//
|
|
// Construct the kernel mode string by replacing the prefix on the value
|
|
// string.
|
|
//
|
|
RtlCopyUnicodeString(KernelString, UserString);
|
|
RtlCopyMemory(
|
|
KernelString->Buffer,
|
|
KERNEL_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX));
|
|
|
|
clean3:
|
|
RtlFreeUnicodeString(&mungedDeviceString);
|
|
|
|
clean2:
|
|
if (!NT_SUCCESS(status)) {
|
|
RtlFreeUnicodeString(UserString);
|
|
}
|
|
|
|
clean1:
|
|
if (!NT_SUCCESS(status)) {
|
|
RtlFreeUnicodeString(KernelString);
|
|
}
|
|
|
|
clean0:
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopReplaceSeperatorWithPound(
|
|
OUT PUNICODE_STRING OutString,
|
|
IN PUNICODE_STRING InString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will copy a string from InString to OutString replacing any
|
|
occurence of '\' or '/' with '#' as it goes.
|
|
|
|
Parameters:
|
|
|
|
OutString - Supplies a pointer to a string which has already been
|
|
initialised to have a buffer large enough to accomodate the string. The
|
|
contents of this string will be over written
|
|
|
|
InString - Supplies a pointer to the string to be munged
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
Remarks:
|
|
|
|
In place munging can be performed - ie. the In and Out strings can be the
|
|
same.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR pInPosition, pOutPosition;
|
|
USHORT count;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(InString);
|
|
ASSERT(OutString);
|
|
|
|
//
|
|
// Ensure we have enough space in the output string
|
|
//
|
|
if(InString->Length > OutString->MaximumLength) {
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
pInPosition = InString->Buffer;
|
|
pOutPosition = OutString->Buffer;
|
|
count = CB_TO_CWC(InString->Length);
|
|
|
|
//
|
|
// Traverse the in string copying and replacing all occurences of '\' or '/'
|
|
// with '#'
|
|
//
|
|
while (count--) {
|
|
|
|
if( (*pInPosition == SEPERATOR_CHAR) ||
|
|
(*pInPosition == ALT_SEPERATOR_CHAR)) {
|
|
|
|
*pOutPosition = REPLACED_SEPERATOR_CHAR;
|
|
} else {
|
|
|
|
*pOutPosition = *pInPosition;
|
|
}
|
|
pInPosition++;
|
|
pOutPosition++;
|
|
}
|
|
|
|
OutString->Length = InString->Length;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDropReferenceString(
|
|
OUT PUNICODE_STRING OutString,
|
|
IN PUNICODE_STRING InString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the reference string from a symbolic link name. No
|
|
space is allocated for the out string so no attempt should be made to free
|
|
the buffer of OutString.
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to a symbolic link name string.
|
|
Both the prefixed strings are valid.
|
|
|
|
GuidReferenceString - Supplies a pointer to an uninitialised string which on
|
|
success will contain the symbolic link name without the reference string.
|
|
See the note on storage allocation above.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
Remarks:
|
|
|
|
The string returned in OutString is dependant on the buffer of
|
|
InString and is only valid as long as InString is valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING refString;
|
|
NTSTATUS status;
|
|
BOOLEAN refStringPresent;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(InString);
|
|
ASSERT(OutString);
|
|
|
|
//
|
|
// Parse the SymbolicLinkName for the refstring component (if there is one).
|
|
// Note that this is also a way of verifying that the string is valid.
|
|
//
|
|
status = IopParseSymbolicLinkName(
|
|
InString,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&refString,
|
|
&refStringPresent,
|
|
NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// The refstring is always at the end, so just use the same buffer and
|
|
// set the length of the output string accordingly.
|
|
//
|
|
OutString->Buffer = InString->Buffer;
|
|
|
|
//
|
|
// If we have a refstring then subtract it's length
|
|
//
|
|
OutString->Length = InString->Length;
|
|
if (refStringPresent) {
|
|
|
|
OutString->Length -= refString.Length + sizeof(WCHAR);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Invalidate the returned string
|
|
//
|
|
OutString->Buffer = NULL;
|
|
OutString->Length = 0;
|
|
}
|
|
|
|
OutString->MaximumLength = OutString->Length;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopBuildGlobalSymbolicLinkString(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
OUT PUNICODE_STRING GlobalString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will construct the global symbolic link name for the given
|
|
kernel-mode or user-mode relative symbolic link name.
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to a symbolic link name string.
|
|
Both the kernel-mode and user-mode prefixed strings are valid.
|
|
|
|
GlobalString - Supplies a pointer to an uninitialised string which on
|
|
success will contain the string that represents the symbolic link
|
|
withing the global namespace. It is of the format
|
|
\GLOBAL??\<MungedDeviceString>\<GuidString>\<Reference>. When no longer
|
|
required it should be freed using RtlFreeUnicodeString.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
USHORT length;
|
|
UNICODE_STRING tempString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// The code is optimised to use the fact that \\.\ and \??\ are the same
|
|
// size, and that since we are replacing the prefix, the routine can take
|
|
// either one. If these prefixes change then we need to change the code.
|
|
//
|
|
|
|
ASSERT(IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX) == IopConstStringSize(USER_SYMLINK_STRING_PREFIX));
|
|
|
|
//
|
|
// Make sure the supplied SymbolicLinkName string begins with either the
|
|
// kernel or user symbolic link prefix. If it does not have a \\?\ or \??\
|
|
// prefix then fail.
|
|
//
|
|
|
|
if ((RtlCompareMemory(SymbolicLinkName->Buffer,
|
|
USER_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(USER_SYMLINK_STRING_PREFIX))
|
|
!= IopConstStringSize(USER_SYMLINK_STRING_PREFIX)) &&
|
|
(RtlCompareMemory(SymbolicLinkName->Buffer,
|
|
KERNEL_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX))
|
|
!= IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX))) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Compute the length of the global symbolic link string.
|
|
//
|
|
|
|
length = SymbolicLinkName->Length - IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX) +
|
|
IopConstStringSize(GLOBAL_SYMLINK_STRING_PREFIX);
|
|
|
|
//
|
|
// Allocate space for the strings.
|
|
//
|
|
|
|
status = IopAllocateUnicodeString(GlobalString, length);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Copy the \GLOBAL?? symbolic link name prefix to the string.
|
|
//
|
|
|
|
status = RtlAppendUnicodeToString(GlobalString,
|
|
GLOBAL_SYMLINK_STRING_PREFIX);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
RtlFreeUnicodeString(GlobalString);
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Append the part of the SymbolicLinkName that follows the prefix.
|
|
//
|
|
|
|
tempString.Buffer = SymbolicLinkName->Buffer +
|
|
IopConstStringLength(KERNEL_SYMLINK_STRING_PREFIX);
|
|
tempString.Length = SymbolicLinkName->Length -
|
|
IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX);
|
|
tempString.MaximumLength = SymbolicLinkName->MaximumLength -
|
|
IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX);
|
|
|
|
status = RtlAppendUnicodeStringToString(GlobalString, &tempString);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
RtlFreeUnicodeString(GlobalString);
|
|
goto clean0;
|
|
}
|
|
|
|
ASSERT(GlobalString->Length == length);
|
|
|
|
clean0:
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopParseSymbolicLinkName(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
OUT PUNICODE_STRING PrefixString OPTIONAL,
|
|
OUT PUNICODE_STRING MungedPathString OPTIONAL,
|
|
OUT PUNICODE_STRING GuidString OPTIONAL,
|
|
OUT PUNICODE_STRING RefString OPTIONAL,
|
|
OUT PBOOLEAN RefStringPresent OPTIONAL,
|
|
OUT LPGUID Guid OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine breaks apart a symbolic link name constructed by
|
|
IopBuildSymbolicLinkNames. Both formats of name are valid - user
|
|
mode \\?\ and kernel mode \??\.
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to the symbolic link name to
|
|
be analysed.
|
|
|
|
PrefixString - Optionally contains a pointer to a string which will contain
|
|
the prefix of the string.
|
|
|
|
MungedPathString - Optionally contains a pointer to a string which will contain
|
|
the enumeration path of the device with all occurences of '\' replaced with '#'.
|
|
|
|
GuidString - Optionally contains a pointer to a string which will contain
|
|
the device class guid in string format from the string.
|
|
|
|
RefString - Optionally contains a pointer to a string which will contain
|
|
the refstring of the string if one is present, otherwise it is undefined.
|
|
|
|
RefStringPresent - Optionally contains a pointer to a boolean value which will
|
|
be set to true if a refstring is present.
|
|
|
|
Guid - Optionally contains a pointer to a guid which will contain
|
|
the function class guid of the string.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PWSTR pCurrent;
|
|
USHORT current, path, guid, reference = 0;
|
|
UNICODE_STRING tempString;
|
|
GUID tempGuid;
|
|
BOOLEAN haveRefString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure we have a SymbolicLinkName to parse.
|
|
//
|
|
if ( !ARGUMENT_PRESENT(SymbolicLinkName) ||
|
|
SymbolicLinkName->Buffer == NULL ||
|
|
SymbolicLinkName->Length == 0) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// check that the input buffer really is big enough
|
|
//
|
|
ASSERT(IopConstStringSize(USER_SYMLINK_STRING_PREFIX) == IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX));
|
|
|
|
if (SymbolicLinkName->Length < (IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX) + GUID_STRING_SIZE + 1)) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Sanity check on the incoming string - if it does not have a \\?\ or \??\ prefix then fail
|
|
//
|
|
if ((RtlCompareMemory(SymbolicLinkName->Buffer,
|
|
USER_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(USER_SYMLINK_STRING_PREFIX))
|
|
!= IopConstStringSize(USER_SYMLINK_STRING_PREFIX)) &&
|
|
(RtlCompareMemory(SymbolicLinkName->Buffer,
|
|
KERNEL_SYMLINK_STRING_PREFIX,
|
|
IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX))
|
|
!= IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX))) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Break apart the string into it's constituent parts
|
|
//
|
|
path = IopConstStringSize(USER_SYMLINK_STRING_PREFIX) + 1;
|
|
|
|
//
|
|
// Find the '\' seperator
|
|
//
|
|
pCurrent = SymbolicLinkName->Buffer + IopConstStringLength(KERNEL_SYMLINK_STRING_PREFIX);
|
|
|
|
for ( current = 0;
|
|
current < (SymbolicLinkName->Length / sizeof(WCHAR)) - IopConstStringLength(KERNEL_SYMLINK_STRING_PREFIX);
|
|
current++, pCurrent++) {
|
|
|
|
if(*pCurrent == SEPERATOR_CHAR) {
|
|
|
|
reference = current + 1 + IopConstStringLength(KERNEL_SYMLINK_STRING_PREFIX);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we don't have a reference string fake it to where it would have been
|
|
//
|
|
if (reference == 0) {
|
|
|
|
haveRefString = FALSE;
|
|
reference = SymbolicLinkName->Length / sizeof(WCHAR) + 1;
|
|
} else {
|
|
|
|
haveRefString = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check the guid looks plausable
|
|
//
|
|
tempString.Length = GUID_STRING_SIZE;
|
|
tempString.MaximumLength = GUID_STRING_SIZE;
|
|
tempString.Buffer = SymbolicLinkName->Buffer + reference - GUID_STRING_LENGTH - 1;
|
|
|
|
if (!NT_SUCCESS(RtlGUIDFromString(&tempString, &tempGuid))) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto clean0;
|
|
}
|
|
|
|
guid = reference - GUID_STRING_LENGTH - 1;
|
|
|
|
//
|
|
// Setup return strings
|
|
//
|
|
if (ARGUMENT_PRESENT(PrefixString)) {
|
|
|
|
PrefixString->Length = IopConstStringSize(KERNEL_SYMLINK_STRING_PREFIX);
|
|
PrefixString->MaximumLength = PrefixString->Length;
|
|
PrefixString->Buffer = SymbolicLinkName->Buffer;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(MungedPathString)) {
|
|
|
|
MungedPathString->Length = (reference - 1 - GUID_STRING_LENGTH - 1 -
|
|
IopConstStringLength(KERNEL_SYMLINK_STRING_PREFIX)) *
|
|
sizeof(WCHAR);
|
|
MungedPathString->MaximumLength = MungedPathString->Length;
|
|
MungedPathString->Buffer = SymbolicLinkName->Buffer +
|
|
IopConstStringLength(KERNEL_SYMLINK_STRING_PREFIX);
|
|
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(GuidString)) {
|
|
|
|
GuidString->Length = GUID_STRING_SIZE;
|
|
GuidString->MaximumLength = GuidString->Length;
|
|
GuidString->Buffer = SymbolicLinkName->Buffer + reference -
|
|
GUID_STRING_LENGTH - 1;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(RefString)) {
|
|
|
|
//
|
|
// Check if we have a refstring
|
|
//
|
|
if (haveRefString) {
|
|
|
|
RefString->Length = SymbolicLinkName->Length - (reference * sizeof(WCHAR));
|
|
RefString->MaximumLength = RefString->Length;
|
|
RefString->Buffer = SymbolicLinkName->Buffer + reference;
|
|
} else {
|
|
|
|
RefString->Length = 0;
|
|
RefString->MaximumLength = 0;
|
|
RefString->Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(RefStringPresent)) {
|
|
|
|
*RefStringPresent = haveRefString;
|
|
}
|
|
|
|
if(ARGUMENT_PRESENT(Guid)) {
|
|
|
|
*Guid = tempGuid;
|
|
}
|
|
|
|
clean0:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IopProcessSetInterfaceState(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN BOOLEAN Enable,
|
|
IN BOOLEAN DeferNotStarted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This DDI allows a device class to activate and deactivate an association
|
|
previously registered using IoRegisterDeviceInterface
|
|
|
|
Parameters:
|
|
|
|
SymbolicLinkName - Supplies a pointer to the symbolic link name which was
|
|
returned by IoRegisterDeviceInterface when the interface was registered,
|
|
or as returned by IoGetDeviceInterfaces.
|
|
|
|
Enable - If TRUE (non-zero), the interface will be enabled. If FALSE, it
|
|
will be disabled.
|
|
|
|
DeferNotStarted - If TRUE then enables will be queued if the PDO isn't
|
|
started. It is FALSE when we've started the PDO and are processing the
|
|
queued enables.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hInterfaceClassKey = NULL;
|
|
HANDLE hInterfaceParentKey= NULL, hInterfaceInstanceKey = NULL;
|
|
HANDLE hInterfaceParentControl = NULL, hInterfaceInstanceControl = NULL;
|
|
UNICODE_STRING tempString, deviceNameString;
|
|
UNICODE_STRING actualSymbolicLinkName, globalSymbolicLinkName;
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueInfo;
|
|
ULONG linked, refcount;
|
|
GUID guid;
|
|
PDEVICE_OBJECT physicalDeviceObject;
|
|
PWCHAR deviceNameBuffer = NULL;
|
|
ULONG deviceNameBufferLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check that the supplied symbolic link can be parsed to extract the device
|
|
// class guid - note that this is also a way of verifying that the
|
|
// SymbolicLinkName string is valid.
|
|
//
|
|
|
|
status = IopParseSymbolicLinkName(SymbolicLinkName,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&guid);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Get the symbolic link name without the ref string.
|
|
//
|
|
status = IopDropReferenceString(&actualSymbolicLinkName, SymbolicLinkName);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Symbolic links created for device interfaces should be visible to all
|
|
// users, in all sessions, so we need to contruct an absolute name for
|
|
// symbolic link in the global DosDevices namespace '\GLOBAL??'. This
|
|
// ensures that a global symbolic link will always be created or deleted by
|
|
// IoSetDeviceInterfaceState, no matter what context it is called in.
|
|
//
|
|
status = IopBuildGlobalSymbolicLinkString(&actualSymbolicLinkName,
|
|
&globalSymbolicLinkName);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Get function class instance handle
|
|
//
|
|
status = IopDeviceInterfaceKeysFromSymbolicLink(SymbolicLinkName,
|
|
KEY_READ | KEY_WRITE,
|
|
&hInterfaceClassKey,
|
|
&hInterfaceParentKey,
|
|
&hInterfaceInstanceKey
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Open the parent interface control subkey
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_KEY_CONTROL);
|
|
status = IopCreateRegistryKeyEx( &hInterfaceParentControl,
|
|
hInterfaceParentKey,
|
|
&tempString,
|
|
KEY_READ,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
|
|
//
|
|
// Find out the name of the device instance that 'owns' this interface.
|
|
//
|
|
status = IopGetRegistryValue(hInterfaceParentKey,
|
|
REGSTR_VAL_DEVICE_INSTANCE,
|
|
&pKeyValueInfo
|
|
);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
//
|
|
// Open the device instance control subkey
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_KEY_CONTROL);
|
|
status = IopCreateRegistryKeyEx( &hInterfaceInstanceControl,
|
|
hInterfaceInstanceKey,
|
|
&tempString,
|
|
KEY_READ,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
if(!NT_SUCCESS(status)) {
|
|
ExFreePool(pKeyValueInfo);
|
|
hInterfaceInstanceControl = NULL;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Find the PDO corresponding to this device instance name.
|
|
//
|
|
if (pKeyValueInfo->Type == REG_SZ) {
|
|
|
|
IopRegistryDataToUnicodeString(&tempString,
|
|
(PWSTR)KEY_VALUE_DATA(pKeyValueInfo),
|
|
pKeyValueInfo->DataLength
|
|
);
|
|
|
|
physicalDeviceObject = IopDeviceObjectFromDeviceInstance(&tempString);
|
|
|
|
if (physicalDeviceObject) {
|
|
|
|
//
|
|
// DeferNotStarted is set TRUE if we are being called from
|
|
// IoSetDeviceInterfaceState. It will be set FALSE if we are
|
|
// processing previously queued operations as we are starting the
|
|
// device.
|
|
//
|
|
if (DeferNotStarted) {
|
|
|
|
if (physicalDeviceObject->DeviceObjectExtension->ExtensionFlags & DOE_START_PENDING) {
|
|
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
//
|
|
// The device hasn't been started yet. We need to queue
|
|
// any enables and remove items from the queue on a disable.
|
|
//
|
|
deviceNode = PP_DO_TO_DN(physicalDeviceObject);
|
|
|
|
if (Enable) {
|
|
|
|
status = PiDeferSetInterfaceState(deviceNode,
|
|
SymbolicLinkName
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ExFreePool(pKeyValueInfo);
|
|
|
|
ObDereferenceObject(physicalDeviceObject);
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto clean2;
|
|
}
|
|
|
|
} else {
|
|
|
|
PiRemoveDeferredSetInterfaceState(deviceNode,
|
|
SymbolicLinkName
|
|
);
|
|
|
|
ExFreePool(pKeyValueInfo);
|
|
|
|
ObDereferenceObject(physicalDeviceObject);
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto clean2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Enable || !NT_SUCCESS(status)) {
|
|
|
|
ObDereferenceObject(physicalDeviceObject);
|
|
}
|
|
} else {
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// This will only happen if the registry information is messed up.
|
|
//
|
|
physicalDeviceObject = NULL;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
if (!Enable) {
|
|
//
|
|
// In the case of Disable we want to continue even if there was an error
|
|
// finding the PDO. Prior to adding support for deferring the
|
|
// IoSetDeviceInterfaceState calls, we never looked up the PDO for
|
|
// disables. This will make sure that we continue to behave the same as
|
|
// we used to in the case where we can't find the PDO.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ExFreePool(pKeyValueInfo);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto clean2;
|
|
}
|
|
|
|
if (Enable) {
|
|
//
|
|
// Retrieve the PDO's device object name. (Start out with a reasonably-sized
|
|
// buffer so we hopefully only have to retrieve this once.
|
|
//
|
|
deviceNameBufferLength = 256 * sizeof(WCHAR);
|
|
|
|
for ( ; ; ) {
|
|
|
|
deviceNameBuffer = ExAllocatePool(PagedPool, deviceNameBufferLength);
|
|
if (!deviceNameBuffer) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
status = IoGetDeviceProperty( physicalDeviceObject,
|
|
DevicePropertyPhysicalDeviceObjectName,
|
|
deviceNameBufferLength,
|
|
deviceNameBuffer,
|
|
&deviceNameBufferLength
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
break;
|
|
} else {
|
|
//
|
|
// Free the current buffer before we figure out what went wrong.
|
|
//
|
|
ExFreePool(deviceNameBuffer);
|
|
|
|
if (status != STATUS_BUFFER_TOO_SMALL) {
|
|
//
|
|
// Our failure wasn't because the buffer was too small--bail now.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Otherwise, loop back and try again with our new buffer size.
|
|
//
|
|
}
|
|
}
|
|
|
|
//
|
|
// OK, we don't need the PDO anymore.
|
|
//
|
|
ObDereferenceObject(physicalDeviceObject);
|
|
|
|
if (!NT_SUCCESS(status) || deviceNameBufferLength == 0) {
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Now create a unicode string based on the device object name we just retrieved.
|
|
//
|
|
|
|
RtlInitUnicodeString(&deviceNameString, deviceNameBuffer);
|
|
}
|
|
|
|
//
|
|
// Retrieve the linked value from the control subkey.
|
|
//
|
|
pKeyValueInfo=NULL;
|
|
status = IopGetRegistryValue(hInterfaceInstanceControl, REGSTR_VAL_LINKED, &pKeyValueInfo);
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
//
|
|
// The absence of a linked value is taken to mean not linked
|
|
//
|
|
linked = 0;
|
|
} else {
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// If the call failed, pKeyValueInfo was never allocated
|
|
//
|
|
goto clean3;
|
|
}
|
|
|
|
//
|
|
// Check linked is a DWORD
|
|
//
|
|
if(pKeyValueInfo->Type == REG_DWORD && pKeyValueInfo->DataLength == sizeof(ULONG)) {
|
|
|
|
linked = *((PULONG) KEY_VALUE_DATA(pKeyValueInfo));
|
|
} else {
|
|
|
|
//
|
|
// The registry is messed up - assume linked is 0 and the registry will be fixed when
|
|
// we update linked in a few moments
|
|
//
|
|
linked = 0;
|
|
}
|
|
}
|
|
if (pKeyValueInfo) {
|
|
|
|
ExFreePool (pKeyValueInfo);
|
|
}
|
|
|
|
//
|
|
// Retrieve the refcount value from the control subkey.
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_REFERENCECOUNT);
|
|
status = IopGetRegistryValue(hInterfaceParentControl,
|
|
tempString.Buffer,
|
|
&pKeyValueInfo
|
|
);
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
//
|
|
// The absence of a refcount value is taken to mean refcount == 0
|
|
//
|
|
refcount = 0;
|
|
|
|
} else {
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto clean3;
|
|
}
|
|
|
|
//
|
|
// Check refcount is a DWORD
|
|
//
|
|
if(pKeyValueInfo->Type == REG_DWORD && pKeyValueInfo->DataLength == sizeof(ULONG)) {
|
|
|
|
refcount = *((PULONG) KEY_VALUE_DATA(pKeyValueInfo));
|
|
|
|
} else {
|
|
|
|
//
|
|
// The registry is messed up - assume refcount is 0 and the registry will be fixed when
|
|
// we update refcount in a few moments
|
|
//
|
|
refcount = 0;
|
|
|
|
}
|
|
|
|
ExFreePool(pKeyValueInfo);
|
|
}
|
|
|
|
if (Enable) {
|
|
|
|
if (!linked) {
|
|
|
|
//
|
|
// check and update the reference count
|
|
//
|
|
if (refcount > 0) {
|
|
|
|
//
|
|
// Another device instance has already referenced this interface;
|
|
// just increment the reference count; don't try create a symbolic link.
|
|
//
|
|
refcount += 1;
|
|
} else {
|
|
|
|
//
|
|
// According to the reference count, no other device instances currently
|
|
// reference this interface, and therefore no symbolic links should exist,
|
|
// so we should create one.
|
|
//
|
|
refcount = 1;
|
|
|
|
status = IoCreateSymbolicLink(&globalSymbolicLinkName, &deviceNameString);
|
|
if (status == STATUS_OBJECT_NAME_COLLISION) {
|
|
|
|
//
|
|
// The reference count is messed up.
|
|
//
|
|
IopDbgPrint(( IOP_ERROR_LEVEL,
|
|
"IoSetDeviceInterfaceState: symbolic link for %ws already exists! status = %8.8X\n",
|
|
globalSymbolicLinkName.Buffer, status));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
linked = 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The association already exists - don't perform the notification
|
|
//
|
|
status = STATUS_OBJECT_NAME_EXISTS; // Informational message not error
|
|
goto clean3;
|
|
|
|
}
|
|
} else {
|
|
|
|
if (linked) {
|
|
|
|
//
|
|
// check and update the reference count
|
|
//
|
|
if (refcount > 1) {
|
|
|
|
//
|
|
// Another device instance already references this interface;
|
|
// just decrement the reference count; don't try to remove the symbolic link.
|
|
//
|
|
refcount -= 1;
|
|
} else {
|
|
|
|
//
|
|
// According to the reference count, only this device instance currently
|
|
// references this interface, so it is ok to delete this symbolic link
|
|
//
|
|
refcount = 0;
|
|
status = IoDeleteSymbolicLink(&globalSymbolicLinkName);
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
//
|
|
// The reference count is messed up.
|
|
//
|
|
IopDbgPrint((IOP_ERROR_LEVEL,
|
|
"IoSetDeviceInterfaceState: no symbolic link for %ws to delete! status = %8.8X\n",
|
|
globalSymbolicLinkName.Buffer,
|
|
status
|
|
));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
linked = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The association does not exists - fail and do not perform
|
|
// notification
|
|
//
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto clean3;
|
|
}
|
|
|
|
//
|
|
// Update the value of linked
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_LINKED);
|
|
status = ZwSetValueKey(hInterfaceInstanceControl,
|
|
&tempString,
|
|
0,
|
|
REG_DWORD,
|
|
&linked,
|
|
sizeof(linked)
|
|
);
|
|
|
|
//
|
|
// Update the value of refcount
|
|
//
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VAL_REFERENCECOUNT);
|
|
status = ZwSetValueKey(hInterfaceParentControl,
|
|
&tempString,
|
|
0,
|
|
REG_DWORD,
|
|
&refcount,
|
|
sizeof(refcount)
|
|
);
|
|
|
|
//
|
|
// Notify anyone that is interested
|
|
//
|
|
if (linked) {
|
|
|
|
PpSetDeviceClassChange((LPGUID)&GUID_DEVICE_INTERFACE_ARRIVAL,
|
|
&guid,
|
|
SymbolicLinkName
|
|
);
|
|
} else {
|
|
|
|
PpSetDeviceClassChange((LPGUID)&GUID_DEVICE_INTERFACE_REMOVAL,
|
|
&guid,
|
|
SymbolicLinkName
|
|
);
|
|
}
|
|
|
|
clean3:
|
|
if (deviceNameBuffer != NULL) {
|
|
ExFreePool(deviceNameBuffer);
|
|
}
|
|
|
|
clean2:
|
|
if (hInterfaceParentControl) {
|
|
ZwClose(hInterfaceParentControl);
|
|
}
|
|
if (hInterfaceInstanceControl) {
|
|
ZwClose(hInterfaceInstanceControl);
|
|
}
|
|
|
|
clean1:
|
|
|
|
RtlFreeUnicodeString(&globalSymbolicLinkName);
|
|
|
|
if (hInterfaceParentKey) {
|
|
ZwClose(hInterfaceParentKey);
|
|
}
|
|
if (hInterfaceInstanceKey) {
|
|
ZwClose(hInterfaceInstanceKey);
|
|
}
|
|
if(hInterfaceClassKey != NULL) {
|
|
ZwClose(hInterfaceClassKey);
|
|
}
|
|
|
|
clean0:
|
|
if (!NT_SUCCESS(status) && !Enable) {
|
|
//
|
|
// If we failed to disable an interface (most likely because the
|
|
// interface keys have already been deleted) report success.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PiDeferSetInterfaceState(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PUNICODE_STRING SymbolicLinkName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PPENDING_SET_INTERFACE_STATE pendingSetState;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(PiIsPnpRegistryLocked(TRUE));
|
|
|
|
pendingSetState = ExAllocatePool(PagedPool,
|
|
sizeof(PENDING_SET_INTERFACE_STATE)
|
|
);
|
|
if (pendingSetState == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IopAllocateUnicodeString(&pendingSetState->LinkName,
|
|
SymbolicLinkName->Length
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ExFreePool(pendingSetState);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyUnicodeString(&pendingSetState->LinkName, SymbolicLinkName);
|
|
|
|
InsertTailList(&DeviceNode->PendedSetInterfaceState,
|
|
&pendingSetState->List
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PiRemoveDeferredSetInterfaceState(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PUNICODE_STRING SymbolicLinkName
|
|
)
|
|
{
|
|
PPENDING_SET_INTERFACE_STATE pendingSetState;
|
|
PLIST_ENTRY entry;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(PiIsPnpRegistryLocked(TRUE));
|
|
//
|
|
// Find the deferred entry and remove it.
|
|
//
|
|
for ( entry = DeviceNode->PendedSetInterfaceState.Flink;
|
|
entry != &DeviceNode->PendedSetInterfaceState;
|
|
entry = entry->Flink) {
|
|
|
|
pendingSetState = CONTAINING_RECORD(entry,
|
|
PENDING_SET_INTERFACE_STATE,
|
|
List
|
|
);
|
|
|
|
if (RtlEqualUnicodeString(&pendingSetState->LinkName,
|
|
SymbolicLinkName,
|
|
TRUE
|
|
)) {
|
|
|
|
//
|
|
// Remove the entry and free associated storage.
|
|
//
|
|
RemoveEntryList(&pendingSetState->List);
|
|
|
|
ExFreePool(pendingSetState->LinkName.Buffer);
|
|
ExFreePool(pendingSetState);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDoDeferredSetInterfaceState(
|
|
IN PDEVICE_NODE DeviceNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the queued IoSetDeviceInterfaceState calls.
|
|
|
|
Parameters:
|
|
|
|
DeviceNode - Device node which has just been started.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
{
|
|
PPENDING_SET_INTERFACE_STATE entry;
|
|
|
|
PAGED_CODE();
|
|
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
PpMarkDeviceStackStartPending(DeviceNode->PhysicalDeviceObject, FALSE);
|
|
|
|
while (!IsListEmpty(&DeviceNode->PendedSetInterfaceState)) {
|
|
|
|
entry = (PPENDING_SET_INTERFACE_STATE)RemoveHeadList(
|
|
&DeviceNode->PendedSetInterfaceState);
|
|
|
|
IopProcessSetInterfaceState(&entry->LinkName, TRUE, FALSE);
|
|
|
|
ExFreePool(entry->LinkName.Buffer);
|
|
ExFreePool(entry);
|
|
}
|
|
|
|
PiUnlockPnpRegistry();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopSetRegistryStringValue(
|
|
IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN PUNICODE_STRING ValueData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets a value key in the registry to a specific value of string (REG_SZ) type.
|
|
|
|
Parameters:
|
|
|
|
KeyHandle - A handle to the key under which the value is stored.
|
|
|
|
ValueName - Supplies a pointer to the name of the value key
|
|
|
|
ValueData - Supplies a pointer to the string to be stored in the key. The
|
|
data will automatically be null terminated for storage in the registry.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING terminatedString, stringCopy;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(ValueName);
|
|
ASSERT(ValueData);
|
|
ASSERT(ValueName->Buffer);
|
|
ASSERT(ValueData->Buffer);
|
|
|
|
PiWstrToUnicodeString(&stringCopy, NULL);
|
|
|
|
//
|
|
// Allocate new string if ValueName is not big enough to hold the
|
|
// terminating NULL.
|
|
//
|
|
if (ValueData->MaximumLength - ValueData->Length < sizeof(UNICODE_NULL)) {
|
|
|
|
status = IopAllocateUnicodeString(&stringCopy, ValueData->Length);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Copy the input string (it will also NULL terminate).
|
|
//
|
|
RtlCopyUnicodeString(&stringCopy, ValueData);
|
|
terminatedString = stringCopy;
|
|
} else {
|
|
|
|
//
|
|
// Null terminate the string
|
|
//
|
|
ValueData->Buffer[CB_TO_CWC(ValueData->Length)] = UNICODE_NULL;
|
|
terminatedString = *ValueData;
|
|
}
|
|
|
|
//
|
|
// Set the value in the registry.
|
|
//
|
|
status = ZwSetValueKey(
|
|
KeyHandle,
|
|
ValueName,
|
|
0,
|
|
REG_SZ,
|
|
(PVOID)terminatedString.Buffer,
|
|
terminatedString.Length + sizeof(UNICODE_NULL));
|
|
|
|
//
|
|
// Free the temporary string (Rtl API will do the right thing for an empty
|
|
// string).
|
|
//
|
|
RtlFreeUnicodeString(&stringCopy);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IopAllocateUnicodeString(
|
|
IN OUT PUNICODE_STRING String,
|
|
IN USHORT Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a buffer for a unicode string of a given length
|
|
and initialises the UNICODE_STRING structure appropriately. When the
|
|
string is no longer required it can be freed using RtlFreeUnicodeString.
|
|
The buffer also be directly deleted by ExFreePool and so can be handed
|
|
back to a caller.
|
|
|
|
Parameters:
|
|
|
|
String - Supplies a pointer to an uninitialised unicode string which will
|
|
be manipulated by the function.
|
|
|
|
Length - The number of BYTES long that the string will be.
|
|
|
|
Return Value:
|
|
|
|
Either STATUS_INSUFFICIENT_RESOURCES indicating paged pool is exhausted or
|
|
STATUS_SUCCESS.
|
|
|
|
Remarks:
|
|
|
|
The buffer allocated will be one character (2 bytes) more than length
|
|
specified. This is to allow for easy null termination of the strings - eg
|
|
for registry storage.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
String->Length = 0;
|
|
String->MaximumLength = Length + sizeof(UNICODE_NULL);
|
|
|
|
String->Buffer = ExAllocatePool(PagedPool, String->MaximumLength);
|
|
if (String->Buffer == NULL) {
|
|
|
|
String->MaximumLength = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|