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