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