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