/*++ Copyright (c) 1995-2001 Microsoft Corporation 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 IopOverwriteBuffer( IN PBUFFER_INFO Info, IN PVOID Data, IN ULONG DataSize ); NTSTATUS IopRealloc( IN OUT PVOID *Buffer, IN ULONG OldSize, IN ULONG NewSize ); 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 ); #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, IopFreeAllocatedUnicodeString) #pragma alloc_text(PAGE, IopFreeBuffer) #pragma alloc_text(PAGE, IopGetDeviceInterfaces) #pragma alloc_text(PAGE, IopOpenOrCreateDeviceInterfaceSubKeys) #pragma alloc_text(PAGE, IopOverwriteBuffer) #pragma alloc_text(PAGE, IopParseSymbolicLinkName) #pragma alloc_text(PAGE, IopProcessSetInterfaceState) #pragma alloc_text(PAGE, IopRealloc) #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) #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. --*/ { ASSERT(Info); Info->Buffer = ExAllocatePool(PagedPool, Size); if (Info->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Info->Current = Info->Buffer; 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; ASSERT(Info); used = (ULONG)(Info->Current - Info->Buffer); newBuffer = ExAllocatePool(PagedPool, NewSize); if (newBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } if (CopyContents) { // // Assert there is room in the 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. --*/ { ASSERT(Info); // // Free the 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; 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)) { goto clean0; } } // // Copy the data into the buffer // RtlCopyMemory(Info->Current, Data, DataSize); // // Advance down the buffer // Info->Current += DataSize; clean0: return status; } NTSTATUS IopOverwriteBuffer( IN PBUFFER_INFO Info, IN PVOID Data, IN ULONG DataSize ) /*++ Routine Description: Copies data into the buffer, overwriting what is currently present, resising 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; ASSERT(Info); free = Info->MaxSize; if (free < DataSize) { status = IopResizeBuffer(Info, DataSize, FALSE); if (!NT_SUCCESS(status)) { goto clean0; } } // // Copy the data into the buffer // RtlCopyMemory(Info->Buffer, Data, DataSize); // // Advance down the buffer // Info->Current += DataSize; clean0: 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 guidString, tempString, defaultString, symLinkString, devnodeString; BUFFER_INFO returnBuffer, infoBuffer, symLinkBuffer, devnodeNameBuffer; PKEY_VALUE_FULL_INFORMATION pDefaultInfo; 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)) { 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, IopConstStringLength(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, IopConstStringLength(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 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 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; PDEVICE_NODE pDeviceNode; PAGED_CODE(); // // Check we have a PDO and if so extract the instance path from it // if (ARGUMENT_PRESENT(PhysicalDeviceObject)) { ASSERT_PDO(PhysicalDeviceObject); pDeviceNode = (PDEVICE_NODE) PhysicalDeviceObject->DeviceObjectExtension->DeviceNode; pDeviceName = &pDeviceNode->InstancePath; } status = IopGetDeviceInterfaces(InterfaceClassGuid, pDeviceName, Flags, FALSE, SymbolicLinkList, NULL ); return status; } NTSTATUS IopRealloc( IN OUT PVOID *Buffer, IN ULONG OldSize, IN ULONG NewSize ) /*++ Routine Description: This implements a variation of the traditional C realloc routine. Parameters: Buffer - Supplies a pointer to a pointer to the buffer that is being reallocated. On sucessful completion it the pointer will be updated to point to the new buffer, on failure it will still point to the old buffer. OldSize - The size in bytes of the memory block referenced by Buffer NewSize - The desired new size in bytes of the buffer. This can be larger or smaller than the OldSize Return Value: Status code that indicates whether or not the function was successful. --*/ { PVOID newBuffer; PAGED_CODE(); ASSERT(*Buffer); // // Allocate a new buffer // newBuffer = ExAllocatePool(PagedPool, NewSize); if (newBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Copy the contents of the old buffer // if(OldSize <= NewSize) { RtlCopyMemory(newBuffer, *Buffer , OldSize); } else { RtlCopyMemory(newBuffer, *Buffer , NewSize); } // // Free up the old buffer // ExFreePool(*Buffer); // // Hand the new buffer back to the caller // *Buffer = newBuffer; return STATUS_SUCCESS; } 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) && !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 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)) { goto clean0; } // // Open the "Device Parameters" subkey. // PiWstrToUnicodeString(&unicodeString, REGSTR_KEY_DEVICEPARAMETERS); status = IopCreateRegistryKeyEx( DeviceInterfaceKey, hKey, &unicodeString, DesiredAccess, REG_OPTION_NON_VOLATILE, NULL ); ZwClose(hKey); clean0: 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 = (PDEVICE_NODE) PhysicalDeviceObject->DeviceObjectExtension->DeviceNode; 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(); IopFreeAllocatedUnicodeString(&otherString); if (!NT_SUCCESS(status)) { IopFreeAllocatedUnicodeString(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(); IopFreeAllocatedUnicodeString(&interfaceKeyName); clean1: IopFreeAllocatedUnicodeString(&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; } IopFreeAllocatedUnicodeString(&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 ); } 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 ); TempInterfaceDisposition = REG_OPENED_EXISTING_KEY; } 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: IopFreeAllocatedUnicodeString(&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 < (IopConstStringLength(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 { IopFreeAllocatedUnicodeString(AliasSymbolicLinkName); } IopFreeAllocatedUnicodeString(&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 \\. 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 \\?\\\ When no longer required it should be freed using IopFreeAllocatedUnicodeString. 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 \??\\\. When no longer required it should be freed using IopFreeAllocatedUnicodeString. 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: IopFreeAllocatedUnicodeString(&mungedDeviceString); clean2: if (!NT_SUCCESS(status)) { IopFreeAllocatedUnicodeString(UserString); } clean1: if (!NT_SUCCESS(status)) { IopFreeAllocatedUnicodeString(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 = InString->Length / sizeof(WCHAR); // // 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 // if (refStringPresent) { OutString->Length = InString->Length - (refString.Length + sizeof(WCHAR)); } else { OutString->Length = InString->Length; } } 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??\\\. When no longer required it should be freed using IopFreeAllocatedUnicodeString. 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)) { IopFreeAllocatedUnicodeString(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)) { IopFreeAllocatedUnicodeString(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)) { 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 < (IopConstStringLength(KERNEL_SYMLINK_STRING_PREFIX)+GUID_STRING_SIZE+1)) { status = STATUS_INVALID_PARAMETER; goto clean0; } // // 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))) { status = STATUS_INVALID_PARAMETER; goto clean0; } // // 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 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. --*/ { KIRQL irql; PDEVICE_OBJECT attachedDevice; PiLockPnpRegistry(TRUE); irql = KeAcquireQueuedSpinLock( LockQueueIoDatabaseLock ); for (attachedDevice = DeviceNode->PhysicalDeviceObject; attachedDevice; attachedDevice = attachedDevice->AttachedDevice) { attachedDevice->DeviceObjectExtension->ExtensionFlags &= ~DOE_START_PENDING; } KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, irql ); while (!IsListEmpty(&DeviceNode->PendedSetInterfaceState)) { PPENDING_SET_INTERFACE_STATE entry; entry = (PPENDING_SET_INTERFACE_STATE)RemoveHeadList(&DeviceNode->PendedSetInterfaceState); IopProcessSetInterfaceState(&entry->LinkName, TRUE, FALSE); ExFreePool(entry->LinkName.Buffer); ExFreePool(entry); } PiUnlockPnpRegistry(); return STATUS_SUCCESS; } 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; PPENDING_SET_INTERFACE_STATE pendingSetState; // // The device hasn't been started yet. We need to queue // any enables and remove items from the queue on a disable. // deviceNode = (PDEVICE_NODE)physicalDeviceObject->DeviceObjectExtension->DeviceNode; if (Enable) { pendingSetState = ExAllocatePool( PagedPool, sizeof(PENDING_SET_INTERFACE_STATE)); if (pendingSetState != NULL) { pendingSetState->LinkName.Buffer = ExAllocatePool( PagedPool, SymbolicLinkName->Length); if (pendingSetState->LinkName.Buffer != NULL) { // // Capture the callers info and queue it to the // devnode. Once the device stack is started // we will dequeue and process it. // pendingSetState->LinkName.MaximumLength = SymbolicLinkName->Length; pendingSetState->LinkName.Length = SymbolicLinkName->Length; RtlCopyMemory( pendingSetState->LinkName.Buffer, SymbolicLinkName->Buffer, SymbolicLinkName->Length); InsertTailList( &deviceNode->PendedSetInterfaceState, &pendingSetState->List); ExFreePool(pKeyValueInfo); ObDereferenceObject(physicalDeviceObject); status = STATUS_SUCCESS; goto clean2; } else { // // Couldn't allocate a buffer to hold the // symbolic link name. // ExFreePool(pendingSetState); status = STATUS_INSUFFICIENT_RESOURCES; } } else { // // Couldn't allocate the PENDING_SET_INTERFACE_STATE // structure. // status = STATUS_INSUFFICIENT_RESOURCES; } } else { PLIST_ENTRY entry; // // We are disabling an interface. Since we aren't // started yet we should have queued the enable. Now // we go back and find the matching enable and remove // it from the queue. // 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)) { // // We found it, remove it from the list and // free it. // RemoveEntryList(&pendingSetState->List); ExFreePool(pendingSetState->LinkName.Buffer); ExFreePool(pendingSetState); break; } } #if 0 // // Debug code to catch the case where we couldn't find // the entry to remove. This could happen if we messed // up adding the entry to the list or the driver disabled // an interface without first enabling it. Either way // it probably merits some investigation. // if (entry == &deviceNode->PendedSetInterfaceState) { IopDbgPrint((IOP_ERROR_LEVEL, "IopProcessSetInterfaceState: Disable couldn't find deferred enable, DeviceNode = 0x%p, SymbolicLink = \"%Z\"\n", deviceNode, SymbolicLinkName)); } ASSERT(entry != &deviceNode->PendedSetInterfaceState); #endif 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; #if 0 IopSetupDeviceObjectFromDeviceClass(physicalDeviceObject, hInterfaceClassKey); #endif } 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: IopFreeAllocatedUnicodeString(&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 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; PAGED_CODE(); ASSERT(ValueName); ASSERT(ValueData); ASSERT(ValueName->Buffer); ASSERT(ValueData->Buffer); // // Null terminate the string // if ((ValueData->MaximumLength - ValueData->Length) >= sizeof(UNICODE_NULL)) { // // There is room in the buffer so just append a null // ValueData->Buffer[(ValueData->Length / sizeof(WCHAR))] = UNICODE_NULL; // // Set the registry value // status = ZwSetValueKey(KeyHandle, ValueName, 0, REG_SZ, (PVOID) ValueData->Buffer, ValueData->Length + sizeof(UNICODE_NULL) ); } else { UNICODE_STRING tempString; // // There is no room so allocate a new buffer and so we need to build // a new string with room // status = IopAllocateUnicodeString(&tempString, ValueData->Length); if (!NT_SUCCESS(status)) { goto clean0; } // // Copy the input string to the output string // tempString.Length = ValueData->Length; RtlCopyMemory(tempString.Buffer, ValueData->Buffer, ValueData->Length); // // Add the null termination // tempString.Buffer[tempString.Length / sizeof(WCHAR)] = UNICODE_NULL; // // Set the registry value // status = ZwSetValueKey(KeyHandle, ValueName, 0, REG_SZ, (PVOID) tempString.Buffer, tempString.Length + sizeof(UNICODE_NULL) ); // // Free the temporary string // IopFreeAllocatedUnicodeString(&tempString); } clean0: 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 IopFreeAllocatedString. 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, Length + sizeof(UNICODE_NULL)); if (String->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } else { return STATUS_SUCCESS; } } VOID IopFreeAllocatedUnicodeString( PUNICODE_STRING String ) /*++ Routine Description: This routine frees a string previously allocated with IopAllocateUnicodeString. Parameters: String - Supplies a pointer to the string that has been previously allocated. Return Value: None --*/ { PAGED_CODE(); ASSERT(String); RtlFreeUnicodeString(String); }