https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d617c8cf329960805eebff5d37907604e5d96db7

commit d617c8cf329960805eebff5d37907604e5d96db7
Author:     Oleg Dubinskiy <oleg.dubinskij2...@yandex.ua>
AuthorDate: Sun Apr 4 20:11:14 2021 +0300
Commit:     Oleg Dubinskiy <oleg.dubinski...@gmail.com>
CommitDate: Sun Feb 2 23:30:38 2025 +0100

    [NTOS:IO] Implement IoGetDeviceInterfaceAlias
    Required by XP/2003 audio stack to be loaded properly.
    CORE-17361
---
 ntoskrnl/io/iomgr/deviface.c | 453 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 420 insertions(+), 33 deletions(-)

diff --git a/ntoskrnl/io/iomgr/deviface.c b/ntoskrnl/io/iomgr/deviface.c
index ed81e4407c4..a5f20efb50a 100644
--- a/ntoskrnl/io/iomgr/deviface.c
+++ b/ntoskrnl/io/iomgr/deviface.c
@@ -7,6 +7,7 @@
  * PROGRAMMERS:     Filip Navara (xnav...@volny.cz)
  *                  Matthew Brace (ism...@austin.rr.com)
  *                  Hervé Poussineau (hpous...@reactos.org)
+ *                  Oleg Dubinskiy (oleg.dubins...@reactos.org)
  */
 
 /* 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

Reply via email to