xref: /reactos/ntoskrnl/io/iomgr/deviface.c (revision 6b82ed57)
1 /*
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
6  *
7  * PROGRAMMERS:     Filip Navara (xnavara@volny.cz)
8  *                  Matthew Brace (ismarc@austin.rr.com)
9  *                  Hervé Poussineau (hpoussin@reactos.org)
10  */
11 
12 /* INCLUDES ******************************************************************/
13 
14 #include <ntoskrnl.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 /* FIXME: This should be somewhere global instead of having 20 different versions */
20 #define GUID_STRING_CHARS 38
21 #define GUID_STRING_BYTES (GUID_STRING_CHARS * sizeof(WCHAR))
22 C_ASSERT(sizeof(L"{01234567-89ab-cdef-0123-456789abcdef}") == GUID_STRING_BYTES + sizeof(UNICODE_NULL));
23 
24 /* FUNCTIONS *****************************************************************/
25 
26 PDEVICE_OBJECT
27 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance);
28 
29 static PWCHAR BaseKeyString = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
30 
31 static
32 NTSTATUS
33 OpenRegistryHandlesFromSymbolicLink(IN PUNICODE_STRING SymbolicLinkName,
34                                     IN ACCESS_MASK DesiredAccess,
35                                     IN OPTIONAL PHANDLE GuidKey,
36                                     IN OPTIONAL PHANDLE DeviceKey,
37                                     IN OPTIONAL PHANDLE InstanceKey)
38 {
39     OBJECT_ATTRIBUTES ObjectAttributes;
40     UNICODE_STRING BaseKeyU;
41     UNICODE_STRING GuidString, SubKeyName, ReferenceString;
42     PWCHAR StartPosition, EndPosition;
43     HANDLE ClassesKey;
44     PHANDLE GuidKeyRealP, DeviceKeyRealP, InstanceKeyRealP;
45     HANDLE GuidKeyReal, DeviceKeyReal, InstanceKeyReal;
46     NTSTATUS Status;
47 
48     SubKeyName.Buffer = NULL;
49 
50     if (GuidKey != NULL)
51         GuidKeyRealP = GuidKey;
52     else
53         GuidKeyRealP = &GuidKeyReal;
54 
55     if (DeviceKey != NULL)
56         DeviceKeyRealP = DeviceKey;
57     else
58         DeviceKeyRealP = &DeviceKeyReal;
59 
60     if (InstanceKey != NULL)
61         InstanceKeyRealP = InstanceKey;
62     else
63         InstanceKeyRealP = &InstanceKeyReal;
64 
65     *GuidKeyRealP = NULL;
66     *DeviceKeyRealP = NULL;
67     *InstanceKeyRealP = NULL;
68 
69     RtlInitUnicodeString(&BaseKeyU, BaseKeyString);
70 
71     /* Open the DeviceClasses key */
72     InitializeObjectAttributes(&ObjectAttributes,
73                                &BaseKeyU,
74                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
75                                NULL,
76                                NULL);
77     Status = ZwOpenKey(&ClassesKey,
78                        DesiredAccess | KEY_ENUMERATE_SUB_KEYS,
79                        &ObjectAttributes);
80     if (!NT_SUCCESS(Status))
81     {
82         DPRINT1("Failed to open %wZ\n", &BaseKeyU);
83         goto cleanup;
84     }
85 
86     StartPosition = wcschr(SymbolicLinkName->Buffer, L'{');
87     EndPosition = wcschr(SymbolicLinkName->Buffer, L'}');
88     if (!StartPosition || !EndPosition || StartPosition > EndPosition)
89     {
90         DPRINT1("Bad symbolic link: %wZ\n", SymbolicLinkName);
91         return STATUS_INVALID_PARAMETER_1;
92     }
93     GuidString.Buffer = StartPosition;
94     GuidString.MaximumLength = GuidString.Length = (USHORT)((ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)StartPosition);
95 
96     InitializeObjectAttributes(&ObjectAttributes,
97                                &GuidString,
98                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
99                                ClassesKey,
100                                NULL);
101     Status = ZwCreateKey(GuidKeyRealP,
102                          DesiredAccess | KEY_ENUMERATE_SUB_KEYS,
103                          &ObjectAttributes,
104                          0,
105                          NULL,
106                          REG_OPTION_NON_VOLATILE,
107                          NULL);
108     ZwClose(ClassesKey);
109     if (!NT_SUCCESS(Status))
110     {
111         DPRINT1("Failed to open %wZ%wZ (%x)\n", &BaseKeyU, &GuidString, Status);
112         goto cleanup;
113     }
114 
115     SubKeyName.MaximumLength = SymbolicLinkName->Length + sizeof(WCHAR);
116     SubKeyName.Length = 0;
117     SubKeyName.Buffer = ExAllocatePool(PagedPool, SubKeyName.MaximumLength);
118     if (!SubKeyName.Buffer)
119     {
120         Status = STATUS_INSUFFICIENT_RESOURCES;
121         goto cleanup;
122     }
123 
124     RtlAppendUnicodeStringToString(&SubKeyName,
125                                    SymbolicLinkName);
126 
127     SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = UNICODE_NULL;
128 
129     SubKeyName.Buffer[0] = L'#';
130     SubKeyName.Buffer[1] = L'#';
131     SubKeyName.Buffer[2] = L'?';
132     SubKeyName.Buffer[3] = L'#';
133 
134     ReferenceString.Buffer = wcsrchr(SubKeyName.Buffer, '\\');
135     if (ReferenceString.Buffer != NULL)
136     {
137         ReferenceString.Buffer[0] = L'#';
138 
139         SubKeyName.Length = (USHORT)((ULONG_PTR)(ReferenceString.Buffer) - (ULONG_PTR)SubKeyName.Buffer);
140         ReferenceString.Length = SymbolicLinkName->Length - SubKeyName.Length;
141     }
142     else
143     {
144         RtlInitUnicodeString(&ReferenceString, L"#");
145     }
146 
147     InitializeObjectAttributes(&ObjectAttributes,
148                                &SubKeyName,
149                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
150                                *GuidKeyRealP,
151                                NULL);
152     Status = ZwCreateKey(DeviceKeyRealP,
153                          DesiredAccess | KEY_ENUMERATE_SUB_KEYS,
154                          &ObjectAttributes,
155                          0,
156                          NULL,
157                          REG_OPTION_NON_VOLATILE,
158                          NULL);
159     if (!NT_SUCCESS(Status))
160     {
161         DPRINT1("Failed to open %wZ%wZ\\%wZ Status %x\n", &BaseKeyU, &GuidString, &SubKeyName, Status);
162         goto cleanup;
163     }
164 
165     InitializeObjectAttributes(&ObjectAttributes,
166                                &ReferenceString,
167                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
168                                *DeviceKeyRealP,
169                                NULL);
170     Status = ZwCreateKey(InstanceKeyRealP,
171                          DesiredAccess,
172                          &ObjectAttributes,
173                          0,
174                          NULL,
175                          REG_OPTION_NON_VOLATILE,
176                          NULL);
177     if (!NT_SUCCESS(Status))
178     {
179         DPRINT1("Failed to open %wZ%wZ\\%wZ%\\%wZ (%x)\n", &BaseKeyU, &GuidString, &SubKeyName, &ReferenceString, Status);
180         goto cleanup;
181     }
182 
183     Status = STATUS_SUCCESS;
184 
185 cleanup:
186     if (SubKeyName.Buffer != NULL)
187         ExFreePool(SubKeyName.Buffer);
188 
189     if (NT_SUCCESS(Status))
190     {
191         if (!GuidKey)
192             ZwClose(*GuidKeyRealP);
193 
194         if (!DeviceKey)
195             ZwClose(*DeviceKeyRealP);
196 
197         if (!InstanceKey)
198             ZwClose(*InstanceKeyRealP);
199     }
200     else
201     {
202         if (*GuidKeyRealP != NULL)
203             ZwClose(*GuidKeyRealP);
204 
205         if (*DeviceKeyRealP != NULL)
206             ZwClose(*DeviceKeyRealP);
207 
208         if (*InstanceKeyRealP != NULL)
209             ZwClose(*InstanceKeyRealP);
210     }
211 
212     return Status;
213 }
214 
215 /*++
216  * @name IoOpenDeviceInterfaceRegistryKey
217  * @unimplemented
218  *
219  * Provides a handle to the device's interface instance registry key.
220  * Documented in WDK.
221  *
222  * @param SymbolicLinkName
223  *        Pointer to a string which identifies the device interface instance
224  *
225  * @param DesiredAccess
226  *        Desired ACCESS_MASK used to access the key (like KEY_READ,
227  *        KEY_WRITE, etc)
228  *
229  * @param DeviceInterfaceKey
230  *        If a call has been succesfull, a handle to the registry key
231  *        will be stored there
232  *
233  * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
234  *         otherwise (see WDK for details)
235  *
236  * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
237  *
238  *--*/
239 NTSTATUS
240 NTAPI
241 IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName,
242                                  IN ACCESS_MASK DesiredAccess,
243                                  OUT PHANDLE DeviceInterfaceKey)
244 {
245     HANDLE InstanceKey, DeviceParametersKey;
246     NTSTATUS Status;
247     OBJECT_ATTRIBUTES ObjectAttributes;
248     UNICODE_STRING DeviceParametersU = RTL_CONSTANT_STRING(L"Device Parameters");
249 
250     Status = OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName,
251                                                  KEY_CREATE_SUB_KEY,
252                                                  NULL,
253                                                  NULL,
254                                                  &InstanceKey);
255     if (!NT_SUCCESS(Status))
256         return Status;
257 
258     InitializeObjectAttributes(&ObjectAttributes,
259                                &DeviceParametersU,
260                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
261                                InstanceKey,
262                                NULL);
263     Status = ZwCreateKey(&DeviceParametersKey,
264                          DesiredAccess,
265                          &ObjectAttributes,
266                          0,
267                          NULL,
268                          REG_OPTION_NON_VOLATILE,
269                          NULL);
270     ZwClose(InstanceKey);
271 
272     if (NT_SUCCESS(Status))
273         *DeviceInterfaceKey = DeviceParametersKey;
274 
275     return Status;
276 }
277 
278 /*++
279  * @name IoGetDeviceInterfaceAlias
280  * @unimplemented
281  *
282  * Returns the alias device interface of the specified device interface
283  * instance, if the alias exists.
284  * Documented in WDK.
285  *
286  * @param SymbolicLinkName
287  *        Pointer to a string which identifies the device interface instance
288  *
289  * @param AliasInterfaceClassGuid
290  *        See WDK
291  *
292  * @param AliasSymbolicLinkName
293  *        See WDK
294  *
295  * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
296  *         otherwise (see WDK for details)
297  *
298  * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
299  *
300  *--*/
301 NTSTATUS
302 NTAPI
303 IoGetDeviceInterfaceAlias(IN PUNICODE_STRING SymbolicLinkName,
304                           IN CONST GUID *AliasInterfaceClassGuid,
305                           OUT PUNICODE_STRING AliasSymbolicLinkName)
306 {
307     return STATUS_NOT_IMPLEMENTED;
308 }
309 
310 /*++
311  * @name IopOpenInterfaceKey
312  *
313  * Returns the alias device interface of the specified device interface
314  *
315  * @param InterfaceClassGuid
316  *        FILLME
317  *
318  * @param DesiredAccess
319  *        FILLME
320  *
321  * @param pInterfaceKey
322  *        FILLME
323  *
324  * @return Usual NTSTATUS
325  *
326  * @remarks None
327  *
328  *--*/
329 static NTSTATUS
330 IopOpenInterfaceKey(IN CONST GUID *InterfaceClassGuid,
331                     IN ACCESS_MASK DesiredAccess,
332                     OUT HANDLE *pInterfaceKey)
333 {
334     UNICODE_STRING LocalMachine = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\");
335     UNICODE_STRING GuidString;
336     UNICODE_STRING KeyName;
337     OBJECT_ATTRIBUTES ObjectAttributes;
338     HANDLE InterfaceKey = NULL;
339     NTSTATUS Status;
340 
341     GuidString.Buffer = KeyName.Buffer = NULL;
342 
343     Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
344     if (!NT_SUCCESS(Status))
345     {
346         DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
347         goto cleanup;
348     }
349 
350     KeyName.Length = 0;
351     KeyName.MaximumLength = LocalMachine.Length + ((USHORT)wcslen(REGSTR_PATH_DEVICE_CLASSES) + 1) * sizeof(WCHAR) + GuidString.Length;
352     KeyName.Buffer = ExAllocatePool(PagedPool, KeyName.MaximumLength);
353     if (!KeyName.Buffer)
354     {
355         DPRINT("ExAllocatePool() failed\n");
356         Status = STATUS_INSUFFICIENT_RESOURCES;
357         goto cleanup;
358     }
359 
360     Status = RtlAppendUnicodeStringToString(&KeyName, &LocalMachine);
361     if (!NT_SUCCESS(Status))
362     {
363         DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
364         goto cleanup;
365     }
366     Status = RtlAppendUnicodeToString(&KeyName, REGSTR_PATH_DEVICE_CLASSES);
367     if (!NT_SUCCESS(Status))
368     {
369         DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
370         goto cleanup;
371     }
372     Status = RtlAppendUnicodeToString(&KeyName, L"\\");
373     if (!NT_SUCCESS(Status))
374     {
375         DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
376         goto cleanup;
377     }
378     Status = RtlAppendUnicodeStringToString(&KeyName, &GuidString);
379     if (!NT_SUCCESS(Status))
380     {
381         DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
382         goto cleanup;
383     }
384 
385     InitializeObjectAttributes(
386         &ObjectAttributes,
387         &KeyName,
388         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
389         NULL,
390         NULL);
391     Status = ZwOpenKey(
392         &InterfaceKey,
393         DesiredAccess,
394         &ObjectAttributes);
395     if (!NT_SUCCESS(Status))
396     {
397         DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
398         goto cleanup;
399     }
400 
401     *pInterfaceKey = InterfaceKey;
402     Status = STATUS_SUCCESS;
403 
404 cleanup:
405     if (!NT_SUCCESS(Status))
406     {
407         if (InterfaceKey != NULL)
408             ZwClose(InterfaceKey);
409     }
410     RtlFreeUnicodeString(&GuidString);
411     RtlFreeUnicodeString(&KeyName);
412     return Status;
413 }
414 
415 /*++
416  * @name IoGetDeviceInterfaces
417  * @implemented
418  *
419  * Returns a list of device interfaces of a particular device interface class.
420  * Documented in WDK
421  *
422  * @param InterfaceClassGuid
423  *        Points to a class GUID specifying the device interface class
424  *
425  * @param PhysicalDeviceObject
426  *        Points to an optional PDO that narrows the search to only the
427  *        device interfaces of the device represented by the PDO
428  *
429  * @param Flags
430  *        Specifies flags that modify the search for device interfaces. The
431  *        DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
432  *        returned symbolic links should contain also disabled device
433  *        interfaces in addition to the enabled ones.
434  *
435  * @param SymbolicLinkList
436  *        Points to a character pointer that is filled in on successful return
437  *        with a list of unicode strings identifying the device interfaces
438  *        that match the search criteria. The newly allocated buffer contains
439  *        a list of symbolic link names. Each unicode string in the list is
440  *        null-terminated; the end of the whole list is marked by an additional
441  *        NULL. The caller is responsible for freeing the buffer (ExFreePool)
442  *        when it is no longer needed.
443  *        If no device interfaces match the search criteria, this routine
444  *        returns STATUS_SUCCESS and the string contains a single NULL
445  *        character.
446  *
447  * @return Usual NTSTATUS
448  *
449  * @remarks None
450  *
451  *--*/
452 NTSTATUS
453 NTAPI
454 IoGetDeviceInterfaces(IN CONST GUID *InterfaceClassGuid,
455                       IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
456                       IN ULONG Flags,
457                       OUT PWSTR *SymbolicLinkList)
458 {
459     UNICODE_STRING Control = RTL_CONSTANT_STRING(L"Control");
460     UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
461     HANDLE InterfaceKey = NULL;
462     HANDLE DeviceKey = NULL;
463     HANDLE ReferenceKey = NULL;
464     HANDLE ControlKey = NULL;
465     PKEY_BASIC_INFORMATION DeviceBi = NULL;
466     PKEY_BASIC_INFORMATION ReferenceBi = NULL;
467     PKEY_VALUE_PARTIAL_INFORMATION bip = NULL;
468     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
469     PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension;
470     PUNICODE_STRING InstanceDevicePath = NULL;
471     UNICODE_STRING KeyName;
472     OBJECT_ATTRIBUTES ObjectAttributes;
473     BOOLEAN FoundRightPDO = FALSE;
474     ULONG i = 0, j, Size, NeededLength, ActualLength, LinkedValue;
475     UNICODE_STRING ReturnBuffer = { 0, 0, NULL };
476     NTSTATUS Status;
477 
478     PAGED_CODE();
479 
480     if (PhysicalDeviceObject != NULL)
481     {
482         /* Parameters must pass three border of checks */
483         DeviceObjectExtension = (PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension;
484 
485         /* 1st level: Presence of a Device Node */
486         if (DeviceObjectExtension->DeviceNode == NULL)
487         {
488             DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject);
489             return STATUS_INVALID_DEVICE_REQUEST;
490         }
491 
492         /* 2nd level: Presence of an non-zero length InstancePath */
493         if (DeviceObjectExtension->DeviceNode->InstancePath.Length == 0)
494         {
495             DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject);
496             return STATUS_INVALID_DEVICE_REQUEST;
497         }
498 
499         InstanceDevicePath = &DeviceObjectExtension->DeviceNode->InstancePath;
500     }
501 
502 
503     Status = IopOpenInterfaceKey(InterfaceClassGuid, KEY_ENUMERATE_SUB_KEYS, &InterfaceKey);
504     if (!NT_SUCCESS(Status))
505     {
506         DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status);
507         goto cleanup;
508     }
509 
510     /* Enumerate subkeys (i.e. the different device objects) */
511     while (TRUE)
512     {
513         Status = ZwEnumerateKey(
514             InterfaceKey,
515             i,
516             KeyBasicInformation,
517             NULL,
518             0,
519             &Size);
520         if (Status == STATUS_NO_MORE_ENTRIES)
521         {
522             break;
523         }
524         else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
525         {
526             DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
527             goto cleanup;
528         }
529 
530         DeviceBi = ExAllocatePool(PagedPool, Size);
531         if (!DeviceBi)
532         {
533             DPRINT("ExAllocatePool() failed\n");
534             Status = STATUS_INSUFFICIENT_RESOURCES;
535             goto cleanup;
536         }
537         Status = ZwEnumerateKey(
538             InterfaceKey,
539             i++,
540             KeyBasicInformation,
541             DeviceBi,
542             Size,
543             &Size);
544         if (!NT_SUCCESS(Status))
545         {
546             DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
547             goto cleanup;
548         }
549 
550         /* Open device key */
551         KeyName.Length = KeyName.MaximumLength = (USHORT)DeviceBi->NameLength;
552         KeyName.Buffer = DeviceBi->Name;
553         InitializeObjectAttributes(
554             &ObjectAttributes,
555             &KeyName,
556             OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
557             InterfaceKey,
558             NULL);
559         Status = ZwOpenKey(
560             &DeviceKey,
561             KEY_ENUMERATE_SUB_KEYS,
562             &ObjectAttributes);
563         if (!NT_SUCCESS(Status))
564         {
565             DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
566             goto cleanup;
567         }
568 
569         if (PhysicalDeviceObject)
570         {
571             /* Check if we are on the right physical device object,
572             * by reading the DeviceInstance string
573             */
574             RtlInitUnicodeString(&KeyName, L"DeviceInstance");
575             Status = ZwQueryValueKey(DeviceKey, &KeyName, KeyValuePartialInformation, NULL, 0, &NeededLength);
576             if (Status == STATUS_BUFFER_TOO_SMALL)
577             {
578                 ActualLength = NeededLength;
579                 PartialInfo = ExAllocatePool(NonPagedPool, ActualLength);
580                 if (!PartialInfo)
581                 {
582                     Status = STATUS_INSUFFICIENT_RESOURCES;
583                     goto cleanup;
584                 }
585 
586                 Status = ZwQueryValueKey(DeviceKey, &KeyName, KeyValuePartialInformation, PartialInfo, ActualLength, &NeededLength);
587                 if (!NT_SUCCESS(Status))
588                 {
589                     DPRINT1("ZwQueryValueKey #2 failed (%x)\n", Status);
590                     ExFreePool(PartialInfo);
591                     goto cleanup;
592                 }
593                 if (PartialInfo->DataLength == InstanceDevicePath->Length)
594                 {
595                     if (RtlCompareMemory(PartialInfo->Data, InstanceDevicePath->Buffer, InstanceDevicePath->Length) == InstanceDevicePath->Length)
596                     {
597                         /* found right pdo */
598                         FoundRightPDO = TRUE;
599                     }
600                 }
601                 ExFreePool(PartialInfo);
602                 PartialInfo = NULL;
603                 if (!FoundRightPDO)
604                 {
605                     /* not yet found */
606                     continue;
607                 }
608             }
609             else
610             {
611                 /* error */
612                 break;
613             }
614         }
615 
616         /* Enumerate subkeys (ie the different reference strings) */
617         j = 0;
618         while (TRUE)
619         {
620             Status = ZwEnumerateKey(
621                 DeviceKey,
622                 j,
623                 KeyBasicInformation,
624                 NULL,
625                 0,
626                 &Size);
627             if (Status == STATUS_NO_MORE_ENTRIES)
628             {
629                 break;
630             }
631             else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
632             {
633                 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
634                 goto cleanup;
635             }
636 
637             ReferenceBi = ExAllocatePool(PagedPool, Size);
638             if (!ReferenceBi)
639             {
640                 DPRINT("ExAllocatePool() failed\n");
641                 Status = STATUS_INSUFFICIENT_RESOURCES;
642                 goto cleanup;
643             }
644             Status = ZwEnumerateKey(
645                 DeviceKey,
646                 j++,
647                 KeyBasicInformation,
648                 ReferenceBi,
649                 Size,
650                 &Size);
651             if (!NT_SUCCESS(Status))
652             {
653                 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
654                 goto cleanup;
655             }
656 
657             KeyName.Length = KeyName.MaximumLength = (USHORT)ReferenceBi->NameLength;
658             KeyName.Buffer = ReferenceBi->Name;
659             if (RtlEqualUnicodeString(&KeyName, &Control, TRUE))
660             {
661                 /* Skip Control subkey */
662                 goto NextReferenceString;
663             }
664 
665             /* Open reference key */
666             InitializeObjectAttributes(
667                 &ObjectAttributes,
668                 &KeyName,
669                 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
670                 DeviceKey,
671                 NULL);
672             Status = ZwOpenKey(
673                 &ReferenceKey,
674                 KEY_QUERY_VALUE,
675                 &ObjectAttributes);
676             if (!NT_SUCCESS(Status))
677             {
678                 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
679                 goto cleanup;
680             }
681 
682             if (!(Flags & DEVICE_INTERFACE_INCLUDE_NONACTIVE))
683             {
684                 /* We have to check if the interface is enabled, by
685                 * reading the Linked value in the Control subkey
686                 */
687                 InitializeObjectAttributes(
688                     &ObjectAttributes,
689                     &Control,
690                     OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
691                     ReferenceKey,
692                     NULL);
693                 Status = ZwOpenKey(
694                     &ControlKey,
695                     KEY_QUERY_VALUE,
696                     &ObjectAttributes);
697                 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
698                 {
699                     /* That's OK. The key doesn't exist (yet) because
700                     * the interface is not activated.
701                     */
702                     goto NextReferenceString;
703                 }
704                 else if (!NT_SUCCESS(Status))
705                 {
706                     DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
707                     goto cleanup;
708                 }
709 
710                 RtlInitUnicodeString(&KeyName, L"Linked");
711                 Status = ZwQueryValueKey(ControlKey,
712                                          &KeyName,
713                                          KeyValuePartialInformation,
714                                          NULL,
715                                          0,
716                                          &NeededLength);
717                 if (Status == STATUS_BUFFER_TOO_SMALL)
718                 {
719                     ActualLength = NeededLength;
720                     PartialInfo = ExAllocatePool(NonPagedPool, ActualLength);
721                     if (!PartialInfo)
722                     {
723                         Status = STATUS_INSUFFICIENT_RESOURCES;
724                         goto cleanup;
725                     }
726 
727                     Status = ZwQueryValueKey(ControlKey,
728                                              &KeyName,
729                                              KeyValuePartialInformation,
730                                              PartialInfo,
731                                              ActualLength,
732                                              &NeededLength);
733                     if (!NT_SUCCESS(Status))
734                     {
735                         DPRINT1("ZwQueryValueKey #2 failed (%x)\n", Status);
736                         ExFreePool(PartialInfo);
737                         goto cleanup;
738                     }
739 
740                     if (PartialInfo->Type != REG_DWORD || PartialInfo->DataLength != sizeof(ULONG))
741                     {
742                         DPRINT1("Bad registry read\n");
743                         ExFreePool(PartialInfo);
744                         goto cleanup;
745                     }
746 
747                     RtlCopyMemory(&LinkedValue,
748                                   PartialInfo->Data,
749                                   PartialInfo->DataLength);
750 
751                     ExFreePool(PartialInfo);
752                     if (LinkedValue == 0)
753                     {
754                         /* This interface isn't active */
755                         goto NextReferenceString;
756                     }
757                 }
758                 else
759                 {
760                     DPRINT1("ZwQueryValueKey #1 failed (%x)\n", Status);
761                     goto cleanup;
762                 }
763             }
764 
765             /* Read the SymbolicLink string and add it into SymbolicLinkList */
766             Status = ZwQueryValueKey(
767                 ReferenceKey,
768                 &SymbolicLink,
769                 KeyValuePartialInformation,
770                 NULL,
771                 0,
772                 &Size);
773             if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
774             {
775                 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
776                 goto cleanup;
777             }
778             bip = ExAllocatePool(PagedPool, Size);
779             if (!bip)
780             {
781                 DPRINT("ExAllocatePool() failed\n");
782                 Status = STATUS_INSUFFICIENT_RESOURCES;
783                 goto cleanup;
784             }
785             Status = ZwQueryValueKey(
786                 ReferenceKey,
787                 &SymbolicLink,
788                 KeyValuePartialInformation,
789                 bip,
790                 Size,
791                 &Size);
792             if (!NT_SUCCESS(Status))
793             {
794                 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
795                 goto cleanup;
796             }
797             else if (bip->Type != REG_SZ)
798             {
799                 DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip->Type, REG_SZ);
800                 Status = STATUS_UNSUCCESSFUL;
801                 goto cleanup;
802             }
803             else if (bip->DataLength < 5 * sizeof(WCHAR))
804             {
805                 DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip->DataLength, 5 * sizeof(WCHAR));
806                 Status = STATUS_UNSUCCESSFUL;
807                 goto cleanup;
808             }
809             KeyName.Length = KeyName.MaximumLength = (USHORT)bip->DataLength;
810             KeyName.Buffer = (PWSTR)bip->Data;
811 
812             /* Fixup the prefix (from "\\?\") */
813             RtlCopyMemory(KeyName.Buffer, L"\\??\\", 4 * sizeof(WCHAR));
814 
815             /* Add new symbolic link to symbolic link list */
816             if (ReturnBuffer.Length + KeyName.Length + sizeof(WCHAR) > ReturnBuffer.MaximumLength)
817             {
818                 PWSTR NewBuffer;
819                 ReturnBuffer.MaximumLength = (USHORT)max(2 * ReturnBuffer.MaximumLength,
820                                                          (USHORT)(ReturnBuffer.Length +
821                                                          KeyName.Length +
822                                                          2 * sizeof(WCHAR)));
823                 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
824                 if (!NewBuffer)
825                 {
826                     DPRINT("ExAllocatePool() failed\n");
827                     Status = STATUS_INSUFFICIENT_RESOURCES;
828                     goto cleanup;
829                 }
830                 if (ReturnBuffer.Buffer)
831                 {
832                     RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
833                     ExFreePool(ReturnBuffer.Buffer);
834                 }
835                 ReturnBuffer.Buffer = NewBuffer;
836             }
837             DPRINT("Adding symbolic link %wZ\n", &KeyName);
838             Status = RtlAppendUnicodeStringToString(&ReturnBuffer, &KeyName);
839             if (!NT_SUCCESS(Status))
840             {
841                 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
842                 goto cleanup;
843             }
844             /* RtlAppendUnicodeStringToString added a NULL at the end of the
845              * destination string, but didn't increase the Length field.
846              * Do it for it.
847              */
848             ReturnBuffer.Length += sizeof(WCHAR);
849 
850 NextReferenceString:
851             ExFreePool(ReferenceBi);
852             ReferenceBi = NULL;
853             if (bip)
854                 ExFreePool(bip);
855             bip = NULL;
856             if (ReferenceKey != NULL)
857             {
858                 ZwClose(ReferenceKey);
859                 ReferenceKey = NULL;
860             }
861             if (ControlKey != NULL)
862             {
863                 ZwClose(ControlKey);
864                 ControlKey = NULL;
865             }
866         }
867         if (FoundRightPDO)
868         {
869             /* No need to go further, as we already have found what we searched */
870             break;
871         }
872 
873         ExFreePool(DeviceBi);
874         DeviceBi = NULL;
875         ZwClose(DeviceKey);
876         DeviceKey = NULL;
877     }
878 
879     /* Add final NULL to ReturnBuffer */
880     ASSERT(ReturnBuffer.Length <= ReturnBuffer.MaximumLength);
881     if (ReturnBuffer.Length >= ReturnBuffer.MaximumLength)
882     {
883         PWSTR NewBuffer;
884         ReturnBuffer.MaximumLength += sizeof(WCHAR);
885         NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
886         if (!NewBuffer)
887         {
888             DPRINT("ExAllocatePool() failed\n");
889             Status = STATUS_INSUFFICIENT_RESOURCES;
890             goto cleanup;
891         }
892         if (ReturnBuffer.Buffer)
893         {
894             RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
895             ExFreePool(ReturnBuffer.Buffer);
896         }
897         ReturnBuffer.Buffer = NewBuffer;
898     }
899     ReturnBuffer.Buffer[ReturnBuffer.Length / sizeof(WCHAR)] = UNICODE_NULL;
900     *SymbolicLinkList = ReturnBuffer.Buffer;
901     Status = STATUS_SUCCESS;
902 
903 cleanup:
904     if (!NT_SUCCESS(Status) && ReturnBuffer.Buffer)
905         ExFreePool(ReturnBuffer.Buffer);
906     if (InterfaceKey != NULL)
907         ZwClose(InterfaceKey);
908     if (DeviceKey != NULL)
909         ZwClose(DeviceKey);
910     if (ReferenceKey != NULL)
911         ZwClose(ReferenceKey);
912     if (ControlKey != NULL)
913         ZwClose(ControlKey);
914     if (DeviceBi)
915         ExFreePool(DeviceBi);
916     if (ReferenceBi)
917         ExFreePool(ReferenceBi);
918     if (bip)
919         ExFreePool(bip);
920     return Status;
921 }
922 
923 /*++
924  * @name IoRegisterDeviceInterface
925  * @implemented
926  *
927  * Registers a device interface class, if it has not been previously registered,
928  * and creates a new instance of the interface class, which a driver can
929  * subsequently enable for use by applications or other system components.
930  * Documented in WDK.
931  *
932  * @param PhysicalDeviceObject
933  *        Points to an optional PDO that narrows the search to only the
934  *        device interfaces of the device represented by the PDO
935  *
936  * @param InterfaceClassGuid
937  *        Points to a class GUID specifying the device interface class
938  *
939  * @param ReferenceString
940  *        Optional parameter, pointing to a unicode string. For a full
941  *        description of this rather rarely used param (usually drivers
942  *        pass NULL here) see WDK
943  *
944  * @param SymbolicLinkName
945  *        Pointer to the resulting unicode string
946  *
947  * @return Usual NTSTATUS
948  *
949  * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
950  *          system thread
951  *
952  *--*/
953 NTSTATUS
954 NTAPI
955 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject,
956                           IN CONST GUID *InterfaceClassGuid,
957                           IN PUNICODE_STRING ReferenceString OPTIONAL,
958                           OUT PUNICODE_STRING SymbolicLinkName)
959 {
960     PUNICODE_STRING InstancePath;
961     UNICODE_STRING GuidString;
962     UNICODE_STRING SubKeyName;
963     UNICODE_STRING InterfaceKeyName;
964     UNICODE_STRING BaseKeyName;
965     UCHAR PdoNameInfoBuffer[sizeof(OBJECT_NAME_INFORMATION) + (256 * sizeof(WCHAR))];
966     POBJECT_NAME_INFORMATION PdoNameInfo = (POBJECT_NAME_INFORMATION)PdoNameInfoBuffer;
967     UNICODE_STRING DeviceInstance = RTL_CONSTANT_STRING(L"DeviceInstance");
968     UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
969     HANDLE ClassKey;
970     HANDLE InterfaceKey;
971     HANDLE SubKey;
972     ULONG StartIndex;
973     OBJECT_ATTRIBUTES ObjectAttributes;
974     ULONG i;
975     NTSTATUS Status, SymLinkStatus;
976     PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension;
977 
978     ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
979 
980     DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
981         PhysicalDeviceObject, ReferenceString);
982 
983     /* Parameters must pass three border of checks */
984     DeviceObjectExtension = (PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension;
985 
986     /* 1st level: Presence of a Device Node */
987     if (DeviceObjectExtension->DeviceNode == NULL)
988     {
989         DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject);
990         return STATUS_INVALID_DEVICE_REQUEST;
991     }
992 
993     /* 2nd level: Presence of an non-zero length InstancePath */
994     if (DeviceObjectExtension->DeviceNode->InstancePath.Length == 0)
995     {
996         DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject);
997         return STATUS_INVALID_DEVICE_REQUEST;
998     }
999 
1000     /* 3rd level: Optional, based on WDK documentation */
1001     if (ReferenceString != NULL)
1002     {
1003         /* Reference string must not contain path-separator symbols */
1004         for (i = 0; i < ReferenceString->Length / sizeof(WCHAR); i++)
1005         {
1006             if ((ReferenceString->Buffer[i] == '\\') ||
1007                 (ReferenceString->Buffer[i] == '/'))
1008                 return STATUS_INVALID_DEVICE_REQUEST;
1009         }
1010     }
1011 
1012     Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
1013     if (!NT_SUCCESS(Status))
1014     {
1015         DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
1016         return Status;
1017     }
1018 
1019     /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
1020     Status = ObQueryNameString(
1021         PhysicalDeviceObject,
1022         PdoNameInfo,
1023         sizeof(PdoNameInfoBuffer),
1024         &i);
1025     if (!NT_SUCCESS(Status))
1026     {
1027         DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status);
1028         return Status;
1029     }
1030     ASSERT(PdoNameInfo->Name.Length);
1031 
1032     /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
1033     ASSERT(((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode);
1034     InstancePath = &((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode->InstancePath;
1035     BaseKeyName.Length = (USHORT)wcslen(BaseKeyString) * sizeof(WCHAR);
1036     BaseKeyName.MaximumLength = BaseKeyName.Length
1037         + GuidString.Length;
1038     BaseKeyName.Buffer = ExAllocatePool(
1039         PagedPool,
1040         BaseKeyName.MaximumLength);
1041     if (!BaseKeyName.Buffer)
1042     {
1043         DPRINT("ExAllocatePool() failed\n");
1044         return STATUS_INSUFFICIENT_RESOURCES;
1045     }
1046     wcscpy(BaseKeyName.Buffer, BaseKeyString);
1047     RtlAppendUnicodeStringToString(&BaseKeyName, &GuidString);
1048 
1049     /* Create BaseKeyName key in registry */
1050     InitializeObjectAttributes(
1051         &ObjectAttributes,
1052         &BaseKeyName,
1053         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
1054         NULL, /* RootDirectory */
1055         NULL); /* SecurityDescriptor */
1056 
1057     Status = ZwCreateKey(
1058         &ClassKey,
1059         KEY_WRITE,
1060         &ObjectAttributes,
1061         0, /* TileIndex */
1062         NULL, /* Class */
1063         REG_OPTION_NON_VOLATILE,
1064         NULL); /* Disposition */
1065 
1066     if (!NT_SUCCESS(Status))
1067     {
1068         DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
1069         ExFreePool(BaseKeyName.Buffer);
1070         return Status;
1071     }
1072 
1073     /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
1074     InterfaceKeyName.Length = 0;
1075     InterfaceKeyName.MaximumLength =
1076         4 * sizeof(WCHAR) + /* 4  = size of ##?# */
1077         InstancePath->Length +
1078         sizeof(WCHAR) +     /* 1  = size of # */
1079         GuidString.Length;
1080     InterfaceKeyName.Buffer = ExAllocatePool(
1081         PagedPool,
1082         InterfaceKeyName.MaximumLength);
1083     if (!InterfaceKeyName.Buffer)
1084     {
1085         DPRINT("ExAllocatePool() failed\n");
1086         return STATUS_INSUFFICIENT_RESOURCES;
1087     }
1088 
1089     RtlAppendUnicodeToString(&InterfaceKeyName, L"##?#");
1090     StartIndex = InterfaceKeyName.Length / sizeof(WCHAR);
1091     RtlAppendUnicodeStringToString(&InterfaceKeyName, InstancePath);
1092     for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
1093     {
1094         if (InterfaceKeyName.Buffer[StartIndex + i] == '\\')
1095             InterfaceKeyName.Buffer[StartIndex + i] = '#';
1096     }
1097     RtlAppendUnicodeToString(&InterfaceKeyName, L"#");
1098     RtlAppendUnicodeStringToString(&InterfaceKeyName, &GuidString);
1099 
1100     /* Create the interface key in registry */
1101     InitializeObjectAttributes(
1102         &ObjectAttributes,
1103         &InterfaceKeyName,
1104         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
1105         ClassKey,
1106         NULL); /* SecurityDescriptor */
1107 
1108     Status = ZwCreateKey(
1109         &InterfaceKey,
1110         KEY_WRITE,
1111         &ObjectAttributes,
1112         0, /* TileIndex */
1113         NULL, /* Class */
1114         REG_OPTION_NON_VOLATILE,
1115         NULL); /* Disposition */
1116 
1117     if (!NT_SUCCESS(Status))
1118     {
1119         DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
1120         ZwClose(ClassKey);
1121         ExFreePool(BaseKeyName.Buffer);
1122         return Status;
1123     }
1124 
1125     /* Write DeviceInstance entry. Value is InstancePath */
1126     Status = ZwSetValueKey(
1127         InterfaceKey,
1128         &DeviceInstance,
1129         0, /* TileIndex */
1130         REG_SZ,
1131         InstancePath->Buffer,
1132         InstancePath->Length);
1133     if (!NT_SUCCESS(Status))
1134     {
1135         DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
1136         ZwClose(InterfaceKey);
1137         ZwClose(ClassKey);
1138         ExFreePool(InterfaceKeyName.Buffer);
1139         ExFreePool(BaseKeyName.Buffer);
1140         return Status;
1141     }
1142 
1143     /* Create subkey. Name is #ReferenceString */
1144     SubKeyName.Length = 0;
1145     SubKeyName.MaximumLength = sizeof(WCHAR);
1146     if (ReferenceString && ReferenceString->Length)
1147         SubKeyName.MaximumLength += ReferenceString->Length;
1148     SubKeyName.Buffer = ExAllocatePool(
1149         PagedPool,
1150         SubKeyName.MaximumLength);
1151     if (!SubKeyName.Buffer)
1152     {
1153         DPRINT("ExAllocatePool() failed\n");
1154         ZwClose(InterfaceKey);
1155         ZwClose(ClassKey);
1156         ExFreePool(InterfaceKeyName.Buffer);
1157         ExFreePool(BaseKeyName.Buffer);
1158         return STATUS_INSUFFICIENT_RESOURCES;
1159     }
1160     RtlAppendUnicodeToString(&SubKeyName, L"#");
1161     if (ReferenceString && ReferenceString->Length)
1162         RtlAppendUnicodeStringToString(&SubKeyName, ReferenceString);
1163 
1164     /* Create SubKeyName key in registry */
1165     InitializeObjectAttributes(
1166         &ObjectAttributes,
1167         &SubKeyName,
1168         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1169         InterfaceKey, /* RootDirectory */
1170         NULL); /* SecurityDescriptor */
1171 
1172     Status = ZwCreateKey(
1173         &SubKey,
1174         KEY_WRITE,
1175         &ObjectAttributes,
1176         0, /* TileIndex */
1177         NULL, /* Class */
1178         REG_OPTION_NON_VOLATILE,
1179         NULL); /* Disposition */
1180 
1181     if (!NT_SUCCESS(Status))
1182     {
1183         DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
1184         ZwClose(InterfaceKey);
1185         ZwClose(ClassKey);
1186         ExFreePool(InterfaceKeyName.Buffer);
1187         ExFreePool(BaseKeyName.Buffer);
1188         return Status;
1189     }
1190 
1191     /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1192     SymbolicLinkName->Length = 0;
1193     SymbolicLinkName->MaximumLength = SymbolicLinkName->Length
1194         + 4 * sizeof(WCHAR) /* 4 = size of \??\ */
1195         + InstancePath->Length
1196         + sizeof(WCHAR)     /* 1  = size of # */
1197         + GuidString.Length
1198         + sizeof(WCHAR);    /* final NULL */
1199     if (ReferenceString && ReferenceString->Length)
1200         SymbolicLinkName->MaximumLength += sizeof(WCHAR) + ReferenceString->Length;
1201     SymbolicLinkName->Buffer = ExAllocatePool(
1202         PagedPool,
1203         SymbolicLinkName->MaximumLength);
1204     if (!SymbolicLinkName->Buffer)
1205     {
1206         DPRINT("ExAllocatePool() failed\n");
1207         ZwClose(SubKey);
1208         ZwClose(InterfaceKey);
1209         ZwClose(ClassKey);
1210         ExFreePool(InterfaceKeyName.Buffer);
1211         ExFreePool(SubKeyName.Buffer);
1212         ExFreePool(BaseKeyName.Buffer);
1213         return STATUS_INSUFFICIENT_RESOURCES;
1214     }
1215     RtlAppendUnicodeToString(SymbolicLinkName, L"\\??\\");
1216     StartIndex = SymbolicLinkName->Length / sizeof(WCHAR);
1217     RtlAppendUnicodeStringToString(SymbolicLinkName, InstancePath);
1218     for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
1219     {
1220         if (SymbolicLinkName->Buffer[StartIndex + i] == '\\')
1221             SymbolicLinkName->Buffer[StartIndex + i] = '#';
1222     }
1223     RtlAppendUnicodeToString(SymbolicLinkName, L"#");
1224     RtlAppendUnicodeStringToString(SymbolicLinkName, &GuidString);
1225     SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
1226 
1227     /* Create symbolic link */
1228     DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName, &PdoNameInfo->Name);
1229     SymLinkStatus = IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name);
1230 
1231     /* If the symbolic link already exists, return an informational success status */
1232     if (SymLinkStatus == STATUS_OBJECT_NAME_COLLISION)
1233     {
1234         /* HACK: Delete the existing symbolic link and update it to the new PDO name */
1235         IoDeleteSymbolicLink(SymbolicLinkName);
1236         IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name);
1237         SymLinkStatus = STATUS_OBJECT_NAME_EXISTS;
1238     }
1239 
1240     if (!NT_SUCCESS(SymLinkStatus))
1241     {
1242         DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", SymLinkStatus);
1243         ZwClose(SubKey);
1244         ZwClose(InterfaceKey);
1245         ZwClose(ClassKey);
1246         ExFreePool(SubKeyName.Buffer);
1247         ExFreePool(InterfaceKeyName.Buffer);
1248         ExFreePool(BaseKeyName.Buffer);
1249         ExFreePool(SymbolicLinkName->Buffer);
1250         return SymLinkStatus;
1251     }
1252 
1253     if (ReferenceString && ReferenceString->Length)
1254     {
1255         RtlAppendUnicodeToString(SymbolicLinkName, L"\\");
1256         RtlAppendUnicodeStringToString(SymbolicLinkName, ReferenceString);
1257     }
1258     SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
1259 
1260     /* Write symbolic link name in registry */
1261     SymbolicLinkName->Buffer[1] = '\\';
1262     Status = ZwSetValueKey(
1263         SubKey,
1264         &SymbolicLink,
1265         0, /* TileIndex */
1266         REG_SZ,
1267         SymbolicLinkName->Buffer,
1268         SymbolicLinkName->Length);
1269     if (!NT_SUCCESS(Status))
1270     {
1271         DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status);
1272         ExFreePool(SymbolicLinkName->Buffer);
1273     }
1274     else
1275     {
1276         SymbolicLinkName->Buffer[1] = '?';
1277     }
1278 
1279     ZwClose(SubKey);
1280     ZwClose(InterfaceKey);
1281     ZwClose(ClassKey);
1282     ExFreePool(SubKeyName.Buffer);
1283     ExFreePool(InterfaceKeyName.Buffer);
1284     ExFreePool(BaseKeyName.Buffer);
1285 
1286     return NT_SUCCESS(Status) ? SymLinkStatus : Status;
1287 }
1288 
1289 /*++
1290  * @name IoSetDeviceInterfaceState
1291  * @implemented
1292  *
1293  * Enables or disables an instance of a previously registered device
1294  * interface class.
1295  * Documented in WDK.
1296  *
1297  * @param SymbolicLinkName
1298  *        Pointer to the string identifying instance to enable or disable
1299  *
1300  * @param Enable
1301  *        TRUE = enable, FALSE = disable
1302  *
1303  * @return Usual NTSTATUS
1304  *
1305  * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
1306  *          system thread
1307  *
1308  *--*/
1309 NTSTATUS
1310 NTAPI
1311 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
1312                           IN BOOLEAN Enable)
1313 {
1314     PDEVICE_OBJECT PhysicalDeviceObject;
1315     UNICODE_STRING GuidString;
1316     NTSTATUS Status;
1317     LPCGUID EventGuid;
1318     HANDLE InstanceHandle, ControlHandle;
1319     UNICODE_STRING KeyName, DeviceInstance;
1320     OBJECT_ATTRIBUTES ObjectAttributes;
1321     ULONG LinkedValue, Index;
1322     GUID DeviceGuid;
1323     UNICODE_STRING DosDevicesPrefix1 = RTL_CONSTANT_STRING(L"\\??\\");
1324     UNICODE_STRING DosDevicesPrefix2 = RTL_CONSTANT_STRING(L"\\\\?\\");
1325     UNICODE_STRING LinkNameNoPrefix;
1326     USHORT i;
1327     USHORT ReferenceStringOffset;
1328 
1329     if (SymbolicLinkName == NULL)
1330     {
1331         return STATUS_INVALID_PARAMETER;
1332     }
1333 
1334     DPRINT("IoSetDeviceInterfaceState('%wZ', %u)\n", SymbolicLinkName, Enable);
1335 
1336     /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1337     /* Make sure it starts with the expected prefix */
1338     if (!RtlPrefixUnicodeString(&DosDevicesPrefix1, SymbolicLinkName, FALSE) &&
1339         !RtlPrefixUnicodeString(&DosDevicesPrefix2, SymbolicLinkName, FALSE))
1340     {
1341         DPRINT1("IoSetDeviceInterfaceState() invalid link name '%wZ'\n", SymbolicLinkName);
1342         return STATUS_INVALID_PARAMETER;
1343     }
1344 
1345     /* Make a version without the prefix for further processing */
1346     ASSERT(DosDevicesPrefix1.Length == DosDevicesPrefix2.Length);
1347     ASSERT(SymbolicLinkName->Length >= DosDevicesPrefix1.Length);
1348     LinkNameNoPrefix.Buffer = SymbolicLinkName->Buffer + DosDevicesPrefix1.Length / sizeof(WCHAR);
1349     LinkNameNoPrefix.Length = SymbolicLinkName->Length - DosDevicesPrefix1.Length;
1350     LinkNameNoPrefix.MaximumLength = LinkNameNoPrefix.Length;
1351 
1352     /* Find the reference string, if any */
1353     for (i = 0; i < LinkNameNoPrefix.Length / sizeof(WCHAR); i++)
1354     {
1355         if (LinkNameNoPrefix.Buffer[i] == L'\\')
1356         {
1357             break;
1358         }
1359     }
1360     ReferenceStringOffset = i * sizeof(WCHAR);
1361 
1362     /* The GUID is before the reference string or at the end */
1363     ASSERT(LinkNameNoPrefix.Length >= ReferenceStringOffset);
1364     if (ReferenceStringOffset < GUID_STRING_BYTES + sizeof(WCHAR))
1365     {
1366         DPRINT1("IoSetDeviceInterfaceState() invalid link name '%wZ'\n", SymbolicLinkName);
1367         return STATUS_INVALID_PARAMETER;
1368     }
1369 
1370     GuidString.Buffer = LinkNameNoPrefix.Buffer + (ReferenceStringOffset - GUID_STRING_BYTES) / sizeof(WCHAR);
1371     GuidString.Length = GUID_STRING_BYTES;
1372     GuidString.MaximumLength = GuidString.Length;
1373     Status = RtlGUIDFromString(&GuidString, &DeviceGuid);
1374     if (!NT_SUCCESS(Status))
1375     {
1376         DPRINT1("RtlGUIDFromString() invalid GUID '%wZ' in link name '%wZ'\n", &GuidString, SymbolicLinkName);
1377         return Status;
1378     }
1379 
1380     /* Open registry keys */
1381     Status = OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName,
1382                                                  KEY_CREATE_SUB_KEY,
1383                                                  NULL,
1384                                                  NULL,
1385                                                  &InstanceHandle);
1386     if (!NT_SUCCESS(Status))
1387         return Status;
1388 
1389     RtlInitUnicodeString(&KeyName, L"Control");
1390     InitializeObjectAttributes(&ObjectAttributes,
1391                                &KeyName,
1392                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1393                                InstanceHandle,
1394                                NULL);
1395     Status = ZwCreateKey(&ControlHandle,
1396                          KEY_SET_VALUE,
1397                          &ObjectAttributes,
1398                          0,
1399                          NULL,
1400                          REG_OPTION_VOLATILE,
1401                          NULL);
1402     ZwClose(InstanceHandle);
1403     if (!NT_SUCCESS(Status))
1404     {
1405         DPRINT1("Failed to create the Control subkey\n");
1406         return Status;
1407     }
1408 
1409     LinkedValue = (Enable ? 1 : 0);
1410 
1411     RtlInitUnicodeString(&KeyName, L"Linked");
1412     Status = ZwSetValueKey(ControlHandle,
1413                            &KeyName,
1414                            0,
1415                            REG_DWORD,
1416                            &LinkedValue,
1417                            sizeof(ULONG));
1418     ZwClose(ControlHandle);
1419     if (!NT_SUCCESS(Status))
1420     {
1421         DPRINT1("Failed to write the Linked value\n");
1422         return Status;
1423     }
1424 
1425     ASSERT(GuidString.Buffer >= LinkNameNoPrefix.Buffer + 1);
1426     DeviceInstance.Length = (GuidString.Buffer - LinkNameNoPrefix.Buffer - 1) * sizeof(WCHAR);
1427     if (DeviceInstance.Length == 0)
1428     {
1429         DPRINT1("No device instance in link name '%wZ'\n", SymbolicLinkName);
1430         return STATUS_OBJECT_NAME_NOT_FOUND;
1431     }
1432     DeviceInstance.MaximumLength = DeviceInstance.Length;
1433     DeviceInstance.Buffer = ExAllocatePoolWithTag(PagedPool,
1434                                                   DeviceInstance.MaximumLength,
1435                                                   TAG_IO);
1436     if (DeviceInstance.Buffer == NULL)
1437     {
1438         /* no memory */
1439         return STATUS_INSUFFICIENT_RESOURCES;
1440     }
1441 
1442     RtlCopyMemory(DeviceInstance.Buffer,
1443                   LinkNameNoPrefix.Buffer,
1444                   DeviceInstance.Length);
1445 
1446     for (Index = 0; Index < DeviceInstance.Length / sizeof(WCHAR); Index++)
1447     {
1448         if (DeviceInstance.Buffer[Index] == L'#')
1449         {
1450             DeviceInstance.Buffer[Index] = L'\\';
1451         }
1452     }
1453 
1454     PhysicalDeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1455 
1456     if (!PhysicalDeviceObject)
1457     {
1458         DPRINT1("IopGetDeviceObjectFromDeviceInstance failed to find device object for %wZ\n", &DeviceInstance);
1459         ExFreePoolWithTag(DeviceInstance.Buffer, TAG_IO);
1460         return STATUS_OBJECT_NAME_NOT_FOUND;
1461     }
1462 
1463     ExFreePoolWithTag(DeviceInstance.Buffer, TAG_IO);
1464 
1465     EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
1466 
1467     PiNotifyDeviceInterfaceChange(EventGuid, &DeviceGuid, SymbolicLinkName);
1468     IopQueueDeviceChangeEvent(EventGuid, &DeviceGuid, SymbolicLinkName);
1469 
1470     ObDereferenceObject(PhysicalDeviceObject);
1471     DPRINT("Status %x\n", Status);
1472     return STATUS_SUCCESS;
1473 }
1474 
1475 /* EOF */
1476