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