Rewrite IoGetDeviceInterfaces: no more memory leaks, be ready to handle PhysicalDevic...
[reactos.git] / reactos / ntoskrnl / io / deviface.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/io/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 <internal/debug.h>
18
19 /* FUNCTIONS *****************************************************************/
20
21 static PWCHAR BaseKeyString = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
22
23 /*
24 * @unimplemented
25 */
26
27 NTSTATUS STDCALL
28 IoOpenDeviceInterfaceRegistryKey(
29 IN PUNICODE_STRING SymbolicLinkName,
30 IN ACCESS_MASK DesiredAccess,
31 OUT PHANDLE DeviceInterfaceKey)
32 {
33 return STATUS_NOT_IMPLEMENTED;
34 }
35
36 /*
37 * @unimplemented
38 */
39
40 NTSTATUS STDCALL
41 IoGetDeviceInterfaceAlias(
42 IN PUNICODE_STRING SymbolicLinkName,
43 IN CONST GUID *AliasInterfaceClassGuid,
44 OUT PUNICODE_STRING AliasSymbolicLinkName)
45 {
46 return STATUS_NOT_IMPLEMENTED;
47 }
48
49 static NTSTATUS
50 IopOpenInterfaceKey(
51 IN CONST GUID *InterfaceClassGuid,
52 IN ACCESS_MASK DesiredAccess,
53 OUT HANDLE *pInterfaceKey)
54 {
55 UNICODE_STRING LocalMachine = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\");
56 UNICODE_STRING GuidString;
57 UNICODE_STRING KeyName;
58 OBJECT_ATTRIBUTES ObjectAttributes;
59 HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
60 NTSTATUS Status;
61
62 GuidString.Buffer = KeyName.Buffer = NULL;
63
64 Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
65 if (!NT_SUCCESS(Status))
66 {
67 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
68 goto cleanup;
69 }
70
71 KeyName.Length = 0;
72 KeyName.MaximumLength = LocalMachine.Length + (wcslen(REGSTR_PATH_DEVICE_CLASSES) + 1) * sizeof(WCHAR) + GuidString.Length;
73 KeyName.Buffer = ExAllocatePool(PagedPool, KeyName.MaximumLength);
74 if (!KeyName.Buffer)
75 {
76 DPRINT("ExAllocatePool() failed\n");
77 Status = STATUS_INSUFFICIENT_RESOURCES;
78 goto cleanup;
79 }
80
81 Status = RtlAppendUnicodeStringToString(&KeyName, &LocalMachine);
82 if (!NT_SUCCESS(Status))
83 {
84 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
85 goto cleanup;
86 }
87 Status = RtlAppendUnicodeToString(&KeyName, REGSTR_PATH_DEVICE_CLASSES);
88 if (!NT_SUCCESS(Status))
89 {
90 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
91 goto cleanup;
92 }
93 Status = RtlAppendUnicodeToString(&KeyName, L"\\");
94 if (!NT_SUCCESS(Status))
95 {
96 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
97 goto cleanup;
98 }
99 Status = RtlAppendUnicodeStringToString(&KeyName, &GuidString);
100 if (!NT_SUCCESS(Status))
101 {
102 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
103 goto cleanup;
104 }
105
106 InitializeObjectAttributes(
107 &ObjectAttributes,
108 &KeyName,
109 OBJ_CASE_INSENSITIVE,
110 NULL,
111 NULL);
112 Status = ZwOpenKey(
113 &InterfaceKey,
114 DesiredAccess,
115 &ObjectAttributes);
116 if (!NT_SUCCESS(Status))
117 {
118 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
119 goto cleanup;
120 }
121
122 *pInterfaceKey = InterfaceKey;
123 Status = STATUS_SUCCESS;
124
125 cleanup:
126 if (!NT_SUCCESS(Status))
127 {
128 if (InterfaceKey != INVALID_HANDLE_VALUE)
129 ZwClose(InterfaceKey);
130 }
131 RtlFreeUnicodeString(&GuidString);
132 RtlFreeUnicodeString(&KeyName);
133 return Status;
134 }
135
136 /*
137 * IoGetDeviceInterfaces
138 *
139 * Returns a list of device interfaces of a particular device interface class.
140 *
141 * Parameters
142 * InterfaceClassGuid
143 * Points to a class GUID specifying the device interface class.
144 *
145 * PhysicalDeviceObject
146 * Points to an optional PDO that narrows the search to only the
147 * device interfaces of the device represented by the PDO.
148 *
149 * Flags
150 * Specifies flags that modify the search for device interfaces. The
151 * DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
152 * returned symbolic links should contain also disabled device
153 * interfaces in addition to the enabled ones.
154 *
155 * SymbolicLinkList
156 * Points to a character pointer that is filled in on successful return
157 * with a list of unicode strings identifying the device interfaces
158 * that match the search criteria. The newly allocated buffer contains
159 * a list of symbolic link names. Each unicode string in the list is
160 * null-terminated; the end of the whole list is marked by an additional
161 * NULL. The caller is responsible for freeing the buffer (ExFreePool)
162 * when it is no longer needed.
163 * If no device interfaces match the search criteria, this routine
164 * returns STATUS_SUCCESS and the string contains a single NULL
165 * character.
166 *
167 * Status
168 * @implemented
169 *
170 */
171
172 NTSTATUS STDCALL
173 IoGetDeviceInterfaces(
174 IN CONST GUID *InterfaceClassGuid,
175 IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
176 IN ULONG Flags,
177 OUT PWSTR *SymbolicLinkList)
178 {
179 UNICODE_STRING Control = RTL_CONSTANT_STRING(L"Control");
180 UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
181 HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
182 HANDLE DeviceKey = INVALID_HANDLE_VALUE;
183 HANDLE ReferenceKey = INVALID_HANDLE_VALUE;
184 HANDLE ControlKey = INVALID_HANDLE_VALUE;
185 PKEY_BASIC_INFORMATION DeviceBi = NULL;
186 PKEY_BASIC_INFORMATION ReferenceBi = NULL;
187 PKEY_VALUE_PARTIAL_INFORMATION bip = NULL;
188 UNICODE_STRING KeyName;
189 OBJECT_ATTRIBUTES ObjectAttributes;
190 BOOLEAN FoundRightPDO = FALSE;
191 ULONG i = 0, j, Size;
192 UNICODE_STRING ReturnBuffer = {0,};
193 NTSTATUS Status;
194
195 PAGED_CODE();
196
197 Status = IopOpenInterfaceKey(InterfaceClassGuid, KEY_ENUMERATE_SUB_KEYS, &InterfaceKey);
198 if (!NT_SUCCESS(Status))
199 {
200 DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status);
201 goto cleanup;
202 }
203
204 /* Enumerate subkeys (ie the different device objets) */
205 while (TRUE)
206 {
207 Status = ZwEnumerateKey(
208 InterfaceKey,
209 i,
210 KeyBasicInformation,
211 NULL,
212 0,
213 &Size);
214 if (Status == STATUS_NO_MORE_ENTRIES)
215 break;
216 else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
217 {
218 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
219 goto cleanup;
220 }
221
222 DeviceBi = ExAllocatePool(PagedPool, Size);
223 if (!DeviceBi)
224 {
225 DPRINT("ExAllocatePool() failed\n");
226 Status = STATUS_INSUFFICIENT_RESOURCES;
227 goto cleanup;
228 }
229 Status = ZwEnumerateKey(
230 InterfaceKey,
231 i++,
232 KeyBasicInformation,
233 DeviceBi,
234 Size,
235 &Size);
236 if (!NT_SUCCESS(Status))
237 {
238 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
239 goto cleanup;
240 }
241
242 /* Open device key */
243 KeyName.Length = KeyName.MaximumLength = DeviceBi->NameLength;
244 KeyName.Buffer = DeviceBi->Name;
245 InitializeObjectAttributes(
246 &ObjectAttributes,
247 &KeyName,
248 OBJ_CASE_INSENSITIVE,
249 InterfaceKey,
250 NULL);
251 Status = ZwOpenKey(
252 &DeviceKey,
253 KEY_ENUMERATE_SUB_KEYS,
254 &ObjectAttributes);
255 if (!NT_SUCCESS(Status))
256 {
257 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
258 goto cleanup;
259 }
260
261 if (PhysicalDeviceObject)
262 {
263 /* Check if we are on the right physical device object,
264 * by reading the DeviceInstance string
265 */
266 DPRINT1("PhysicalDeviceObject != NULL. Case not implemented.\n");
267 //FoundRightPDO = TRUE;
268 Status = STATUS_NOT_IMPLEMENTED;
269 goto cleanup;
270 }
271
272 /* Enumerate subkeys (ie the different reference strings) */
273 j = 0;
274 while (TRUE)
275 {
276 Status = ZwEnumerateKey(
277 DeviceKey,
278 j,
279 KeyBasicInformation,
280 NULL,
281 0,
282 &Size);
283 if (Status == STATUS_NO_MORE_ENTRIES)
284 break;
285 else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
286 {
287 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
288 goto cleanup;
289 }
290
291 ReferenceBi = ExAllocatePool(PagedPool, Size);
292 if (!ReferenceBi)
293 {
294 DPRINT("ExAllocatePool() failed\n");
295 Status = STATUS_INSUFFICIENT_RESOURCES;
296 goto cleanup;
297 }
298 Status = ZwEnumerateKey(
299 DeviceKey,
300 j++,
301 KeyBasicInformation,
302 ReferenceBi,
303 Size,
304 &Size);
305 if (!NT_SUCCESS(Status))
306 {
307 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
308 goto cleanup;
309 }
310
311 KeyName.Length = KeyName.MaximumLength = ReferenceBi->NameLength;
312 KeyName.Buffer = ReferenceBi->Name;
313 if (RtlEqualUnicodeString(&KeyName, &Control, TRUE))
314 {
315 /* Skip Control subkey */
316 goto NextReferenceString;
317 }
318
319 /* Open reference key */
320 InitializeObjectAttributes(
321 &ObjectAttributes,
322 &KeyName,
323 OBJ_CASE_INSENSITIVE,
324 DeviceKey,
325 NULL);
326 Status = ZwOpenKey(
327 &ReferenceKey,
328 KEY_QUERY_VALUE,
329 &ObjectAttributes);
330 if (!NT_SUCCESS(Status))
331 {
332 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
333 goto cleanup;
334 }
335
336 if (!(Flags & DEVICE_INTERFACE_INCLUDE_NONACTIVE))
337 {
338 /* We have to check if the interface is enabled, by
339 * reading the Linked value in the Control subkey
340 */
341 InitializeObjectAttributes(
342 &ObjectAttributes,
343 &Control,
344 OBJ_CASE_INSENSITIVE,
345 ReferenceKey,
346 NULL);
347 Status = ZwOpenKey(
348 &ControlKey,
349 KEY_QUERY_VALUE,
350 &ObjectAttributes);
351 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
352 {
353 /* That's OK. The key doesn't exist (yet) because
354 * the interface is not activated.
355 */
356 goto NextReferenceString;
357 }
358 else if (!NT_SUCCESS(Status))
359 {
360 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
361 goto cleanup;
362 }
363 /* FIXME: Read the Linked value
364 * If it doesn't exist => ERROR
365 * If it is not a REG_DWORD or Size != sizeof(ULONG) => ERROR
366 * If its value is 0, go to NextReferenceString
367 * At the moment, do as if it is active...
368 */
369 DPRINT1("Checking if device is enabled is not implemented yet!\n");
370 }
371
372 /* Read the SymbolicLink string and add it into SymbolicLinkList */
373 Status = ZwQueryValueKey(
374 ReferenceKey,
375 &SymbolicLink,
376 KeyValuePartialInformation,
377 NULL,
378 0,
379 &Size);
380 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
381 {
382 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
383 goto cleanup;
384 }
385 bip = ExAllocatePool(PagedPool, Size);
386 if (!bip)
387 {
388 DPRINT("ExAllocatePool() failed\n");
389 Status = STATUS_INSUFFICIENT_RESOURCES;
390 goto cleanup;
391 }
392 Status = ZwQueryValueKey(
393 ReferenceKey,
394 &SymbolicLink,
395 KeyValuePartialInformation,
396 bip,
397 Size,
398 &Size);
399 if (!NT_SUCCESS(Status))
400 {
401 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
402 goto cleanup;
403 }
404 else if (bip->Type != REG_SZ)
405 {
406 DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip->Type, REG_SZ);
407 Status = STATUS_UNSUCCESSFUL;
408 goto cleanup;
409 }
410 else if (bip->DataLength < 5 * sizeof(WCHAR))
411 {
412 DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip->DataLength < 5 * sizeof(WCHAR));
413 Status = STATUS_UNSUCCESSFUL;
414 goto cleanup;
415 }
416 KeyName.Length = KeyName.MaximumLength = bip->DataLength - 4 * sizeof(WCHAR);
417 KeyName.Buffer = &((PWSTR)bip->Data)[4];
418 if (KeyName.Length && KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] == UNICODE_NULL)
419 /* Remove trailing NULL */
420 KeyName.Length -= sizeof(WCHAR);
421
422 /* Add new symbolic link to symbolic link list */
423 if (ReturnBuffer.Length + KeyName.Length + sizeof(WCHAR) > ReturnBuffer.MaximumLength)
424 {
425 PWSTR NewBuffer;
426 ReturnBuffer.MaximumLength = max(ReturnBuffer.MaximumLength * 2, ReturnBuffer.Length + KeyName.Length + 2 * sizeof(WCHAR));
427 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
428 if (!NewBuffer)
429 {
430 DPRINT("ExAllocatePool() failed\n");
431 Status = STATUS_INSUFFICIENT_RESOURCES;
432 goto cleanup;
433 }
434 RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
435 ExFreePool(ReturnBuffer.Buffer);
436 ReturnBuffer.Buffer = NewBuffer;
437 }
438 DPRINT("Adding symbolic link %wZ\n", &KeyName);
439 Status = RtlAppendUnicodeStringToString(&ReturnBuffer, &KeyName);
440 if (!NT_SUCCESS(Status))
441 {
442 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
443 goto cleanup;
444 }
445 /* RtlAppendUnicodeStringToString added a NULL at the end of the
446 * destination string, but didn't increase the Length field.
447 * Do it for it.
448 */
449 ReturnBuffer.Length += sizeof(WCHAR);
450
451 NextReferenceString:
452 ExFreePool(ReferenceBi);
453 ReferenceBi = NULL;
454 ExFreePool(bip);
455 bip = NULL;
456 if (ReferenceKey != INVALID_HANDLE_VALUE)
457 {
458 ZwClose(ReferenceKey);
459 ReferenceKey = INVALID_HANDLE_VALUE;
460 }
461 if (ControlKey != INVALID_HANDLE_VALUE)
462 {
463 ZwClose(ControlKey);
464 ControlKey = INVALID_HANDLE_VALUE;
465 }
466 }
467 if (FoundRightPDO)
468 {
469 /* No need to go further, as we already have found what we searched */
470 break;
471 }
472
473 ExFreePool(DeviceBi);
474 DeviceBi = NULL;
475 ZwClose(DeviceKey);
476 DeviceKey = INVALID_HANDLE_VALUE;
477 }
478
479 /* Add final NULL to ReturnBuffer */
480 if (ReturnBuffer.Length >= ReturnBuffer.MaximumLength)
481 {
482 PWSTR NewBuffer;
483 ReturnBuffer.MaximumLength += sizeof(WCHAR);
484 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
485 if (!NewBuffer)
486 {
487 DPRINT("ExAllocatePool() failed\n");
488 Status = STATUS_INSUFFICIENT_RESOURCES;
489 goto cleanup;
490 }
491 RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
492 ExFreePool(ReturnBuffer.Buffer);
493 ReturnBuffer.Buffer = NewBuffer;
494 }
495 ReturnBuffer.Buffer[ReturnBuffer.Length / sizeof(WCHAR)] = UNICODE_NULL;
496 *SymbolicLinkList = ReturnBuffer.Buffer;
497 Status = STATUS_SUCCESS;
498
499 cleanup:
500 if (!NT_SUCCESS(Status))
501 ExFreePool(ReturnBuffer.Buffer);
502 if (InterfaceKey != INVALID_HANDLE_VALUE)
503 ZwClose(InterfaceKey);
504 if (DeviceKey != INVALID_HANDLE_VALUE)
505 ZwClose(DeviceKey);
506 if (ReferenceKey != INVALID_HANDLE_VALUE)
507 ZwClose(ReferenceKey);
508 if (ControlKey != INVALID_HANDLE_VALUE)
509 ZwClose(ControlKey);
510 ExFreePool(DeviceBi);
511 ExFreePool(ReferenceBi);
512 ExFreePool(bip);
513 return Status;
514 }
515
516 /*
517 * @implemented
518 */
519
520 NTSTATUS STDCALL
521 IoRegisterDeviceInterface(
522 IN PDEVICE_OBJECT PhysicalDeviceObject,
523 IN CONST GUID *InterfaceClassGuid,
524 IN PUNICODE_STRING ReferenceString OPTIONAL,
525 OUT PUNICODE_STRING SymbolicLinkName)
526 {
527 PUNICODE_STRING InstancePath;
528 UNICODE_STRING GuidString;
529 UNICODE_STRING SubKeyName;
530 UNICODE_STRING InterfaceKeyName;
531 UNICODE_STRING BaseKeyName;
532 UCHAR PdoNameInfoBuffer[sizeof(OBJECT_NAME_INFORMATION) + (256 * sizeof(WCHAR))];
533 POBJECT_NAME_INFORMATION PdoNameInfo = (POBJECT_NAME_INFORMATION)PdoNameInfoBuffer;
534 UNICODE_STRING DeviceInstance = RTL_CONSTANT_STRING(L"DeviceInstance");
535 UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
536 HANDLE ClassKey;
537 HANDLE InterfaceKey;
538 HANDLE SubKey;
539 ULONG StartIndex;
540 OBJECT_ATTRIBUTES ObjectAttributes;
541 ULONG i;
542 NTSTATUS Status;
543
544 ASSERT_IRQL(PASSIVE_LEVEL);
545
546 if (!(PhysicalDeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE))
547 {
548 DPRINT("PhysicalDeviceObject 0x%p is not a valid Pdo\n", PhysicalDeviceObject);
549 return STATUS_INVALID_PARAMETER_1;
550 }
551
552 Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
553 if (!NT_SUCCESS(Status))
554 {
555 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
556 return Status;
557 }
558
559 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
560 Status = ObQueryNameString(
561 PhysicalDeviceObject,
562 PdoNameInfo,
563 sizeof(PdoNameInfoBuffer),
564 &i);
565 if (!NT_SUCCESS(Status))
566 {
567 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status);
568 return Status;
569 }
570 ASSERT(PdoNameInfo->Name.Length);
571
572 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
573 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode);
574 InstancePath = &((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode->InstancePath;
575 BaseKeyName.Length = wcslen(BaseKeyString) * sizeof(WCHAR);
576 BaseKeyName.MaximumLength = BaseKeyName.Length
577 + GuidString.Length;
578 BaseKeyName.Buffer = ExAllocatePool(
579 PagedPool,
580 BaseKeyName.MaximumLength);
581 if (!BaseKeyName.Buffer)
582 {
583 DPRINT("ExAllocatePool() failed\n");
584 return STATUS_INSUFFICIENT_RESOURCES;
585 }
586 wcscpy(BaseKeyName.Buffer, BaseKeyString);
587 RtlAppendUnicodeStringToString(&BaseKeyName, &GuidString);
588
589 /* Create BaseKeyName key in registry */
590 InitializeObjectAttributes(
591 &ObjectAttributes,
592 &BaseKeyName,
593 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
594 NULL, /* RootDirectory */
595 NULL); /* SecurityDescriptor */
596
597 Status = ZwCreateKey(
598 &ClassKey,
599 KEY_WRITE,
600 &ObjectAttributes,
601 0, /* TileIndex */
602 NULL, /* Class */
603 REG_OPTION_VOLATILE,
604 NULL); /* Disposition */
605
606 if (!NT_SUCCESS(Status))
607 {
608 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
609 ExFreePool(BaseKeyName.Buffer);
610 return Status;
611 }
612
613 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
614 InterfaceKeyName.Length = 0;
615 InterfaceKeyName.MaximumLength =
616 4 * sizeof(WCHAR) + /* 4 = size of ##?# */
617 InstancePath->Length +
618 sizeof(WCHAR) + /* 1 = size of # */
619 GuidString.Length;
620 InterfaceKeyName.Buffer = ExAllocatePool(
621 PagedPool,
622 InterfaceKeyName.MaximumLength);
623 if (!InterfaceKeyName.Buffer)
624 {
625 DPRINT("ExAllocatePool() failed\n");
626 return STATUS_INSUFFICIENT_RESOURCES;
627 }
628
629 RtlAppendUnicodeToString(&InterfaceKeyName, L"##?#");
630 StartIndex = InterfaceKeyName.Length / sizeof(WCHAR);
631 RtlAppendUnicodeStringToString(&InterfaceKeyName, InstancePath);
632 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
633 {
634 if (InterfaceKeyName.Buffer[StartIndex + i] == '\\')
635 InterfaceKeyName.Buffer[StartIndex + i] = '#';
636 }
637 RtlAppendUnicodeToString(&InterfaceKeyName, L"#");
638 RtlAppendUnicodeStringToString(&InterfaceKeyName, &GuidString);
639
640 /* Create the interface key in registry */
641 InitializeObjectAttributes(
642 &ObjectAttributes,
643 &InterfaceKeyName,
644 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
645 ClassKey,
646 NULL); /* SecurityDescriptor */
647
648 Status = ZwCreateKey(
649 &InterfaceKey,
650 KEY_WRITE,
651 &ObjectAttributes,
652 0, /* TileIndex */
653 NULL, /* Class */
654 REG_OPTION_VOLATILE,
655 NULL); /* Disposition */
656
657 if (!NT_SUCCESS(Status))
658 {
659 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
660 ZwClose(ClassKey);
661 ExFreePool(BaseKeyName.Buffer);
662 return Status;
663 }
664
665 /* Write DeviceInstance entry. Value is InstancePath */
666 Status = ZwSetValueKey(
667 InterfaceKey,
668 &DeviceInstance,
669 0, /* TileIndex */
670 REG_SZ,
671 InstancePath->Buffer,
672 InstancePath->Length);
673 if (!NT_SUCCESS(Status))
674 {
675 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
676 ZwClose(InterfaceKey);
677 ZwClose(ClassKey);
678 ExFreePool(InterfaceKeyName.Buffer);
679 ExFreePool(BaseKeyName.Buffer);
680 return Status;
681 }
682
683 /* Create subkey. Name is #ReferenceString */
684 SubKeyName.Length = 0;
685 SubKeyName.MaximumLength = sizeof(WCHAR);
686 if (ReferenceString && ReferenceString->Length)
687 SubKeyName.MaximumLength += ReferenceString->Length;
688 SubKeyName.Buffer = ExAllocatePool(
689 PagedPool,
690 SubKeyName.MaximumLength);
691 if (!SubKeyName.Buffer)
692 {
693 DPRINT("ExAllocatePool() failed\n");
694 ZwClose(InterfaceKey);
695 ZwClose(ClassKey);
696 ExFreePool(InterfaceKeyName.Buffer);
697 ExFreePool(BaseKeyName.Buffer);
698 return STATUS_INSUFFICIENT_RESOURCES;
699 }
700 RtlAppendUnicodeToString(&SubKeyName, L"#");
701 if (ReferenceString && ReferenceString->Length)
702 RtlAppendUnicodeStringToString(&SubKeyName, ReferenceString);
703
704 /* Create SubKeyName key in registry */
705 InitializeObjectAttributes(
706 &ObjectAttributes,
707 &SubKeyName,
708 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
709 InterfaceKey, /* RootDirectory */
710 NULL); /* SecurityDescriptor */
711
712 Status = ZwCreateKey(
713 &SubKey,
714 KEY_WRITE,
715 &ObjectAttributes,
716 0, /* TileIndex */
717 NULL, /* Class */
718 REG_OPTION_VOLATILE,
719 NULL); /* Disposition */
720
721 if (!NT_SUCCESS(Status))
722 {
723 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
724 ZwClose(InterfaceKey);
725 ZwClose(ClassKey);
726 ExFreePool(InterfaceKeyName.Buffer);
727 ExFreePool(BaseKeyName.Buffer);
728 return Status;
729 }
730
731 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
732 SymbolicLinkName->Length = 0;
733 SymbolicLinkName->MaximumLength = SymbolicLinkName->Length
734 + 4 * sizeof(WCHAR) /* 4 = size of \??\ */
735 + InstancePath->Length
736 + sizeof(WCHAR) /* 1 = size of # */
737 + GuidString.Length
738 + sizeof(WCHAR); /* final NULL */
739 if (ReferenceString && ReferenceString->Length)
740 SymbolicLinkName->MaximumLength += sizeof(WCHAR) + ReferenceString->Length;
741 SymbolicLinkName->Buffer = ExAllocatePool(
742 PagedPool,
743 SymbolicLinkName->MaximumLength);
744 if (!SymbolicLinkName->Buffer)
745 {
746 DPRINT("ExAllocatePool() failed\n");
747 ZwClose(SubKey);
748 ZwClose(InterfaceKey);
749 ZwClose(ClassKey);
750 ExFreePool(InterfaceKeyName.Buffer);
751 ExFreePool(SubKeyName.Buffer);
752 ExFreePool(BaseKeyName.Buffer);
753 return STATUS_INSUFFICIENT_RESOURCES;
754 }
755 RtlAppendUnicodeToString(SymbolicLinkName, L"\\??\\");
756 StartIndex = SymbolicLinkName->Length / sizeof(WCHAR);
757 RtlAppendUnicodeStringToString(SymbolicLinkName, InstancePath);
758 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
759 {
760 if (SymbolicLinkName->Buffer[StartIndex + i] == '\\')
761 SymbolicLinkName->Buffer[StartIndex + i] = '#';
762 }
763 RtlAppendUnicodeToString(SymbolicLinkName, L"#");
764 RtlAppendUnicodeStringToString(SymbolicLinkName, &GuidString);
765 if (ReferenceString && ReferenceString->Length)
766 {
767 RtlAppendUnicodeToString(SymbolicLinkName, L"\\");
768 RtlAppendUnicodeStringToString(SymbolicLinkName, ReferenceString);
769 }
770 SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
771
772 /* Create symbolic link */
773 DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName, &PdoNameInfo->Name);
774 Status = IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name);
775 if (!NT_SUCCESS(Status))
776 {
777 DPRINT("IoCreateSymbolicLink() failed with status 0x%08lx\n", Status);
778 ZwClose(SubKey);
779 ZwClose(InterfaceKey);
780 ZwClose(ClassKey);
781 ExFreePool(SubKeyName.Buffer);
782 ExFreePool(InterfaceKeyName.Buffer);
783 ExFreePool(BaseKeyName.Buffer);
784 ExFreePool(SymbolicLinkName->Buffer);
785 return Status;
786 }
787
788 /* Write symbolic link name in registry */
789 SymbolicLinkName->Buffer[1] = '\\';
790 Status = ZwSetValueKey(
791 SubKey,
792 &SymbolicLink,
793 0, /* TileIndex */
794 REG_SZ,
795 SymbolicLinkName->Buffer,
796 SymbolicLinkName->Length);
797 if (!NT_SUCCESS(Status))
798 {
799 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
800 ExFreePool(SymbolicLinkName->Buffer);
801 }
802
803 /* Remove \\?\ at the start of symbolic link name */
804 SymbolicLinkName->Length -= 4 * sizeof(WCHAR);
805 SymbolicLinkName->MaximumLength -= 4 * sizeof(WCHAR);
806 RtlMoveMemory(
807 SymbolicLinkName->Buffer,
808 &SymbolicLinkName->Buffer[4],
809 SymbolicLinkName->Length);
810
811 ZwClose(SubKey);
812 ZwClose(InterfaceKey);
813 ZwClose(ClassKey);
814 ExFreePool(SubKeyName.Buffer);
815 ExFreePool(InterfaceKeyName.Buffer);
816 ExFreePool(BaseKeyName.Buffer);
817
818 return Status;
819 }
820
821 /*
822 * @implemented
823 */
824
825 NTSTATUS STDCALL
826 IoSetDeviceInterfaceState(
827 IN PUNICODE_STRING SymbolicLinkName,
828 IN BOOLEAN Enable)
829 {
830 PDEVICE_OBJECT PhysicalDeviceObject;
831 PFILE_OBJECT FileObject;
832 UNICODE_STRING ObjectName;
833 UNICODE_STRING GuidString;
834 PWCHAR StartPosition;
835 PWCHAR EndPosition;
836 NTSTATUS Status;
837 LPCGUID EventGuid;
838
839 if (SymbolicLinkName == NULL)
840 return STATUS_INVALID_PARAMETER_1;
841
842 DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
843
844 /* Symbolic link name is ACPI#PNP0501#1#{GUID}\ReferenceString */
845 /* Get GUID from SymbolicLinkName */
846 StartPosition = wcschr(SymbolicLinkName->Buffer, L'{');
847 EndPosition = wcschr(SymbolicLinkName->Buffer, L'}');
848 if (!StartPosition ||!EndPosition || StartPosition > EndPosition)
849 return STATUS_INVALID_PARAMETER_1;
850 GuidString.Buffer = StartPosition;
851 GuidString.MaximumLength = GuidString.Length = (ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)StartPosition;
852
853 /* Create \??\SymbolicLinkName string */
854 ObjectName.Length = 0;
855 ObjectName.MaximumLength = SymbolicLinkName->Length + 4 * sizeof(WCHAR);
856 ObjectName.Buffer = ExAllocatePool(PagedPool, ObjectName.MaximumLength);
857 if (!ObjectName.Buffer)
858 return STATUS_INSUFFICIENT_RESOURCES;
859 RtlAppendUnicodeToString(&ObjectName, L"\\??\\");
860 RtlAppendUnicodeStringToString(&ObjectName, SymbolicLinkName);
861
862 /* Get pointer to the PDO */
863 Status = IoGetDeviceObjectPointer(&ObjectName,
864 0, /* DesiredAccess */
865 &FileObject,
866 &PhysicalDeviceObject);
867 if (!NT_SUCCESS(Status))
868 {
869 ExFreePool(ObjectName.Buffer);
870 return Status;
871 }
872
873 EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
874 IopNotifyPlugPlayNotification(
875 PhysicalDeviceObject,
876 EventCategoryDeviceInterfaceChange,
877 EventGuid,
878 &GuidString,
879 (PVOID)SymbolicLinkName);
880
881 ObDereferenceObject(FileObject);
882 ExFreePool(ObjectName.Buffer);
883
884 return STATUS_SUCCESS;
885 }
886
887 /* EOF */