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