https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d617c8cf329960805eebf…
commit d617c8cf329960805eebff5d37907604e5d96db7
Author: Oleg Dubinskiy <oleg.dubinskij2013(a)yandex.ua>
AuthorDate: Sun Apr 4 20:11:14 2021 +0300
Commit: Oleg Dubinskiy <oleg.dubinskij30(a)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 (xnavara(a)volny.cz)
* Matthew Brace (ismarc(a)austin.rr.com)
* Hervé Poussineau (hpoussin(a)reactos.org)
+ * Oleg Dubinskiy (oleg.dubinskiy(a)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