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