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