2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/deviface.c
5 * PURPOSE: Device interface functions
7 * PROGRAMMERS: Filip Navara (xnavara@volny.cz)
8 * Matthew Brace (ismarc@austin.rr.com)
9 * Hervé Poussineau (hpoussin@reactos.org)
12 /* INCLUDES ******************************************************************/
19 /* FUNCTIONS *****************************************************************/
22 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance
);
24 static PWCHAR BaseKeyString
= L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
28 OpenRegistryHandlesFromSymbolicLink(IN PUNICODE_STRING SymbolicLinkName
,
29 IN ACCESS_MASK DesiredAccess
,
30 IN OPTIONAL PHANDLE GuidKey
,
31 IN OPTIONAL PHANDLE DeviceKey
,
32 IN OPTIONAL PHANDLE InstanceKey
)
34 OBJECT_ATTRIBUTES ObjectAttributes
;
35 WCHAR PathBuffer
[MAX_PATH
];
36 UNICODE_STRING BaseKeyU
;
37 UNICODE_STRING GuidString
, SubKeyName
, ReferenceString
;
38 PWCHAR StartPosition
, EndPosition
;
40 PHANDLE GuidKeyRealP
, DeviceKeyRealP
, InstanceKeyRealP
;
41 HANDLE GuidKeyReal
, DeviceKeyReal
, InstanceKeyReal
;
44 SubKeyName
.Buffer
= NULL
;
47 GuidKeyRealP
= GuidKey
;
49 GuidKeyRealP
= &GuidKeyReal
;
51 if (DeviceKey
!= NULL
)
52 DeviceKeyRealP
= DeviceKey
;
54 DeviceKeyRealP
= &DeviceKeyReal
;
56 if (InstanceKey
!= NULL
)
57 InstanceKeyRealP
= InstanceKey
;
59 InstanceKeyRealP
= &InstanceKeyReal
;
61 *GuidKeyRealP
= INVALID_HANDLE_VALUE
;
62 *DeviceKeyRealP
= INVALID_HANDLE_VALUE
;
63 *InstanceKeyRealP
= INVALID_HANDLE_VALUE
;
65 BaseKeyU
.Buffer
= PathBuffer
;
67 BaseKeyU
.MaximumLength
= MAX_PATH
* sizeof(WCHAR
);
69 RtlAppendUnicodeToString(&BaseKeyU
, BaseKeyString
);
71 /* Open the DeviceClasses key */
72 InitializeObjectAttributes(&ObjectAttributes
,
74 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
77 Status
= ZwOpenKey(&ClassesKey
,
78 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
80 if (!NT_SUCCESS(Status
))
82 DPRINT1("Failed to open %wZ\n", &BaseKeyU
);
86 StartPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'{');
87 EndPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'}');
88 if (!StartPosition
|| !EndPosition
|| StartPosition
> EndPosition
)
90 DPRINT1("Bad symbolic link: %wZ\n", SymbolicLinkName
);
91 return STATUS_INVALID_PARAMETER_1
;
93 GuidString
.Buffer
= StartPosition
;
94 GuidString
.MaximumLength
= GuidString
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)StartPosition
);
96 InitializeObjectAttributes(&ObjectAttributes
,
98 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
101 Status
= ZwCreateKey(GuidKeyRealP
,
102 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
109 if (!NT_SUCCESS(Status
))
111 DPRINT1("Failed to open %wZ%wZ (%x)\n", &BaseKeyU
, &GuidString
, Status
);
115 SubKeyName
.MaximumLength
= SymbolicLinkName
->Length
+ sizeof(WCHAR
);
116 SubKeyName
.Length
= 0;
117 SubKeyName
.Buffer
= ExAllocatePool(PagedPool
, SubKeyName
.MaximumLength
);
118 if (!SubKeyName
.Buffer
)
120 Status
= STATUS_INSUFFICIENT_RESOURCES
;
124 RtlAppendUnicodeStringToString(&SubKeyName
,
127 SubKeyName
.Buffer
[SubKeyName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
129 SubKeyName
.Buffer
[0] = L
'#';
130 SubKeyName
.Buffer
[1] = L
'#';
131 SubKeyName
.Buffer
[2] = L
'?';
132 SubKeyName
.Buffer
[3] = L
'#';
134 ReferenceString
.Buffer
= wcsrchr(SubKeyName
.Buffer
, '\\');
135 if (ReferenceString
.Buffer
!= NULL
)
137 ReferenceString
.Buffer
[0] = L
'#';
139 SubKeyName
.Length
= (USHORT
)((ULONG_PTR
)(ReferenceString
.Buffer
) - (ULONG_PTR
)SubKeyName
.Buffer
);
140 ReferenceString
.Length
= SymbolicLinkName
->Length
- SubKeyName
.Length
;
144 RtlInitUnicodeString(&ReferenceString
, L
"#");
147 InitializeObjectAttributes(&ObjectAttributes
,
149 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
152 Status
= ZwCreateKey(DeviceKeyRealP
,
153 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
159 if (!NT_SUCCESS(Status
))
161 DPRINT1("Failed to open %wZ%wZ\\%wZ Status %x\n", &BaseKeyU
, &GuidString
, &SubKeyName
, Status
);
165 InitializeObjectAttributes(&ObjectAttributes
,
167 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
170 Status
= ZwCreateKey(InstanceKeyRealP
,
177 if (!NT_SUCCESS(Status
))
179 DPRINT1("Failed to open %wZ%wZ\\%wZ%\\%wZ (%x)\n", &BaseKeyU
, &GuidString
, &SubKeyName
, &ReferenceString
, Status
);
183 Status
= STATUS_SUCCESS
;
186 if (SubKeyName
.Buffer
!= NULL
)
187 ExFreePool(SubKeyName
.Buffer
);
189 if (NT_SUCCESS(Status
))
192 ZwClose(*GuidKeyRealP
);
195 ZwClose(*DeviceKeyRealP
);
198 ZwClose(*InstanceKeyRealP
);
202 if (*GuidKeyRealP
!= INVALID_HANDLE_VALUE
)
203 ZwClose(*GuidKeyRealP
);
205 if (*DeviceKeyRealP
!= INVALID_HANDLE_VALUE
)
206 ZwClose(*DeviceKeyRealP
);
208 if (*InstanceKeyRealP
!= INVALID_HANDLE_VALUE
)
209 ZwClose(*InstanceKeyRealP
);
215 * @name IoOpenDeviceInterfaceRegistryKey
218 * Provides a handle to the device's interface instance registry key.
221 * @param SymbolicLinkName
222 * Pointer to a string which identifies the device interface instance
224 * @param DesiredAccess
225 * Desired ACCESS_MASK used to access the key (like KEY_READ,
228 * @param DeviceInterfaceKey
229 * If a call has been succesfull, a handle to the registry key
230 * will be stored there
232 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
233 * otherwise (see WDK for details)
235 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
240 IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName
,
241 IN ACCESS_MASK DesiredAccess
,
242 OUT PHANDLE DeviceInterfaceKey
)
244 HANDLE InstanceKey
, DeviceParametersKey
;
246 OBJECT_ATTRIBUTES ObjectAttributes
;
247 UNICODE_STRING DeviceParametersU
= RTL_CONSTANT_STRING(L
"Device Parameters");
249 Status
= OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName
,
254 if (!NT_SUCCESS(Status
))
257 InitializeObjectAttributes(&ObjectAttributes
,
259 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
262 Status
= ZwCreateKey(&DeviceParametersKey
,
267 REG_OPTION_NON_VOLATILE
,
269 ZwClose(InstanceKey
);
271 if (NT_SUCCESS(Status
))
272 *DeviceInterfaceKey
= DeviceParametersKey
;
278 * @name IoGetDeviceInterfaceAlias
281 * Returns the alias device interface of the specified device interface
282 * instance, if the alias exists.
285 * @param SymbolicLinkName
286 * Pointer to a string which identifies the device interface instance
288 * @param AliasInterfaceClassGuid
291 * @param AliasSymbolicLinkName
294 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
295 * otherwise (see WDK for details)
297 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
302 IoGetDeviceInterfaceAlias(IN PUNICODE_STRING SymbolicLinkName
,
303 IN CONST GUID
*AliasInterfaceClassGuid
,
304 OUT PUNICODE_STRING AliasSymbolicLinkName
)
306 return STATUS_NOT_IMPLEMENTED
;
310 * @name IopOpenInterfaceKey
312 * Returns the alias device interface of the specified device interface
314 * @param InterfaceClassGuid
317 * @param DesiredAccess
320 * @param pInterfaceKey
323 * @return Usual NTSTATUS
329 IopOpenInterfaceKey(IN CONST GUID
*InterfaceClassGuid
,
330 IN ACCESS_MASK DesiredAccess
,
331 OUT HANDLE
*pInterfaceKey
)
333 UNICODE_STRING LocalMachine
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\");
334 UNICODE_STRING GuidString
;
335 UNICODE_STRING KeyName
;
336 OBJECT_ATTRIBUTES ObjectAttributes
;
337 HANDLE InterfaceKey
= INVALID_HANDLE_VALUE
;
340 GuidString
.Buffer
= KeyName
.Buffer
= NULL
;
342 Status
= RtlStringFromGUID(InterfaceClassGuid
, &GuidString
);
343 if (!NT_SUCCESS(Status
))
345 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status
);
350 KeyName
.MaximumLength
= LocalMachine
.Length
+ ((USHORT
)wcslen(REGSTR_PATH_DEVICE_CLASSES
) + 1) * sizeof(WCHAR
) + GuidString
.Length
;
351 KeyName
.Buffer
= ExAllocatePool(PagedPool
, KeyName
.MaximumLength
);
354 DPRINT("ExAllocatePool() failed\n");
355 Status
= STATUS_INSUFFICIENT_RESOURCES
;
359 Status
= RtlAppendUnicodeStringToString(&KeyName
, &LocalMachine
);
360 if (!NT_SUCCESS(Status
))
362 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
365 Status
= RtlAppendUnicodeToString(&KeyName
, REGSTR_PATH_DEVICE_CLASSES
);
366 if (!NT_SUCCESS(Status
))
368 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status
);
371 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
372 if (!NT_SUCCESS(Status
))
374 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status
);
377 Status
= RtlAppendUnicodeStringToString(&KeyName
, &GuidString
);
378 if (!NT_SUCCESS(Status
))
380 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
384 InitializeObjectAttributes(
387 OBJ_CASE_INSENSITIVE
,
394 if (!NT_SUCCESS(Status
))
396 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
400 *pInterfaceKey
= InterfaceKey
;
401 Status
= STATUS_SUCCESS
;
404 if (!NT_SUCCESS(Status
))
406 if (InterfaceKey
!= INVALID_HANDLE_VALUE
)
407 ZwClose(InterfaceKey
);
409 RtlFreeUnicodeString(&GuidString
);
410 RtlFreeUnicodeString(&KeyName
);
415 * @name IoGetDeviceInterfaces
418 * Returns a list of device interfaces of a particular device interface class.
421 * @param InterfaceClassGuid
422 * Points to a class GUID specifying the device interface class
424 * @param PhysicalDeviceObject
425 * Points to an optional PDO that narrows the search to only the
426 * device interfaces of the device represented by the PDO
429 * Specifies flags that modify the search for device interfaces. The
430 * DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
431 * returned symbolic links should contain also disabled device
432 * interfaces in addition to the enabled ones.
434 * @param SymbolicLinkList
435 * Points to a character pointer that is filled in on successful return
436 * with a list of unicode strings identifying the device interfaces
437 * that match the search criteria. The newly allocated buffer contains
438 * a list of symbolic link names. Each unicode string in the list is
439 * null-terminated; the end of the whole list is marked by an additional
440 * NULL. The caller is responsible for freeing the buffer (ExFreePool)
441 * when it is no longer needed.
442 * If no device interfaces match the search criteria, this routine
443 * returns STATUS_SUCCESS and the string contains a single NULL
446 * @return Usual NTSTATUS
453 IoGetDeviceInterfaces(IN CONST GUID
*InterfaceClassGuid
,
454 IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL
,
456 OUT PWSTR
*SymbolicLinkList
)
458 UNICODE_STRING Control
= RTL_CONSTANT_STRING(L
"Control");
459 UNICODE_STRING SymbolicLink
= RTL_CONSTANT_STRING(L
"SymbolicLink");
460 HANDLE InterfaceKey
= INVALID_HANDLE_VALUE
;
461 HANDLE DeviceKey
= INVALID_HANDLE_VALUE
;
462 HANDLE ReferenceKey
= INVALID_HANDLE_VALUE
;
463 HANDLE ControlKey
= INVALID_HANDLE_VALUE
;
464 PKEY_BASIC_INFORMATION DeviceBi
= NULL
;
465 PKEY_BASIC_INFORMATION ReferenceBi
= NULL
;
466 PKEY_VALUE_PARTIAL_INFORMATION bip
= NULL
;
467 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
468 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension
;
469 PUNICODE_STRING InstanceDevicePath
= NULL
;
470 UNICODE_STRING KeyName
;
471 OBJECT_ATTRIBUTES ObjectAttributes
;
472 BOOLEAN FoundRightPDO
= FALSE
;
473 ULONG i
= 0, j
, Size
, NeededLength
, ActualLength
, LinkedValue
;
474 UNICODE_STRING ReturnBuffer
= { 0, 0, NULL
};
479 if (PhysicalDeviceObject
!= NULL
)
481 /* Parameters must pass three border of checks */
482 DeviceObjectExtension
= (PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
;
484 /* 1st level: Presence of a Device Node */
485 if (DeviceObjectExtension
->DeviceNode
== NULL
)
487 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject
);
488 return STATUS_INVALID_DEVICE_REQUEST
;
491 /* 2nd level: Presence of an non-zero length InstancePath */
492 if (DeviceObjectExtension
->DeviceNode
->InstancePath
.Length
== 0)
494 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject
);
495 return STATUS_INVALID_DEVICE_REQUEST
;
498 InstanceDevicePath
= &DeviceObjectExtension
->DeviceNode
->InstancePath
;
502 Status
= IopOpenInterfaceKey(InterfaceClassGuid
, KEY_ENUMERATE_SUB_KEYS
, &InterfaceKey
);
503 if (!NT_SUCCESS(Status
))
505 DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status
);
509 /* Enumerate subkeys (i.e. the different device objects) */
512 Status
= ZwEnumerateKey(
519 if (Status
== STATUS_NO_MORE_ENTRIES
)
523 else if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
525 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
529 DeviceBi
= ExAllocatePool(PagedPool
, Size
);
532 DPRINT("ExAllocatePool() failed\n");
533 Status
= STATUS_INSUFFICIENT_RESOURCES
;
536 Status
= ZwEnumerateKey(
543 if (!NT_SUCCESS(Status
))
545 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
549 /* Open device key */
550 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)DeviceBi
->NameLength
;
551 KeyName
.Buffer
= DeviceBi
->Name
;
552 InitializeObjectAttributes(
555 OBJ_CASE_INSENSITIVE
,
560 KEY_ENUMERATE_SUB_KEYS
,
562 if (!NT_SUCCESS(Status
))
564 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
568 if (PhysicalDeviceObject
)
570 /* Check if we are on the right physical device object,
571 * by reading the DeviceInstance string
573 RtlInitUnicodeString(&KeyName
, L
"DeviceInstance");
574 Status
= ZwQueryValueKey(DeviceKey
, &KeyName
, KeyValuePartialInformation
, NULL
, 0, &NeededLength
);
575 if (Status
== STATUS_BUFFER_TOO_SMALL
)
577 ActualLength
= NeededLength
;
578 PartialInfo
= ExAllocatePool(NonPagedPool
, ActualLength
);
581 Status
= STATUS_INSUFFICIENT_RESOURCES
;
585 Status
= ZwQueryValueKey(DeviceKey
, &KeyName
, KeyValuePartialInformation
, PartialInfo
, ActualLength
, &NeededLength
);
586 if (!NT_SUCCESS(Status
))
588 DPRINT1("ZwQueryValueKey #2 failed (%x)\n", Status
);
589 ExFreePool(PartialInfo
);
592 if (PartialInfo
->DataLength
== InstanceDevicePath
->Length
)
594 if (RtlCompareMemory(PartialInfo
->Data
, InstanceDevicePath
->Buffer
, InstanceDevicePath
->Length
) == InstanceDevicePath
->Length
)
596 /* found right pdo */
597 FoundRightPDO
= TRUE
;
600 ExFreePool(PartialInfo
);
615 /* Enumerate subkeys (ie the different reference strings) */
619 Status
= ZwEnumerateKey(
626 if (Status
== STATUS_NO_MORE_ENTRIES
)
630 else if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
632 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
636 ReferenceBi
= ExAllocatePool(PagedPool
, Size
);
639 DPRINT("ExAllocatePool() failed\n");
640 Status
= STATUS_INSUFFICIENT_RESOURCES
;
643 Status
= ZwEnumerateKey(
650 if (!NT_SUCCESS(Status
))
652 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
656 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)ReferenceBi
->NameLength
;
657 KeyName
.Buffer
= ReferenceBi
->Name
;
658 if (RtlEqualUnicodeString(&KeyName
, &Control
, TRUE
))
660 /* Skip Control subkey */
661 goto NextReferenceString
;
664 /* Open reference key */
665 InitializeObjectAttributes(
668 OBJ_CASE_INSENSITIVE
,
675 if (!NT_SUCCESS(Status
))
677 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
681 if (!(Flags
& DEVICE_INTERFACE_INCLUDE_NONACTIVE
))
683 /* We have to check if the interface is enabled, by
684 * reading the Linked value in the Control subkey
686 InitializeObjectAttributes(
689 OBJ_CASE_INSENSITIVE
,
696 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
698 /* That's OK. The key doesn't exist (yet) because
699 * the interface is not activated.
701 goto NextReferenceString
;
703 else if (!NT_SUCCESS(Status
))
705 DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status
);
709 RtlInitUnicodeString(&KeyName
, L
"Linked");
710 Status
= ZwQueryValueKey(ControlKey
,
712 KeyValuePartialInformation
,
716 if (Status
== STATUS_BUFFER_TOO_SMALL
)
718 ActualLength
= NeededLength
;
719 PartialInfo
= ExAllocatePool(NonPagedPool
, ActualLength
);
722 Status
= STATUS_INSUFFICIENT_RESOURCES
;
726 Status
= ZwQueryValueKey(ControlKey
,
728 KeyValuePartialInformation
,
732 if (!NT_SUCCESS(Status
))
734 DPRINT1("ZwQueryValueKey #2 failed (%x)\n", Status
);
735 ExFreePool(PartialInfo
);
739 if (PartialInfo
->Type
!= REG_DWORD
|| PartialInfo
->DataLength
!= sizeof(ULONG
))
741 DPRINT1("Bad registry read\n");
742 ExFreePool(PartialInfo
);
746 RtlCopyMemory(&LinkedValue
,
748 PartialInfo
->DataLength
);
750 ExFreePool(PartialInfo
);
751 if (LinkedValue
== 0)
753 /* This interface isn't active */
754 goto NextReferenceString
;
759 DPRINT1("ZwQueryValueKey #1 failed (%x)\n", Status
);
764 /* Read the SymbolicLink string and add it into SymbolicLinkList */
765 Status
= ZwQueryValueKey(
768 KeyValuePartialInformation
,
772 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
774 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status
);
777 bip
= ExAllocatePool(PagedPool
, Size
);
780 DPRINT("ExAllocatePool() failed\n");
781 Status
= STATUS_INSUFFICIENT_RESOURCES
;
784 Status
= ZwQueryValueKey(
787 KeyValuePartialInformation
,
791 if (!NT_SUCCESS(Status
))
793 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status
);
796 else if (bip
->Type
!= REG_SZ
)
798 DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip
->Type
, REG_SZ
);
799 Status
= STATUS_UNSUCCESSFUL
;
802 else if (bip
->DataLength
< 5 * sizeof(WCHAR
))
804 DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip
->DataLength
, 5 * sizeof(WCHAR
));
805 Status
= STATUS_UNSUCCESSFUL
;
808 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)bip
->DataLength
;
809 KeyName
.Buffer
= (PWSTR
)bip
->Data
;
811 /* Fixup the prefix (from "\\?\") */
812 RtlCopyMemory(KeyName
.Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
814 /* Add new symbolic link to symbolic link list */
815 if (ReturnBuffer
.Length
+ KeyName
.Length
+ sizeof(WCHAR
) > ReturnBuffer
.MaximumLength
)
818 ReturnBuffer
.MaximumLength
= (USHORT
)max(2 * ReturnBuffer
.MaximumLength
,
819 (USHORT
)(ReturnBuffer
.Length
+
822 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
825 DPRINT("ExAllocatePool() failed\n");
826 Status
= STATUS_INSUFFICIENT_RESOURCES
;
829 if (ReturnBuffer
.Buffer
)
831 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
832 ExFreePool(ReturnBuffer
.Buffer
);
834 ReturnBuffer
.Buffer
= NewBuffer
;
836 DPRINT("Adding symbolic link %wZ\n", &KeyName
);
837 Status
= RtlAppendUnicodeStringToString(&ReturnBuffer
, &KeyName
);
838 if (!NT_SUCCESS(Status
))
840 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
843 /* RtlAppendUnicodeStringToString added a NULL at the end of the
844 * destination string, but didn't increase the Length field.
847 ReturnBuffer
.Length
+= sizeof(WCHAR
);
850 ExFreePool(ReferenceBi
);
855 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
857 ZwClose(ReferenceKey
);
858 ReferenceKey
= INVALID_HANDLE_VALUE
;
860 if (ControlKey
!= INVALID_HANDLE_VALUE
)
863 ControlKey
= INVALID_HANDLE_VALUE
;
868 /* No need to go further, as we already have found what we searched */
872 ExFreePool(DeviceBi
);
875 DeviceKey
= INVALID_HANDLE_VALUE
;
878 /* Add final NULL to ReturnBuffer */
879 ASSERT(ReturnBuffer
.Length
<= ReturnBuffer
.MaximumLength
);
880 if (ReturnBuffer
.Length
>= ReturnBuffer
.MaximumLength
)
883 ReturnBuffer
.MaximumLength
+= sizeof(WCHAR
);
884 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
887 DPRINT("ExAllocatePool() failed\n");
888 Status
= STATUS_INSUFFICIENT_RESOURCES
;
891 if (ReturnBuffer
.Buffer
)
893 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
894 ExFreePool(ReturnBuffer
.Buffer
);
896 ReturnBuffer
.Buffer
= NewBuffer
;
898 ReturnBuffer
.Buffer
[ReturnBuffer
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
899 *SymbolicLinkList
= ReturnBuffer
.Buffer
;
900 Status
= STATUS_SUCCESS
;
903 if (!NT_SUCCESS(Status
) && ReturnBuffer
.Buffer
)
904 ExFreePool(ReturnBuffer
.Buffer
);
905 if (InterfaceKey
!= INVALID_HANDLE_VALUE
)
906 ZwClose(InterfaceKey
);
907 if (DeviceKey
!= INVALID_HANDLE_VALUE
)
909 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
910 ZwClose(ReferenceKey
);
911 if (ControlKey
!= INVALID_HANDLE_VALUE
)
914 ExFreePool(DeviceBi
);
916 ExFreePool(ReferenceBi
);
923 * @name IoRegisterDeviceInterface
926 * Registers a device interface class, if it has not been previously registered,
927 * and creates a new instance of the interface class, which a driver can
928 * subsequently enable for use by applications or other system components.
931 * @param PhysicalDeviceObject
932 * Points to an optional PDO that narrows the search to only the
933 * device interfaces of the device represented by the PDO
935 * @param InterfaceClassGuid
936 * Points to a class GUID specifying the device interface class
938 * @param ReferenceString
939 * Optional parameter, pointing to a unicode string. For a full
940 * description of this rather rarely used param (usually drivers
941 * pass NULL here) see WDK
943 * @param SymbolicLinkName
944 * Pointer to the resulting unicode string
946 * @return Usual NTSTATUS
948 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
954 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject
,
955 IN CONST GUID
*InterfaceClassGuid
,
956 IN PUNICODE_STRING ReferenceString OPTIONAL
,
957 OUT PUNICODE_STRING SymbolicLinkName
)
959 PUNICODE_STRING InstancePath
;
960 UNICODE_STRING GuidString
;
961 UNICODE_STRING SubKeyName
;
962 UNICODE_STRING InterfaceKeyName
;
963 UNICODE_STRING BaseKeyName
;
964 UCHAR PdoNameInfoBuffer
[sizeof(OBJECT_NAME_INFORMATION
) + (256 * sizeof(WCHAR
))];
965 POBJECT_NAME_INFORMATION PdoNameInfo
= (POBJECT_NAME_INFORMATION
)PdoNameInfoBuffer
;
966 UNICODE_STRING DeviceInstance
= RTL_CONSTANT_STRING(L
"DeviceInstance");
967 UNICODE_STRING SymbolicLink
= RTL_CONSTANT_STRING(L
"SymbolicLink");
972 OBJECT_ATTRIBUTES ObjectAttributes
;
974 NTSTATUS Status
, SymLinkStatus
;
975 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension
;
977 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL
);
979 DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
980 PhysicalDeviceObject
, ReferenceString
);
982 /* Parameters must pass three border of checks */
983 DeviceObjectExtension
= (PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
;
985 /* 1st level: Presence of a Device Node */
986 if (DeviceObjectExtension
->DeviceNode
== NULL
)
988 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject
);
989 return STATUS_INVALID_DEVICE_REQUEST
;
992 /* 2nd level: Presence of an non-zero length InstancePath */
993 if (DeviceObjectExtension
->DeviceNode
->InstancePath
.Length
== 0)
995 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject
);
996 return STATUS_INVALID_DEVICE_REQUEST
;
999 /* 3rd level: Optional, based on WDK documentation */
1000 if (ReferenceString
!= NULL
)
1002 /* Reference string must not contain path-separator symbols */
1003 for (i
= 0; i
< ReferenceString
->Length
/ sizeof(WCHAR
); i
++)
1005 if ((ReferenceString
->Buffer
[i
] == '\\') ||
1006 (ReferenceString
->Buffer
[i
] == '/'))
1007 return STATUS_INVALID_DEVICE_REQUEST
;
1011 Status
= RtlStringFromGUID(InterfaceClassGuid
, &GuidString
);
1012 if (!NT_SUCCESS(Status
))
1014 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status
);
1018 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
1019 Status
= ObQueryNameString(
1020 PhysicalDeviceObject
,
1022 sizeof(PdoNameInfoBuffer
),
1024 if (!NT_SUCCESS(Status
))
1026 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status
);
1029 ASSERT(PdoNameInfo
->Name
.Length
);
1031 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
1032 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
);
1033 InstancePath
= &((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
->InstancePath
;
1034 BaseKeyName
.Length
= (USHORT
)wcslen(BaseKeyString
) * sizeof(WCHAR
);
1035 BaseKeyName
.MaximumLength
= BaseKeyName
.Length
1036 + GuidString
.Length
;
1037 BaseKeyName
.Buffer
= ExAllocatePool(
1039 BaseKeyName
.MaximumLength
);
1040 if (!BaseKeyName
.Buffer
)
1042 DPRINT("ExAllocatePool() failed\n");
1043 return STATUS_INSUFFICIENT_RESOURCES
;
1045 wcscpy(BaseKeyName
.Buffer
, BaseKeyString
);
1046 RtlAppendUnicodeStringToString(&BaseKeyName
, &GuidString
);
1048 /* Create BaseKeyName key in registry */
1049 InitializeObjectAttributes(
1052 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
1053 NULL
, /* RootDirectory */
1054 NULL
); /* SecurityDescriptor */
1056 Status
= ZwCreateKey(
1062 REG_OPTION_VOLATILE
,
1063 NULL
); /* Disposition */
1065 if (!NT_SUCCESS(Status
))
1067 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1068 ExFreePool(BaseKeyName
.Buffer
);
1072 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
1073 InterfaceKeyName
.Length
= 0;
1074 InterfaceKeyName
.MaximumLength
=
1075 4 * sizeof(WCHAR
) + /* 4 = size of ##?# */
1076 InstancePath
->Length
+
1077 sizeof(WCHAR
) + /* 1 = size of # */
1079 InterfaceKeyName
.Buffer
= ExAllocatePool(
1081 InterfaceKeyName
.MaximumLength
);
1082 if (!InterfaceKeyName
.Buffer
)
1084 DPRINT("ExAllocatePool() failed\n");
1085 return STATUS_INSUFFICIENT_RESOURCES
;
1088 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"##?#");
1089 StartIndex
= InterfaceKeyName
.Length
/ sizeof(WCHAR
);
1090 RtlAppendUnicodeStringToString(&InterfaceKeyName
, InstancePath
);
1091 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1093 if (InterfaceKeyName
.Buffer
[StartIndex
+ i
] == '\\')
1094 InterfaceKeyName
.Buffer
[StartIndex
+ i
] = '#';
1096 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"#");
1097 RtlAppendUnicodeStringToString(&InterfaceKeyName
, &GuidString
);
1099 /* Create the interface key in registry */
1100 InitializeObjectAttributes(
1103 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
1105 NULL
); /* SecurityDescriptor */
1107 Status
= ZwCreateKey(
1113 REG_OPTION_VOLATILE
,
1114 NULL
); /* Disposition */
1116 if (!NT_SUCCESS(Status
))
1118 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1120 ExFreePool(BaseKeyName
.Buffer
);
1124 /* Write DeviceInstance entry. Value is InstancePath */
1125 Status
= ZwSetValueKey(
1130 InstancePath
->Buffer
,
1131 InstancePath
->Length
);
1132 if (!NT_SUCCESS(Status
))
1134 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1135 ZwClose(InterfaceKey
);
1137 ExFreePool(InterfaceKeyName
.Buffer
);
1138 ExFreePool(BaseKeyName
.Buffer
);
1142 /* Create subkey. Name is #ReferenceString */
1143 SubKeyName
.Length
= 0;
1144 SubKeyName
.MaximumLength
= sizeof(WCHAR
);
1145 if (ReferenceString
&& ReferenceString
->Length
)
1146 SubKeyName
.MaximumLength
+= ReferenceString
->Length
;
1147 SubKeyName
.Buffer
= ExAllocatePool(
1149 SubKeyName
.MaximumLength
);
1150 if (!SubKeyName
.Buffer
)
1152 DPRINT("ExAllocatePool() failed\n");
1153 ZwClose(InterfaceKey
);
1155 ExFreePool(InterfaceKeyName
.Buffer
);
1156 ExFreePool(BaseKeyName
.Buffer
);
1157 return STATUS_INSUFFICIENT_RESOURCES
;
1159 RtlAppendUnicodeToString(&SubKeyName
, L
"#");
1160 if (ReferenceString
&& ReferenceString
->Length
)
1161 RtlAppendUnicodeStringToString(&SubKeyName
, ReferenceString
);
1163 /* Create SubKeyName key in registry */
1164 InitializeObjectAttributes(
1167 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1168 InterfaceKey
, /* RootDirectory */
1169 NULL
); /* SecurityDescriptor */
1171 Status
= ZwCreateKey(
1177 REG_OPTION_VOLATILE
,
1178 NULL
); /* Disposition */
1180 if (!NT_SUCCESS(Status
))
1182 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1183 ZwClose(InterfaceKey
);
1185 ExFreePool(InterfaceKeyName
.Buffer
);
1186 ExFreePool(BaseKeyName
.Buffer
);
1190 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1191 SymbolicLinkName
->Length
= 0;
1192 SymbolicLinkName
->MaximumLength
= SymbolicLinkName
->Length
1193 + 4 * sizeof(WCHAR
) /* 4 = size of \??\ */
1194 + InstancePath
->Length
1195 + sizeof(WCHAR
) /* 1 = size of # */
1197 + sizeof(WCHAR
); /* final NULL */
1198 if (ReferenceString
&& ReferenceString
->Length
)
1199 SymbolicLinkName
->MaximumLength
+= sizeof(WCHAR
) + ReferenceString
->Length
;
1200 SymbolicLinkName
->Buffer
= ExAllocatePool(
1202 SymbolicLinkName
->MaximumLength
);
1203 if (!SymbolicLinkName
->Buffer
)
1205 DPRINT("ExAllocatePool() failed\n");
1207 ZwClose(InterfaceKey
);
1209 ExFreePool(InterfaceKeyName
.Buffer
);
1210 ExFreePool(SubKeyName
.Buffer
);
1211 ExFreePool(BaseKeyName
.Buffer
);
1212 return STATUS_INSUFFICIENT_RESOURCES
;
1214 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\??\\");
1215 StartIndex
= SymbolicLinkName
->Length
/ sizeof(WCHAR
);
1216 RtlAppendUnicodeStringToString(SymbolicLinkName
, InstancePath
);
1217 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1219 if (SymbolicLinkName
->Buffer
[StartIndex
+ i
] == '\\')
1220 SymbolicLinkName
->Buffer
[StartIndex
+ i
] = '#';
1222 RtlAppendUnicodeToString(SymbolicLinkName
, L
"#");
1223 RtlAppendUnicodeStringToString(SymbolicLinkName
, &GuidString
);
1224 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1226 /* Create symbolic link */
1227 DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName
, &PdoNameInfo
->Name
);
1228 SymLinkStatus
= IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1230 /* If the symbolic link already exists, return an informational success status */
1231 if (SymLinkStatus
== STATUS_OBJECT_NAME_COLLISION
)
1233 /* HACK: Delete the existing symbolic link and update it to the new PDO name */
1234 IoDeleteSymbolicLink(SymbolicLinkName
);
1235 IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1236 SymLinkStatus
= STATUS_OBJECT_NAME_EXISTS
;
1239 if (!NT_SUCCESS(SymLinkStatus
))
1241 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", SymLinkStatus
);
1243 ZwClose(InterfaceKey
);
1245 ExFreePool(SubKeyName
.Buffer
);
1246 ExFreePool(InterfaceKeyName
.Buffer
);
1247 ExFreePool(BaseKeyName
.Buffer
);
1248 ExFreePool(SymbolicLinkName
->Buffer
);
1249 return SymLinkStatus
;
1252 if (ReferenceString
&& ReferenceString
->Length
)
1254 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\");
1255 RtlAppendUnicodeStringToString(SymbolicLinkName
, ReferenceString
);
1257 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1259 /* Write symbolic link name in registry */
1260 SymbolicLinkName
->Buffer
[1] = '\\';
1261 Status
= ZwSetValueKey(
1266 SymbolicLinkName
->Buffer
,
1267 SymbolicLinkName
->Length
);
1268 if (!NT_SUCCESS(Status
))
1270 DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1271 ExFreePool(SymbolicLinkName
->Buffer
);
1275 SymbolicLinkName
->Buffer
[1] = '?';
1279 ZwClose(InterfaceKey
);
1281 ExFreePool(SubKeyName
.Buffer
);
1282 ExFreePool(InterfaceKeyName
.Buffer
);
1283 ExFreePool(BaseKeyName
.Buffer
);
1285 return NT_SUCCESS(Status
) ? SymLinkStatus
: Status
;
1289 * @name IoSetDeviceInterfaceState
1292 * Enables or disables an instance of a previously registered device
1294 * Documented in WDK.
1296 * @param SymbolicLinkName
1297 * Pointer to the string identifying instance to enable or disable
1300 * TRUE = enable, FALSE = disable
1302 * @return Usual NTSTATUS
1304 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
1310 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName
,
1313 PDEVICE_OBJECT PhysicalDeviceObject
;
1314 UNICODE_STRING GuidString
;
1315 PWCHAR StartPosition
;
1319 HANDLE InstanceHandle
, ControlHandle
;
1320 UNICODE_STRING KeyName
, DeviceInstance
;
1321 OBJECT_ATTRIBUTES ObjectAttributes
;
1322 ULONG LinkedValue
, Index
;
1326 if (SymbolicLinkName
== NULL
)
1327 return STATUS_INVALID_PARAMETER_1
;
1329 DPRINT("IoSetDeviceInterfaceState('%wZ', %u)\n", SymbolicLinkName
, Enable
);
1331 /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1332 /* Get GUID from SymbolicLinkName */
1333 StartPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'{');
1334 EndPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'}');
1335 if (!StartPosition
||!EndPosition
|| StartPosition
> EndPosition
)
1337 DPRINT1("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
1338 return STATUS_INVALID_PARAMETER_1
;
1340 GuidString
.Buffer
= StartPosition
;
1341 GuidString
.MaximumLength
= GuidString
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)StartPosition
);
1343 Status
= OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName
,
1348 if (!NT_SUCCESS(Status
))
1351 RtlInitUnicodeString(&KeyName
, L
"Control");
1352 InitializeObjectAttributes(&ObjectAttributes
,
1354 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1357 Status
= ZwCreateKey(&ControlHandle
,
1362 REG_OPTION_VOLATILE
,
1364 ZwClose(InstanceHandle
);
1365 if (!NT_SUCCESS(Status
))
1367 DPRINT1("Failed to create the Control subkey\n");
1371 LinkedValue
= (Enable
? 1 : 0);
1373 RtlInitUnicodeString(&KeyName
, L
"Linked");
1374 Status
= ZwSetValueKey(ControlHandle
,
1380 ZwClose(ControlHandle
);
1381 if (!NT_SUCCESS(Status
))
1383 DPRINT1("Failed to write the Linked value\n");
1387 DeviceInstance
.Buffer
= ExAllocatePool(PagedPool
, (ULONG_PTR
)StartPosition
- (ULONG_PTR
)SymbolicLinkName
->Buffer
);
1388 if (DeviceInstance
.Buffer
== NULL
)
1391 return STATUS_INSUFFICIENT_RESOURCES
;
1394 DeviceInstance
.MaximumLength
= DeviceInstance
.Length
= ((ULONG_PTR
)StartPosition
- (ULONG_PTR
)SymbolicLinkName
->Buffer
) - 5 * sizeof(WCHAR
);
1395 RtlCopyMemory(DeviceInstance
.Buffer
, &SymbolicLinkName
->Buffer
[4], DeviceInstance
.Length
);
1396 for(Index
= 0; Index
< DeviceInstance
.Length
/ sizeof(WCHAR
); Index
++)
1398 if (DeviceInstance
.Buffer
[Index
] == L
'#')
1400 DeviceInstance
.Buffer
[Index
] = L
'\\';
1404 PhysicalDeviceObject
= IopGetDeviceObjectFromDeviceInstance(&DeviceInstance
);
1406 if (!PhysicalDeviceObject
)
1408 DPRINT1("IopGetDeviceObjectFromDeviceInstance failed to find status %wZ\n", &DeviceInstance
);
1409 ExFreePool(DeviceInstance
.Buffer
);
1410 return STATUS_NOT_FOUND
;
1413 ExFreePool(DeviceInstance
.Buffer
);
1414 Status
= RtlGUIDFromString(&GuidString
, &DeviceGuid
);
1415 if (!NT_SUCCESS(Status
))
1417 DPRINT1("RtlGUIDFromString() failed with status 0x%08lx\n", Status
);
1418 ObDereferenceObject(PhysicalDeviceObject
);
1422 EventGuid
= Enable
? &GUID_DEVICE_INTERFACE_ARRIVAL
: &GUID_DEVICE_INTERFACE_REMOVAL
;
1423 IopNotifyPlugPlayNotification(
1424 PhysicalDeviceObject
,
1425 EventCategoryDeviceInterfaceChange
,
1428 (PVOID
)SymbolicLinkName
);
1430 ObDereferenceObject(PhysicalDeviceObject
);
1431 DPRINT("Status %x\n", Status
);
1432 return STATUS_SUCCESS
;