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