mirror of
https://github.com/reactos/reactos.git
synced 2024-11-11 01:04:11 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
1326 lines
44 KiB
C
1326 lines
44 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/io/iomgr/deviface.c
|
|
* PURPOSE: Device interface functions
|
|
*
|
|
* PROGRAMMERS: Filip Navara (xnavara@volny.cz)
|
|
* Matthew Brace (ismarc@austin.rr.com)
|
|
* Hervé Poussineau (hpoussin@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static PWCHAR BaseKeyString = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
|
|
|
|
static
|
|
NTSTATUS
|
|
OpenRegistryHandlesFromSymbolicLink(IN PUNICODE_STRING SymbolicLinkName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN OPTIONAL PHANDLE GuidKey,
|
|
IN OPTIONAL PHANDLE DeviceKey,
|
|
IN OPTIONAL PHANDLE InstanceKey)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
WCHAR PathBuffer[MAX_PATH];
|
|
UNICODE_STRING BaseKeyU;
|
|
UNICODE_STRING GuidString, SubKeyName, ReferenceString;
|
|
PWCHAR StartPosition, EndPosition;
|
|
HANDLE ClassesKey;
|
|
PHANDLE GuidKeyRealP, DeviceKeyRealP, InstanceKeyRealP;
|
|
HANDLE GuidKeyReal, DeviceKeyReal, InstanceKeyReal;
|
|
NTSTATUS Status;
|
|
|
|
SubKeyName.Buffer = NULL;
|
|
|
|
if (GuidKey != NULL)
|
|
GuidKeyRealP = GuidKey;
|
|
else
|
|
GuidKeyRealP = &GuidKeyReal;
|
|
|
|
if (DeviceKey != NULL)
|
|
DeviceKeyRealP = DeviceKey;
|
|
else
|
|
DeviceKeyRealP = &DeviceKeyReal;
|
|
|
|
if (InstanceKey != NULL)
|
|
InstanceKeyRealP = InstanceKey;
|
|
else
|
|
InstanceKeyRealP = &InstanceKeyReal;
|
|
|
|
*GuidKeyRealP = INVALID_HANDLE_VALUE;
|
|
*DeviceKeyRealP = INVALID_HANDLE_VALUE;
|
|
*InstanceKeyRealP = INVALID_HANDLE_VALUE;
|
|
|
|
BaseKeyU.Buffer = PathBuffer;
|
|
BaseKeyU.Length = 0;
|
|
BaseKeyU.MaximumLength = MAX_PATH * sizeof(WCHAR);
|
|
|
|
RtlAppendUnicodeToString(&BaseKeyU, BaseKeyString);
|
|
|
|
/* Open the DeviceClasses key */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&BaseKeyU,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
Status = ZwOpenKey(&ClassesKey,
|
|
DesiredAccess | KEY_ENUMERATE_SUB_KEYS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to open %wZ\n", &BaseKeyU);
|
|
goto cleanup;
|
|
}
|
|
|
|
StartPosition = wcschr(SymbolicLinkName->Buffer, L'{');
|
|
EndPosition = wcschr(SymbolicLinkName->Buffer, L'}');
|
|
if (!StartPosition || !EndPosition || StartPosition > EndPosition)
|
|
{
|
|
DPRINT1("Bad symbolic link: %wZ\n", SymbolicLinkName);
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
GuidString.Buffer = StartPosition;
|
|
GuidString.MaximumLength = GuidString.Length = (USHORT)((ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)StartPosition);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&GuidString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
ClassesKey,
|
|
NULL);
|
|
Status = ZwOpenKey(GuidKeyRealP,
|
|
DesiredAccess | KEY_ENUMERATE_SUB_KEYS,
|
|
&ObjectAttributes);
|
|
ZwClose(ClassesKey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to open %wZ%wZ (%x)\n", &BaseKeyU, &GuidString, Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
SubKeyName.MaximumLength = SymbolicLinkName->Length + sizeof(WCHAR);
|
|
SubKeyName.Length = 0;
|
|
SubKeyName.Buffer = ExAllocatePool(PagedPool, SubKeyName.MaximumLength);
|
|
if (!SubKeyName.Buffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(&SubKeyName,
|
|
SymbolicLinkName);
|
|
|
|
SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
SubKeyName.Buffer[0] = L'#';
|
|
SubKeyName.Buffer[1] = L'#';
|
|
SubKeyName.Buffer[2] = L'?';
|
|
SubKeyName.Buffer[3] = L'#';
|
|
|
|
ReferenceString.Buffer = wcsrchr(SubKeyName.Buffer, '\\');
|
|
if (ReferenceString.Buffer != NULL)
|
|
{
|
|
ReferenceString.Buffer[0] = L'#';
|
|
|
|
SubKeyName.Length = (USHORT)((ULONG_PTR)(ReferenceString.Buffer) - (ULONG_PTR)SubKeyName.Buffer);
|
|
ReferenceString.Length = SymbolicLinkName->Length - SubKeyName.Length;
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&ReferenceString, L"#");
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&SubKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
*GuidKeyRealP,
|
|
NULL);
|
|
Status = ZwOpenKey(DeviceKeyRealP,
|
|
DesiredAccess | KEY_ENUMERATE_SUB_KEYS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to open %wZ%wZ\\%wZ\n", &BaseKeyU, &GuidString, &SubKeyName);
|
|
goto cleanup;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ReferenceString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
*DeviceKeyRealP,
|
|
NULL);
|
|
Status = ZwOpenKey(InstanceKeyRealP,
|
|
DesiredAccess,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to open %wZ%wZ\\%wZ%\\%wZ (%x)\n", &BaseKeyU, &GuidString, &SubKeyName, &ReferenceString, Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
if (SubKeyName.Buffer != NULL)
|
|
ExFreePool(SubKeyName.Buffer);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (!GuidKey)
|
|
ZwClose(*GuidKeyRealP);
|
|
|
|
if (!DeviceKey)
|
|
ZwClose(*DeviceKeyRealP);
|
|
|
|
if (!InstanceKey)
|
|
ZwClose(*InstanceKeyRealP);
|
|
}
|
|
else
|
|
{
|
|
if (*GuidKeyRealP != INVALID_HANDLE_VALUE)
|
|
ZwClose(*GuidKeyRealP);
|
|
|
|
if (*DeviceKeyRealP != INVALID_HANDLE_VALUE)
|
|
ZwClose(*DeviceKeyRealP);
|
|
|
|
if (*InstanceKeyRealP != INVALID_HANDLE_VALUE)
|
|
ZwClose(*InstanceKeyRealP);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
/*++
|
|
* @name IoOpenDeviceInterfaceRegistryKey
|
|
* @unimplemented
|
|
*
|
|
* Provides a handle to the device's interface instance registry key.
|
|
* Documented in WDK.
|
|
*
|
|
* @param SymbolicLinkName
|
|
* Pointer to a string which identifies the device interface instance
|
|
*
|
|
* @param DesiredAccess
|
|
* Desired ACCESS_MASK used to access the key (like KEY_READ,
|
|
* KEY_WRITE, etc)
|
|
*
|
|
* @param DeviceInterfaceKey
|
|
* If a call has been succesfull, a handle to the registry key
|
|
* will be stored there
|
|
*
|
|
* @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
|
|
* otherwise (see WDK for details)
|
|
*
|
|
* @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE DeviceInterfaceKey)
|
|
{
|
|
HANDLE InstanceKey, DeviceParametersKey;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING DeviceParametersU = RTL_CONSTANT_STRING(L"Device Parameters");
|
|
|
|
Status = OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName,
|
|
KEY_CREATE_SUB_KEY,
|
|
NULL,
|
|
NULL,
|
|
&InstanceKey);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DeviceParametersU,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
|
InstanceKey,
|
|
NULL);
|
|
Status = ZwCreateKey(&DeviceParametersKey,
|
|
DesiredAccess,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL);
|
|
ZwClose(InstanceKey);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
*DeviceInterfaceKey = DeviceParametersKey;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
* @name IoGetDeviceInterfaceAlias
|
|
* @unimplemented
|
|
*
|
|
* Returns the alias device interface of the specified device interface
|
|
* instance, if the alias exists.
|
|
* Documented in WDK.
|
|
*
|
|
* @param SymbolicLinkName
|
|
* Pointer to a string which identifies the device interface instance
|
|
*
|
|
* @param AliasInterfaceClassGuid
|
|
* See WDK
|
|
*
|
|
* @param AliasSymbolicLinkName
|
|
* See WDK
|
|
*
|
|
* @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
|
|
* otherwise (see WDK for details)
|
|
*
|
|
* @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoGetDeviceInterfaceAlias(IN PUNICODE_STRING SymbolicLinkName,
|
|
IN CONST GUID *AliasInterfaceClassGuid,
|
|
OUT PUNICODE_STRING AliasSymbolicLinkName)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*++
|
|
* @name IopOpenInterfaceKey
|
|
*
|
|
* Returns the alias device interface of the specified device interface
|
|
*
|
|
* @param InterfaceClassGuid
|
|
* FILLME
|
|
*
|
|
* @param DesiredAccess
|
|
* FILLME
|
|
*
|
|
* @param pInterfaceKey
|
|
* FILLME
|
|
*
|
|
* @return Usual NTSTATUS
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
static NTSTATUS
|
|
IopOpenInterfaceKey(IN CONST GUID *InterfaceClassGuid,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT HANDLE *pInterfaceKey)
|
|
{
|
|
UNICODE_STRING LocalMachine = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\");
|
|
UNICODE_STRING GuidString;
|
|
UNICODE_STRING KeyName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
|
|
NTSTATUS Status;
|
|
|
|
GuidString.Buffer = KeyName.Buffer = NULL;
|
|
|
|
Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
KeyName.Length = 0;
|
|
KeyName.MaximumLength = LocalMachine.Length + (wcslen(REGSTR_PATH_DEVICE_CLASSES) + 1) * sizeof(WCHAR) + GuidString.Length;
|
|
KeyName.Buffer = ExAllocatePool(PagedPool, KeyName.MaximumLength);
|
|
if (!KeyName.Buffer)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = RtlAppendUnicodeStringToString(&KeyName, &LocalMachine);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
Status = RtlAppendUnicodeToString(&KeyName, REGSTR_PATH_DEVICE_CLASSES);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
Status = RtlAppendUnicodeToString(&KeyName, L"\\");
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
Status = RtlAppendUnicodeStringToString(&KeyName, &GuidString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = ZwOpenKey(
|
|
&InterfaceKey,
|
|
DesiredAccess,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
*pInterfaceKey = InterfaceKey;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InterfaceKey != INVALID_HANDLE_VALUE)
|
|
ZwClose(InterfaceKey);
|
|
}
|
|
RtlFreeUnicodeString(&GuidString);
|
|
RtlFreeUnicodeString(&KeyName);
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
* @name IoGetDeviceInterfaces
|
|
* @implemented
|
|
*
|
|
* Returns a list of device interfaces of a particular device interface class.
|
|
* Documented in WDK
|
|
*
|
|
* @param InterfaceClassGuid
|
|
* Points to a class GUID specifying the device interface class
|
|
*
|
|
* @param PhysicalDeviceObject
|
|
* Points to an optional PDO that narrows the search to only the
|
|
* device interfaces of the device represented by the PDO
|
|
*
|
|
* @param Flags
|
|
* Specifies flags that modify the search for device interfaces. The
|
|
* DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
|
|
* returned symbolic links should contain also disabled device
|
|
* interfaces in addition to the enabled ones.
|
|
*
|
|
* @param SymbolicLinkList
|
|
* Points to a character pointer that is filled in on successful return
|
|
* with a list of unicode strings identifying the device interfaces
|
|
* that match the search criteria. The newly allocated buffer contains
|
|
* a list of symbolic link names. Each unicode string in the list is
|
|
* null-terminated; the end of the whole list is marked by an additional
|
|
* NULL. The caller is responsible for freeing the buffer (ExFreePool)
|
|
* when it is no longer needed.
|
|
* If no device interfaces match the search criteria, this routine
|
|
* returns STATUS_SUCCESS and the string contains a single NULL
|
|
* character.
|
|
*
|
|
* @return Usual NTSTATUS
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoGetDeviceInterfaces(IN CONST GUID *InterfaceClassGuid,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
|
|
IN ULONG Flags,
|
|
OUT PWSTR *SymbolicLinkList)
|
|
{
|
|
UNICODE_STRING Control = RTL_CONSTANT_STRING(L"Control");
|
|
UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
|
|
HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
|
|
HANDLE DeviceKey = INVALID_HANDLE_VALUE;
|
|
HANDLE ReferenceKey = INVALID_HANDLE_VALUE;
|
|
HANDLE ControlKey = INVALID_HANDLE_VALUE;
|
|
PKEY_BASIC_INFORMATION DeviceBi = NULL;
|
|
PKEY_BASIC_INFORMATION ReferenceBi = NULL;
|
|
PKEY_VALUE_PARTIAL_INFORMATION bip = NULL;
|
|
PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
|
|
UNICODE_STRING KeyName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
BOOLEAN FoundRightPDO = FALSE;
|
|
ULONG i = 0, j, Size, NeededLength, ActualLength, LinkedValue;
|
|
UNICODE_STRING ReturnBuffer = { 0, 0, NULL };
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = IopOpenInterfaceKey(InterfaceClassGuid, KEY_ENUMERATE_SUB_KEYS, &InterfaceKey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Enumerate subkeys (ie the different device objets) */
|
|
while (TRUE)
|
|
{
|
|
Status = ZwEnumerateKey(
|
|
InterfaceKey,
|
|
i,
|
|
KeyBasicInformation,
|
|
NULL,
|
|
0,
|
|
&Size);
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
break;
|
|
}
|
|
else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
DeviceBi = ExAllocatePool(PagedPool, Size);
|
|
if (!DeviceBi)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
Status = ZwEnumerateKey(
|
|
InterfaceKey,
|
|
i++,
|
|
KeyBasicInformation,
|
|
DeviceBi,
|
|
Size,
|
|
&Size);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Open device key */
|
|
KeyName.Length = KeyName.MaximumLength = (USHORT)DeviceBi->NameLength;
|
|
KeyName.Buffer = DeviceBi->Name;
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
InterfaceKey,
|
|
NULL);
|
|
Status = ZwOpenKey(
|
|
&DeviceKey,
|
|
KEY_ENUMERATE_SUB_KEYS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (PhysicalDeviceObject)
|
|
{
|
|
/* Check if we are on the right physical device object,
|
|
* by reading the DeviceInstance string
|
|
*/
|
|
DPRINT1("PhysicalDeviceObject != NULL. Case not implemented.\n");
|
|
//FoundRightPDO = TRUE;
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Enumerate subkeys (ie the different reference strings) */
|
|
j = 0;
|
|
while (TRUE)
|
|
{
|
|
Status = ZwEnumerateKey(
|
|
DeviceKey,
|
|
j,
|
|
KeyBasicInformation,
|
|
NULL,
|
|
0,
|
|
&Size);
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
break;
|
|
}
|
|
else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
ReferenceBi = ExAllocatePool(PagedPool, Size);
|
|
if (!ReferenceBi)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
Status = ZwEnumerateKey(
|
|
DeviceKey,
|
|
j++,
|
|
KeyBasicInformation,
|
|
ReferenceBi,
|
|
Size,
|
|
&Size);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
KeyName.Length = KeyName.MaximumLength = (USHORT)ReferenceBi->NameLength;
|
|
KeyName.Buffer = ReferenceBi->Name;
|
|
if (RtlEqualUnicodeString(&KeyName, &Control, TRUE))
|
|
{
|
|
/* Skip Control subkey */
|
|
goto NextReferenceString;
|
|
}
|
|
|
|
/* Open reference key */
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
DeviceKey,
|
|
NULL);
|
|
Status = ZwOpenKey(
|
|
&ReferenceKey,
|
|
KEY_QUERY_VALUE,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(Flags & DEVICE_INTERFACE_INCLUDE_NONACTIVE))
|
|
{
|
|
/* We have to check if the interface is enabled, by
|
|
* reading the Linked value in the Control subkey
|
|
*/
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&Control,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ReferenceKey,
|
|
NULL);
|
|
Status = ZwOpenKey(
|
|
&ControlKey,
|
|
KEY_QUERY_VALUE,
|
|
&ObjectAttributes);
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
/* That's OK. The key doesn't exist (yet) because
|
|
* the interface is not activated.
|
|
*/
|
|
goto NextReferenceString;
|
|
}
|
|
else if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(&KeyName, L"Linked");
|
|
Status = ZwQueryValueKey(ControlKey,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
NULL,
|
|
0,
|
|
&NeededLength);
|
|
if (Status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
ActualLength = NeededLength;
|
|
PartialInfo = ExAllocatePool(NonPagedPool, ActualLength);
|
|
if (!PartialInfo)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = ZwQueryValueKey(ControlKey,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
PartialInfo,
|
|
ActualLength,
|
|
&NeededLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("ZwQueryValueKey #2 failed (%x)\n", Status);
|
|
ExFreePool(PartialInfo);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (PartialInfo->Type != REG_DWORD || PartialInfo->DataLength != sizeof(ULONG))
|
|
{
|
|
DPRINT1("Bad registry read\n");
|
|
ExFreePool(PartialInfo);
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(&LinkedValue,
|
|
PartialInfo->Data,
|
|
PartialInfo->DataLength);
|
|
|
|
ExFreePool(PartialInfo);
|
|
if (LinkedValue == 0)
|
|
{
|
|
/* This interface isn't active */
|
|
goto NextReferenceString;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("ZwQueryValueKey #1 failed (%x)\n", Status);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Read the SymbolicLink string and add it into SymbolicLinkList */
|
|
Status = ZwQueryValueKey(
|
|
ReferenceKey,
|
|
&SymbolicLink,
|
|
KeyValuePartialInformation,
|
|
NULL,
|
|
0,
|
|
&Size);
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
bip = ExAllocatePool(PagedPool, Size);
|
|
if (!bip)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
Status = ZwQueryValueKey(
|
|
ReferenceKey,
|
|
&SymbolicLink,
|
|
KeyValuePartialInformation,
|
|
bip,
|
|
Size,
|
|
&Size);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
else if (bip->Type != REG_SZ)
|
|
{
|
|
DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip->Type, REG_SZ);
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
else if (bip->DataLength < 5 * sizeof(WCHAR))
|
|
{
|
|
DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip->DataLength < 5 * sizeof(WCHAR));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
KeyName.Length = KeyName.MaximumLength = (USHORT)bip->DataLength - 4 * sizeof(WCHAR);
|
|
KeyName.Buffer = &((PWSTR)bip->Data)[4];
|
|
if (KeyName.Length && KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] == UNICODE_NULL)
|
|
{
|
|
/* Remove trailing NULL */
|
|
KeyName.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
/* Add new symbolic link to symbolic link list */
|
|
if (ReturnBuffer.Length + KeyName.Length + sizeof(WCHAR) > ReturnBuffer.MaximumLength)
|
|
{
|
|
PWSTR NewBuffer;
|
|
ReturnBuffer.MaximumLength = max(ReturnBuffer.MaximumLength * 2, ReturnBuffer.Length + KeyName.Length + 2 * sizeof(WCHAR));
|
|
NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
|
|
if (!NewBuffer)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
|
|
if (ReturnBuffer.Buffer)
|
|
ExFreePool(ReturnBuffer.Buffer);
|
|
ReturnBuffer.Buffer = NewBuffer;
|
|
}
|
|
DPRINT("Adding symbolic link %wZ\n", &KeyName);
|
|
Status = RtlAppendUnicodeStringToString(&ReturnBuffer, &KeyName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
|
|
goto cleanup;
|
|
}
|
|
/* RtlAppendUnicodeStringToString added a NULL at the end of the
|
|
* destination string, but didn't increase the Length field.
|
|
* Do it for it.
|
|
*/
|
|
ReturnBuffer.Length += sizeof(WCHAR);
|
|
|
|
NextReferenceString:
|
|
ExFreePool(ReferenceBi);
|
|
ReferenceBi = NULL;
|
|
if (bip)
|
|
ExFreePool(bip);
|
|
bip = NULL;
|
|
if (ReferenceKey != INVALID_HANDLE_VALUE)
|
|
{
|
|
ZwClose(ReferenceKey);
|
|
ReferenceKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
if (ControlKey != INVALID_HANDLE_VALUE)
|
|
{
|
|
ZwClose(ControlKey);
|
|
ControlKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
if (FoundRightPDO)
|
|
{
|
|
/* No need to go further, as we already have found what we searched */
|
|
break;
|
|
}
|
|
|
|
ExFreePool(DeviceBi);
|
|
DeviceBi = NULL;
|
|
ZwClose(DeviceKey);
|
|
DeviceKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
/* Add final NULL to ReturnBuffer */
|
|
if (ReturnBuffer.Length >= ReturnBuffer.MaximumLength)
|
|
{
|
|
PWSTR NewBuffer;
|
|
ReturnBuffer.MaximumLength += sizeof(WCHAR);
|
|
NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
|
|
if (!NewBuffer)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
if (ReturnBuffer.Buffer)
|
|
{
|
|
RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
|
|
ExFreePool(ReturnBuffer.Buffer);
|
|
}
|
|
ReturnBuffer.Buffer = NewBuffer;
|
|
}
|
|
ReturnBuffer.Buffer[ReturnBuffer.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
*SymbolicLinkList = ReturnBuffer.Buffer;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
if (!NT_SUCCESS(Status) && ReturnBuffer.Buffer)
|
|
ExFreePool(ReturnBuffer.Buffer);
|
|
if (InterfaceKey != INVALID_HANDLE_VALUE)
|
|
ZwClose(InterfaceKey);
|
|
if (DeviceKey != INVALID_HANDLE_VALUE)
|
|
ZwClose(DeviceKey);
|
|
if (ReferenceKey != INVALID_HANDLE_VALUE)
|
|
ZwClose(ReferenceKey);
|
|
if (ControlKey != INVALID_HANDLE_VALUE)
|
|
ZwClose(ControlKey);
|
|
if (DeviceBi)
|
|
ExFreePool(DeviceBi);
|
|
if (ReferenceBi)
|
|
ExFreePool(ReferenceBi);
|
|
if (bip)
|
|
ExFreePool(bip);
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
* @name IoRegisterDeviceInterface
|
|
* @implemented
|
|
*
|
|
* Registers a device interface class, if it has not been previously registered,
|
|
* and creates a new instance of the interface class, which a driver can
|
|
* subsequently enable for use by applications or other system components.
|
|
* Documented in WDK.
|
|
*
|
|
* @param PhysicalDeviceObject
|
|
* Points to an optional PDO that narrows the search to only the
|
|
* device interfaces of the device represented by the PDO
|
|
*
|
|
* @param InterfaceClassGuid
|
|
* Points to a class GUID specifying the device interface class
|
|
*
|
|
* @param ReferenceString
|
|
* Optional parameter, pointing to a unicode string. For a full
|
|
* description of this rather rarely used param (usually drivers
|
|
* pass NULL here) see WDK
|
|
*
|
|
* @param SymbolicLinkName
|
|
* Pointer to the resulting unicode string
|
|
*
|
|
* @return Usual NTSTATUS
|
|
*
|
|
* @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
|
|
* system thread
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN CONST GUID *InterfaceClassGuid,
|
|
IN PUNICODE_STRING ReferenceString OPTIONAL,
|
|
OUT PUNICODE_STRING SymbolicLinkName)
|
|
{
|
|
PUNICODE_STRING InstancePath;
|
|
UNICODE_STRING GuidString;
|
|
UNICODE_STRING SubKeyName;
|
|
UNICODE_STRING InterfaceKeyName;
|
|
UNICODE_STRING BaseKeyName;
|
|
UCHAR PdoNameInfoBuffer[sizeof(OBJECT_NAME_INFORMATION) + (256 * sizeof(WCHAR))];
|
|
POBJECT_NAME_INFORMATION PdoNameInfo = (POBJECT_NAME_INFORMATION)PdoNameInfoBuffer;
|
|
UNICODE_STRING DeviceInstance = RTL_CONSTANT_STRING(L"DeviceInstance");
|
|
UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
|
|
HANDLE ClassKey;
|
|
HANDLE InterfaceKey;
|
|
HANDLE SubKey;
|
|
ULONG StartIndex;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension;
|
|
|
|
ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
|
|
|
|
DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
|
|
PhysicalDeviceObject, ReferenceString);
|
|
|
|
/* Parameters must pass three border of checks */
|
|
DeviceObjectExtension = (PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension;
|
|
|
|
/* 1st level: Presence of a Device Node */
|
|
if (DeviceObjectExtension->DeviceNode == NULL)
|
|
{
|
|
DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
/* 2nd level: Presence of an non-zero length InstancePath */
|
|
if (DeviceObjectExtension->DeviceNode->InstancePath.Length == 0)
|
|
{
|
|
DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
/* 3rd level: Optional, based on WDK documentation */
|
|
if (ReferenceString != NULL)
|
|
{
|
|
/* Reference string must not contain path-separator symbols */
|
|
for (i = 0; i < ReferenceString->Length / sizeof(WCHAR); i++)
|
|
{
|
|
if ((ReferenceString->Buffer[i] == '\\') ||
|
|
(ReferenceString->Buffer[i] == '/'))
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
}
|
|
|
|
Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
|
|
Status = ObQueryNameString(
|
|
PhysicalDeviceObject,
|
|
PdoNameInfo,
|
|
sizeof(PdoNameInfoBuffer),
|
|
&i);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status);
|
|
return Status;
|
|
}
|
|
ASSERT(PdoNameInfo->Name.Length);
|
|
|
|
/* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
|
|
ASSERT(((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode);
|
|
InstancePath = &((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode->InstancePath;
|
|
BaseKeyName.Length = wcslen(BaseKeyString) * sizeof(WCHAR);
|
|
BaseKeyName.MaximumLength = BaseKeyName.Length
|
|
+ GuidString.Length;
|
|
BaseKeyName.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
BaseKeyName.MaximumLength);
|
|
if (!BaseKeyName.Buffer)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
wcscpy(BaseKeyName.Buffer, BaseKeyString);
|
|
RtlAppendUnicodeStringToString(&BaseKeyName, &GuidString);
|
|
|
|
/* Create BaseKeyName key in registry */
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&BaseKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
|
|
NULL, /* RootDirectory */
|
|
NULL); /* SecurityDescriptor */
|
|
|
|
Status = ZwCreateKey(
|
|
&ClassKey,
|
|
KEY_WRITE,
|
|
&ObjectAttributes,
|
|
0, /* TileIndex */
|
|
NULL, /* Class */
|
|
REG_OPTION_VOLATILE,
|
|
NULL); /* Disposition */
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
return Status;
|
|
}
|
|
|
|
/* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
|
|
InterfaceKeyName.Length = 0;
|
|
InterfaceKeyName.MaximumLength =
|
|
4 * sizeof(WCHAR) + /* 4 = size of ##?# */
|
|
InstancePath->Length +
|
|
sizeof(WCHAR) + /* 1 = size of # */
|
|
GuidString.Length;
|
|
InterfaceKeyName.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
InterfaceKeyName.MaximumLength);
|
|
if (!InterfaceKeyName.Buffer)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlAppendUnicodeToString(&InterfaceKeyName, L"##?#");
|
|
StartIndex = InterfaceKeyName.Length / sizeof(WCHAR);
|
|
RtlAppendUnicodeStringToString(&InterfaceKeyName, InstancePath);
|
|
for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
|
|
{
|
|
if (InterfaceKeyName.Buffer[StartIndex + i] == '\\')
|
|
InterfaceKeyName.Buffer[StartIndex + i] = '#';
|
|
}
|
|
RtlAppendUnicodeToString(&InterfaceKeyName, L"#");
|
|
RtlAppendUnicodeStringToString(&InterfaceKeyName, &GuidString);
|
|
|
|
/* Create the interface key in registry */
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&InterfaceKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
|
|
ClassKey,
|
|
NULL); /* SecurityDescriptor */
|
|
|
|
Status = ZwCreateKey(
|
|
&InterfaceKey,
|
|
KEY_WRITE,
|
|
&ObjectAttributes,
|
|
0, /* TileIndex */
|
|
NULL, /* Class */
|
|
REG_OPTION_VOLATILE,
|
|
NULL); /* Disposition */
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
|
|
ZwClose(ClassKey);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
return Status;
|
|
}
|
|
|
|
/* Write DeviceInstance entry. Value is InstancePath */
|
|
Status = ZwSetValueKey(
|
|
InterfaceKey,
|
|
&DeviceInstance,
|
|
0, /* TileIndex */
|
|
REG_SZ,
|
|
InstancePath->Buffer,
|
|
InstancePath->Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
|
|
ZwClose(InterfaceKey);
|
|
ZwClose(ClassKey);
|
|
ExFreePool(InterfaceKeyName.Buffer);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
return Status;
|
|
}
|
|
|
|
/* Create subkey. Name is #ReferenceString */
|
|
SubKeyName.Length = 0;
|
|
SubKeyName.MaximumLength = sizeof(WCHAR);
|
|
if (ReferenceString && ReferenceString->Length)
|
|
SubKeyName.MaximumLength += ReferenceString->Length;
|
|
SubKeyName.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
SubKeyName.MaximumLength);
|
|
if (!SubKeyName.Buffer)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
ZwClose(InterfaceKey);
|
|
ZwClose(ClassKey);
|
|
ExFreePool(InterfaceKeyName.Buffer);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlAppendUnicodeToString(&SubKeyName, L"#");
|
|
if (ReferenceString && ReferenceString->Length)
|
|
RtlAppendUnicodeStringToString(&SubKeyName, ReferenceString);
|
|
|
|
/* Create SubKeyName key in registry */
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&SubKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
InterfaceKey, /* RootDirectory */
|
|
NULL); /* SecurityDescriptor */
|
|
|
|
Status = ZwCreateKey(
|
|
&SubKey,
|
|
KEY_WRITE,
|
|
&ObjectAttributes,
|
|
0, /* TileIndex */
|
|
NULL, /* Class */
|
|
REG_OPTION_VOLATILE,
|
|
NULL); /* Disposition */
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
|
|
ZwClose(InterfaceKey);
|
|
ZwClose(ClassKey);
|
|
ExFreePool(InterfaceKeyName.Buffer);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
return Status;
|
|
}
|
|
|
|
/* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
|
|
SymbolicLinkName->Length = 0;
|
|
SymbolicLinkName->MaximumLength = SymbolicLinkName->Length
|
|
+ 4 * sizeof(WCHAR) /* 4 = size of \??\ */
|
|
+ InstancePath->Length
|
|
+ sizeof(WCHAR) /* 1 = size of # */
|
|
+ GuidString.Length
|
|
+ sizeof(WCHAR); /* final NULL */
|
|
if (ReferenceString && ReferenceString->Length)
|
|
SymbolicLinkName->MaximumLength += sizeof(WCHAR) + ReferenceString->Length;
|
|
SymbolicLinkName->Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
SymbolicLinkName->MaximumLength);
|
|
if (!SymbolicLinkName->Buffer)
|
|
{
|
|
DPRINT("ExAllocatePool() failed\n");
|
|
ZwClose(SubKey);
|
|
ZwClose(InterfaceKey);
|
|
ZwClose(ClassKey);
|
|
ExFreePool(InterfaceKeyName.Buffer);
|
|
ExFreePool(SubKeyName.Buffer);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlAppendUnicodeToString(SymbolicLinkName, L"\\??\\");
|
|
StartIndex = SymbolicLinkName->Length / sizeof(WCHAR);
|
|
RtlAppendUnicodeStringToString(SymbolicLinkName, InstancePath);
|
|
for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
|
|
{
|
|
if (SymbolicLinkName->Buffer[StartIndex + i] == '\\')
|
|
SymbolicLinkName->Buffer[StartIndex + i] = '#';
|
|
}
|
|
RtlAppendUnicodeToString(SymbolicLinkName, L"#");
|
|
RtlAppendUnicodeStringToString(SymbolicLinkName, &GuidString);
|
|
SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
|
|
|
|
/* Create symbolic link */
|
|
DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName, &PdoNameInfo->Name);
|
|
Status = IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name);
|
|
if (!NT_SUCCESS(Status) && ReferenceString == NULL)
|
|
{
|
|
DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", Status);
|
|
ZwClose(SubKey);
|
|
ZwClose(InterfaceKey);
|
|
ZwClose(ClassKey);
|
|
ExFreePool(SubKeyName.Buffer);
|
|
ExFreePool(InterfaceKeyName.Buffer);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
ExFreePool(SymbolicLinkName->Buffer);
|
|
return Status;
|
|
}
|
|
|
|
if (ReferenceString && ReferenceString->Length)
|
|
{
|
|
RtlAppendUnicodeToString(SymbolicLinkName, L"\\");
|
|
RtlAppendUnicodeStringToString(SymbolicLinkName, ReferenceString);
|
|
}
|
|
SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
|
|
|
|
/* Write symbolic link name in registry */
|
|
SymbolicLinkName->Buffer[1] = '\\';
|
|
Status = ZwSetValueKey(
|
|
SubKey,
|
|
&SymbolicLink,
|
|
0, /* TileIndex */
|
|
REG_SZ,
|
|
SymbolicLinkName->Buffer,
|
|
SymbolicLinkName->Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status);
|
|
ExFreePool(SymbolicLinkName->Buffer);
|
|
}
|
|
else
|
|
{
|
|
SymbolicLinkName->Buffer[1] = '?';
|
|
}
|
|
|
|
ZwClose(SubKey);
|
|
ZwClose(InterfaceKey);
|
|
ZwClose(ClassKey);
|
|
ExFreePool(SubKeyName.Buffer);
|
|
ExFreePool(InterfaceKeyName.Buffer);
|
|
ExFreePool(BaseKeyName.Buffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
* @name IoSetDeviceInterfaceState
|
|
* @implemented
|
|
*
|
|
* Enables or disables an instance of a previously registered device
|
|
* interface class.
|
|
* Documented in WDK.
|
|
*
|
|
* @param SymbolicLinkName
|
|
* Pointer to the string identifying instance to enable or disable
|
|
*
|
|
* @param Enable
|
|
* TRUE = enable, FALSE = disable
|
|
*
|
|
* @return Usual NTSTATUS
|
|
*
|
|
* @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
|
|
* system thread
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
|
|
IN BOOLEAN Enable)
|
|
{
|
|
PDEVICE_OBJECT PhysicalDeviceObject;
|
|
PFILE_OBJECT FileObject;
|
|
UNICODE_STRING GuidString;
|
|
UNICODE_STRING SymLink;
|
|
PWCHAR StartPosition;
|
|
PWCHAR EndPosition;
|
|
NTSTATUS Status;
|
|
LPCGUID EventGuid;
|
|
HANDLE InstanceHandle, ControlHandle;
|
|
UNICODE_STRING KeyName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG LinkedValue;
|
|
|
|
if (SymbolicLinkName == NULL)
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
|
|
DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
|
|
|
|
/* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
|
|
/* Get GUID from SymbolicLinkName */
|
|
StartPosition = wcschr(SymbolicLinkName->Buffer, L'{');
|
|
EndPosition = wcschr(SymbolicLinkName->Buffer, L'}');
|
|
if (!StartPosition ||!EndPosition || StartPosition > EndPosition)
|
|
{
|
|
DPRINT1("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
GuidString.Buffer = StartPosition;
|
|
GuidString.MaximumLength = GuidString.Length = (USHORT)((ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)StartPosition);
|
|
|
|
SymLink.Buffer = SymbolicLinkName->Buffer;
|
|
SymLink.MaximumLength = SymLink.Length = (USHORT)((ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)SymLink.Buffer);
|
|
DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
|
|
|
|
Status = OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName,
|
|
KEY_CREATE_SUB_KEY,
|
|
NULL,
|
|
NULL,
|
|
&InstanceHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
RtlInitUnicodeString(&KeyName, L"Control");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
InstanceHandle,
|
|
NULL);
|
|
Status = ZwCreateKey(&ControlHandle,
|
|
KEY_SET_VALUE,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
NULL);
|
|
ZwClose(InstanceHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to create the Control subkey\n");
|
|
return Status;
|
|
}
|
|
|
|
LinkedValue = (Enable ? 1 : 0);
|
|
|
|
RtlInitUnicodeString(&KeyName, L"Linked");
|
|
Status = ZwSetValueKey(ControlHandle,
|
|
&KeyName,
|
|
0,
|
|
REG_DWORD,
|
|
&LinkedValue,
|
|
sizeof(ULONG));
|
|
ZwClose(ControlHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to write the Linked value\n");
|
|
return Status;
|
|
}
|
|
|
|
/* Get pointer to the PDO */
|
|
Status = IoGetDeviceObjectPointer(
|
|
&SymLink,
|
|
0, /* DesiredAccess */
|
|
&FileObject,
|
|
&PhysicalDeviceObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("IoGetDeviceObjectPointer() failed with status 0x%08lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
|
|
IopNotifyPlugPlayNotification(
|
|
PhysicalDeviceObject,
|
|
EventCategoryDeviceInterfaceChange,
|
|
EventGuid,
|
|
&GuidString,
|
|
(PVOID)SymbolicLinkName);
|
|
|
|
ObDereferenceObject(FileObject);
|
|
DPRINT("Status %x\n", Status);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* EOF */
|