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