mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-26 19:33:31 +00:00
[NTOS:IO] Implement IoGetDeviceInterfaceAlias
Required by XP/2003 audio stack to be loaded properly. CORE-17361
This commit is contained in:
committed by
Oleg Dubinskiy
parent
6b2f00e70a
commit
d617c8cf32
+420
-33
@@ -7,6 +7,7 @@
|
||||
* PROGRAMMERS: Filip Navara ([email protected])
|
||||
* Matthew Brace ([email protected])
|
||||
* Hervé Poussineau ([email protected])
|
||||
* Oleg Dubinskiy ([email protected])
|
||||
*/
|
||||
|
||||
/* INCLUDES ******************************************************************/
|
||||
@@ -26,7 +27,265 @@ C_ASSERT(sizeof(L"{01234567-89ab-cdef-0123-456789abcdef}") == GUID_STRING_BYTES
|
||||
PDEVICE_OBJECT
|
||||
IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance);
|
||||
|
||||
static PWCHAR BaseKeyString = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
|
||||
static PCWSTR BaseKeyString = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Creates a new symbolic link from the specified format of the prefix, device string,
|
||||
* class GUID and reference string (if any).
|
||||
*
|
||||
* @param[in] DeviceString
|
||||
* Device string, placed after prefix and before GUID, for example ACPI#PNP0501#1#.
|
||||
*
|
||||
* @param[in] GuidString
|
||||
* Device interface class GUID represented by a string. Placed in curly brackets {},
|
||||
* after device string, should always be 38 characters long. For example,
|
||||
* {01234567-89ab-cdef-0123-456789abcdef}.
|
||||
*
|
||||
* @param[in] ReferenceString
|
||||
* Optional reference string, if any. Placed after GUID, at the end of symbolic link.
|
||||
* Usually contains human-readable subdevice name or class GUID.
|
||||
*
|
||||
* @param[in] UserModePrefixFormat
|
||||
* Specifies whether a new symbolic link should have either a kernel mode or user mode prefix.
|
||||
* TRUE for user mode prefix, FALSE for kernel mode.
|
||||
*
|
||||
* @param[out] SymbolicLinkName
|
||||
* Pointer to unicode string which receives created symbolic link.
|
||||
*
|
||||
* @return
|
||||
* STATUS_SUCCESS in case of success, or an NTSTATUS error code otherwise.
|
||||
**/
|
||||
static
|
||||
NTSTATUS
|
||||
IopBuildSymbolicLink(
|
||||
_In_ PCUNICODE_STRING DeviceString,
|
||||
_In_ PCUNICODE_STRING GuidString,
|
||||
_In_opt_ PCUNICODE_STRING ReferenceString,
|
||||
_In_ BOOLEAN UserModePrefixFormat,
|
||||
_Out_ PUNICODE_STRING SymbolicLinkName)
|
||||
{
|
||||
static const UNICODE_STRING KernelModePrefix = RTL_CONSTANT_STRING(L"\\??\\");
|
||||
static const UNICODE_STRING UserModePrefix = RTL_CONSTANT_STRING(L"\\\\?\\");
|
||||
static const UNICODE_STRING PathSep = RTL_CONSTANT_STRING(L"\\");
|
||||
UNICODE_STRING MungedDeviceString, SymbolicLink;
|
||||
NTSTATUS Status;
|
||||
ULONG Length;
|
||||
USHORT i;
|
||||
|
||||
/* Use a backslash if reference string is not specified */
|
||||
if (!ReferenceString)
|
||||
ReferenceString = &PathSep;
|
||||
|
||||
/* Duplicate the device string (to "munge" it) */
|
||||
Status = RtlDuplicateUnicodeString(0, DeviceString, &MungedDeviceString);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("RtlDuplicateUnicodeString() failed, Status 0x%08lx\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Replace all '\' by '#' in device string */
|
||||
for (i = 0; i < MungedDeviceString.Length / sizeof(WCHAR); i++)
|
||||
{
|
||||
if (MungedDeviceString.Buffer[i] == L'\\')
|
||||
MungedDeviceString.Buffer[i] = L'#';
|
||||
}
|
||||
|
||||
/* Calculate total length */
|
||||
Length = KernelModePrefix.Length // Same as UserModePrefix.Length
|
||||
+ MungedDeviceString.Length
|
||||
+ sizeof(L"#") + GuidString->Length
|
||||
+ ReferenceString->Length;
|
||||
ASSERT(Length <= MAXUSHORT);
|
||||
|
||||
/* Build up new symbolic link */
|
||||
SymbolicLink.Length = 0;
|
||||
SymbolicLink.MaximumLength = Length;
|
||||
SymbolicLink.Buffer = ExAllocatePoolWithTag(PagedPool, SymbolicLink.MaximumLength, TAG_IO);
|
||||
if (!SymbolicLink.Buffer)
|
||||
{
|
||||
DPRINT1("ExAllocatePoolWithTag() failed\n");
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
Status = RtlUnicodeStringPrintf(&SymbolicLink,
|
||||
L"%wZ%wZ#%wZ%wZ",
|
||||
UserModePrefixFormat ?
|
||||
&UserModePrefix : &KernelModePrefix,
|
||||
&MungedDeviceString,
|
||||
GuidString,
|
||||
ReferenceString);
|
||||
NT_VERIFY(NT_SUCCESS(Status));
|
||||
|
||||
DPRINT("New symbolic link is %wZ\n", &SymbolicLink);
|
||||
|
||||
*SymbolicLinkName = SymbolicLink;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Parses the specified symbolic link onto the 4 parts: prefix, device string,
|
||||
* class GUID and reference string.
|
||||
*
|
||||
* @param[in] SymbolicLinkName
|
||||
* Pointer to a symbolic link string to parse.
|
||||
*
|
||||
* @param[out] PrefixString
|
||||
* Receives prefix of symbolic link. Can be '\??\' for Kernel mode or '\\?\' for User mode.
|
||||
*
|
||||
* @param[out] MungedString
|
||||
* Receives device string. For example, ##?#ACPI#PNP0501#1#.
|
||||
*
|
||||
* @param[out] GuidString
|
||||
* Receives device interface class GUID string represented by device interface.
|
||||
* For example, {01234567-89ab-cdef-0123-456789abcdef}.
|
||||
*
|
||||
* @param[out] ReferenceString
|
||||
* Receives reference string, if any. Usually contains a human-readable
|
||||
* subdevice name or class GUID.
|
||||
*
|
||||
* @param[out] ReferenceStringPresent
|
||||
* Pointer to variable that indicates whether the reference string exists in symbolic link.
|
||||
* TRUE if it does, FALSE otherwise.
|
||||
*
|
||||
* @param[out] InterfaceClassGuid
|
||||
* Receives the interface class GUID to which specified symbolic link belongs to.
|
||||
*
|
||||
* @return
|
||||
* STATUS_SUCCESS in case of success, or an NTSTATUS error code otherwise.
|
||||
**/
|
||||
static
|
||||
NTSTATUS
|
||||
IopSeparateSymbolicLink(
|
||||
_In_ PCUNICODE_STRING SymbolicLinkName,
|
||||
_Out_opt_ PUNICODE_STRING PrefixString,
|
||||
_Out_opt_ PUNICODE_STRING MungedString,
|
||||
_Out_opt_ PUNICODE_STRING GuidString,
|
||||
_Out_opt_ PUNICODE_STRING ReferenceString,
|
||||
_Out_opt_ PBOOLEAN ReferenceStringPresent,
|
||||
_Out_opt_ LPGUID InterfaceClassGuid)
|
||||
{
|
||||
static const UNICODE_STRING KernelModePrefix = RTL_CONSTANT_STRING(L"\\??\\");
|
||||
static const UNICODE_STRING UserModePrefix = RTL_CONSTANT_STRING(L"\\\\?\\");
|
||||
UNICODE_STRING MungedStringReal, GuidStringReal, ReferenceStringReal;
|
||||
UNICODE_STRING LinkNameNoPrefix;
|
||||
USHORT i, ReferenceStringOffset;
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
DPRINT("Symbolic link is %wZ\n", SymbolicLinkName);
|
||||
|
||||
/* The symbolic link name looks like \??\ACPI#PNP0501#1#{GUID}\ReferenceString
|
||||
* Make sure it starts with the expected prefix. */
|
||||
if (!RtlPrefixUnicodeString(&KernelModePrefix, SymbolicLinkName, FALSE) &&
|
||||
!RtlPrefixUnicodeString(&UserModePrefix, SymbolicLinkName, FALSE))
|
||||
{
|
||||
DPRINT1("Invalid link name %wZ\n", SymbolicLinkName);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Sanity checks */
|
||||
ASSERT(KernelModePrefix.Length == UserModePrefix.Length);
|
||||
ASSERT(SymbolicLinkName->Length >= KernelModePrefix.Length);
|
||||
|
||||
/* Make a version without the prefix for further processing */
|
||||
LinkNameNoPrefix.Buffer = SymbolicLinkName->Buffer + KernelModePrefix.Length / sizeof(WCHAR);
|
||||
LinkNameNoPrefix.Length = SymbolicLinkName->Length - KernelModePrefix.Length;
|
||||
LinkNameNoPrefix.MaximumLength = LinkNameNoPrefix.Length;
|
||||
|
||||
DPRINT("Symbolic link without prefix is %wZ\n", &LinkNameNoPrefix);
|
||||
|
||||
/* Find the reference string, if any */
|
||||
for (i = 0; i < LinkNameNoPrefix.Length / sizeof(WCHAR); i++)
|
||||
{
|
||||
if (LinkNameNoPrefix.Buffer[i] == L'\\')
|
||||
break;
|
||||
}
|
||||
ReferenceStringOffset = i * sizeof(WCHAR);
|
||||
|
||||
/* The GUID is before the reference string or at the end */
|
||||
ASSERT(LinkNameNoPrefix.Length >= ReferenceStringOffset);
|
||||
if (ReferenceStringOffset < GUID_STRING_BYTES + sizeof(WCHAR))
|
||||
{
|
||||
DPRINT1("Invalid link name %wZ\n", SymbolicLinkName);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Get reference string (starts with \ after {GUID}) from link without prefix */
|
||||
ReferenceStringReal.Buffer = LinkNameNoPrefix.Buffer + ReferenceStringOffset / sizeof(WCHAR);
|
||||
ReferenceStringReal.Length = LinkNameNoPrefix.Length - ReferenceStringOffset;
|
||||
ReferenceStringReal.MaximumLength = ReferenceStringReal.Length;
|
||||
|
||||
DPRINT("Reference string is %wZ\n", &ReferenceStringReal);
|
||||
|
||||
/* Get GUID string (device class GUID in {} brackets) */
|
||||
GuidStringReal.Buffer = LinkNameNoPrefix.Buffer + (ReferenceStringOffset - GUID_STRING_BYTES) / sizeof(WCHAR);
|
||||
GuidStringReal.Length = GUID_STRING_BYTES;
|
||||
GuidStringReal.MaximumLength = GuidStringReal.Length;
|
||||
|
||||
DPRINT("GUID string is %wZ\n", &GuidStringReal);
|
||||
|
||||
/* Validate GUID string for:
|
||||
* 1) {} brackets at the start and the end;
|
||||
* 2) - separators in the appropriate places. */
|
||||
ASSERT(GuidStringReal.Buffer[0] == L'{');
|
||||
ASSERT(GuidStringReal.Buffer[GUID_STRING_CHARS - 1] == L'}');
|
||||
ASSERT(GuidStringReal.Buffer[9] == L'-');
|
||||
ASSERT(GuidStringReal.Buffer[14] == L'-');
|
||||
ASSERT(GuidStringReal.Buffer[19] == L'-');
|
||||
ASSERT(GuidStringReal.Buffer[24] == L'-');
|
||||
|
||||
if (MungedString)
|
||||
{
|
||||
/* Create a munged path string (looks like ACPI#PNP0501#1#) */
|
||||
MungedStringReal.Buffer = LinkNameNoPrefix.Buffer;
|
||||
MungedStringReal.Length = LinkNameNoPrefix.Length - ReferenceStringReal.Length - GUID_STRING_BYTES - sizeof(WCHAR);
|
||||
MungedStringReal.MaximumLength = MungedStringReal.Length;
|
||||
|
||||
DPRINT("Munged string is %wZ\n", &MungedStringReal);
|
||||
}
|
||||
|
||||
/* Store received parts if the parameters are not null */
|
||||
if (PrefixString)
|
||||
{
|
||||
PrefixString->Buffer = SymbolicLinkName->Buffer;
|
||||
PrefixString->Length = KernelModePrefix.Length; // Same as UserModePrefix.Length
|
||||
PrefixString->MaximumLength = PrefixString->Length;
|
||||
|
||||
DPRINT("Prefix string is %wZ\n", PrefixString);
|
||||
}
|
||||
|
||||
if (MungedString)
|
||||
*MungedString = MungedStringReal;
|
||||
|
||||
if (GuidString)
|
||||
*GuidString = GuidStringReal;
|
||||
|
||||
if (ReferenceString)
|
||||
{
|
||||
if (ReferenceStringReal.Length > sizeof(WCHAR))
|
||||
*ReferenceString = ReferenceStringReal;
|
||||
else
|
||||
RtlInitEmptyUnicodeString(ReferenceString, NULL, 0);
|
||||
}
|
||||
|
||||
if (ReferenceStringPresent)
|
||||
*ReferenceStringPresent = ReferenceStringReal.Length > sizeof(WCHAR);
|
||||
|
||||
if (InterfaceClassGuid)
|
||||
{
|
||||
/* Convert GUID string into a GUID and store it also */
|
||||
Status = RtlGUIDFromString(&GuidStringReal, InterfaceClassGuid);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("RtlGUIDFromString() failed, Status 0x%08lx\n", Status);
|
||||
}
|
||||
}
|
||||
|
||||
/* We're done */
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
@@ -275,38 +534,6 @@ IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName,
|
||||
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
|
||||
*
|
||||
@@ -412,6 +639,166 @@ cleanup:
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Returns the alias device interface of the specified device interface
|
||||
* instance, if the alias exists.
|
||||
*
|
||||
* @param[in] SymbolicLinkName
|
||||
* Pointer to a symbolic link string which identifies the device interface instance.
|
||||
*
|
||||
* @param[in] AliasInterfaceClassGuid
|
||||
* Pointer to a device interface class GUID.
|
||||
*
|
||||
* @param[out] AliasSymbolicLinkName
|
||||
* Pointer to unicode string which receives the alias symbolic link upon success.
|
||||
* Must be freed with RtlFreeUnicodeString after using.
|
||||
*
|
||||
* @return NTSTATUS values in case of errors, STATUS_SUCCESS otherwise.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
static const UNICODE_STRING UserModePrefix = RTL_CONSTANT_STRING(L"\\\\?\\");
|
||||
UNICODE_STRING AliasSymbolicLink = {0};
|
||||
UNICODE_STRING AliasGuidString = {0};
|
||||
UNICODE_STRING DeviceString = {0};
|
||||
UNICODE_STRING ReferenceString = {0};
|
||||
PKEY_VALUE_FULL_INFORMATION kvInfo;
|
||||
HANDLE DeviceKey, AliasInstanceKey;
|
||||
BOOLEAN UserModePrefixFormat;
|
||||
BOOLEAN ReferenceStringPresent = FALSE; /* Assuming no ref string by default */
|
||||
PVOID Buffer;
|
||||
NTSTATUS Status;
|
||||
|
||||
DPRINT("IoGetDeviceInterfaceAlias(%wZ, 0x%p)\n", SymbolicLinkName, AliasInterfaceClassGuid);
|
||||
|
||||
/* Sanity check */
|
||||
if (!SymbolicLinkName || !AliasInterfaceClassGuid)
|
||||
{
|
||||
DPRINT1("IoGetDeviceInterfaceAlias() invalid symbolic link or alias class GUID\n");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Convert alias GUID to a string */
|
||||
Status = RtlStringFromGUID(AliasInterfaceClassGuid, &AliasGuidString);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("RtlStringFromGUID() failed, Status 0x%08lx\n", Status);
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
DPRINT("Alias GUID is %wZ\n", &AliasGuidString);
|
||||
|
||||
/* Get the device instance string of existing symbolic link */
|
||||
Status = OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName,
|
||||
KEY_QUERY_VALUE,
|
||||
NULL,
|
||||
&DeviceKey,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to open device instance key for %wZ, Status 0x%08lx\n", SymbolicLinkName, Status);
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
Status = IopGetRegistryValue(DeviceKey, L"DeviceInstance", &kvInfo);
|
||||
ZwClose(DeviceKey);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed get device instance value, Status 0x%08lx\n", Status);
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
if (kvInfo->Type != REG_SZ || kvInfo->DataLength == 0 || kvInfo->DataLength > MAXUSHORT)
|
||||
{
|
||||
DPRINT1("Wrong or empty instance value\n");
|
||||
Status = STATUS_INVALID_PARAMETER;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
/* Convert received data to unicode string */
|
||||
Buffer = (PVOID)((ULONG_PTR)kvInfo + kvInfo->DataOffset);
|
||||
PnpRegSzToString(Buffer, kvInfo->DataLength, &DeviceString.Length);
|
||||
DeviceString.MaximumLength = DeviceString.Length;
|
||||
DeviceString.Buffer = Buffer;
|
||||
|
||||
/*
|
||||
* Separate symbolic link into 4 parts:
|
||||
* 1) prefix string (\??\ for kernel mode or \\?\ for user mode),
|
||||
* 2) munged path string (like ##?#ACPI#PNP0501#1#{GUID}),
|
||||
* 3) GUID string (the current GUID),
|
||||
* 4) reference string (goes after GUID, starts with '\').
|
||||
*
|
||||
* We need only reference string.
|
||||
*/
|
||||
Status = IopSeparateSymbolicLink(SymbolicLinkName,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&ReferenceString,
|
||||
&ReferenceStringPresent,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to separate symbolic link %wZ, Status 0x%08lx\n", SymbolicLinkName, Status);
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
DPRINT("Device string is '%wZ'\n", &DeviceString);
|
||||
|
||||
/* Does symbolic link have kernel mode "\??\" or user mode "\\?\" prefix format? */
|
||||
UserModePrefixFormat = RtlPrefixUnicodeString(&UserModePrefix, SymbolicLinkName, FALSE);
|
||||
|
||||
/* Build up new symbolic link with alias GUID */
|
||||
Status = IopBuildSymbolicLink(&DeviceString,
|
||||
&AliasGuidString,
|
||||
ReferenceStringPresent ? &ReferenceString : NULL,
|
||||
UserModePrefixFormat,
|
||||
&AliasSymbolicLink);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to build alias symbolic link, Status 0x%08lx\n", Status);
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
/* Make sure that alias symbolic link key exists in registry */
|
||||
Status = OpenRegistryHandlesFromSymbolicLink(&AliasSymbolicLink,
|
||||
KEY_READ,
|
||||
NULL,
|
||||
NULL,
|
||||
&AliasInstanceKey);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to open alias symbolic link key, Status 0x%08lx\n", Status);
|
||||
goto Quit;
|
||||
}
|
||||
ZwClose(AliasInstanceKey);
|
||||
|
||||
/* We're done */
|
||||
DPRINT("IoGetDeviceInterfaceAlias(): alias symbolic link %wZ\n", &AliasSymbolicLink);
|
||||
*AliasSymbolicLinkName = AliasSymbolicLink;
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
Quit:
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
if (AliasSymbolicLink.Buffer)
|
||||
RtlFreeUnicodeString(&AliasSymbolicLink);
|
||||
}
|
||||
|
||||
if (AliasGuidString.Buffer)
|
||||
RtlFreeUnicodeString(&AliasGuidString);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/*++
|
||||
* @name IoGetDeviceInterfaces
|
||||
* @implemented
|
||||
|
||||
Reference in New Issue
Block a user