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