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