1 /* 2 * COPYRIGHT: See COPYING.ARM in the top level directory 3 * PROJECT: ReactOS UEFI Boot Library 4 * FILE: boot/environ/lib/misc/bootreg.c 5 * PURPOSE: Boot Library Boot Registry Wrapper for CMLIB 6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include "bl.h" 12 #include <bcd.h> 13 14 /* DEFINITIONS ***************************************************************/ 15 16 #define BI_FLUSH_HIVE 0x01 17 #define BI_HIVE_WRITEABLE 0x02 18 19 /* DATA STRUCTURES ***********************************************************/ 20 21 typedef struct _BI_KEY_HIVE 22 { 23 PHBASE_BLOCK BaseBlock; 24 ULONG HiveSize; 25 PBL_FILE_PATH_DESCRIPTOR FilePath; 26 CMHIVE Hive; 27 LONG ReferenceCount; 28 ULONG Flags; 29 PCM_KEY_NODE RootNode; 30 } BI_KEY_HIVE, *PBI_KEY_HIVE; 31 32 typedef struct _BI_KEY_OBJECT 33 { 34 PBI_KEY_HIVE KeyHive; 35 PCM_KEY_NODE KeyNode; 36 HCELL_INDEX KeyCell; 37 PWCHAR KeyName; 38 } BI_KEY_OBJECT, *PBI_KEY_OBJECT; 39 40 /* GLOBALS *******************************************************************/ 41 42 BOOLEAN BiHiveHashLibraryInitialized; 43 ULONGLONG HvSymcryptSeed; 44 45 /* FUNCTIONS *****************************************************************/ 46 47 BOOLEAN 48 HvIsInPlaceBaseBlockValid ( 49 _In_ PHBASE_BLOCK BaseBlock 50 ) 51 { 52 ULONG HiveLength, HeaderSum; 53 BOOLEAN Valid; 54 55 /* Assume failure */ 56 Valid = FALSE; 57 58 /* Check for incorrect signature, type, version, or format */ 59 if ((BaseBlock->Signature == 'fger') && 60 (BaseBlock->Type == 0) && 61 (BaseBlock->Major <= 1) && 62 (BaseBlock->Minor <= 5) && 63 (BaseBlock->Minor >= 3) && 64 (BaseBlock->Format == 1)) 65 { 66 /* Check for invalid hive size */ 67 HiveLength = BaseBlock->Length; 68 if (HiveLength) 69 { 70 /* Check for misaligned or too large hive size */ 71 if (!(HiveLength & 0xFFF) && HiveLength <= 0x7FFFE000) 72 { 73 /* Check for invalid header checksum */ 74 HeaderSum = HvpHiveHeaderChecksum(BaseBlock); 75 if (HeaderSum == BaseBlock->CheckSum) 76 { 77 /* All good */ 78 Valid = TRUE; 79 } 80 } 81 } 82 } 83 84 /* Return validity */ 85 return Valid; 86 } 87 88 PVOID 89 NTAPI 90 CmpAllocate ( 91 _In_ SIZE_T Size, 92 _In_ BOOLEAN Paged, 93 _In_ ULONG Tag 94 ) 95 { 96 UNREFERENCED_PARAMETER(Paged); 97 UNREFERENCED_PARAMETER(Tag); 98 99 /* Call the heap allocator */ 100 return BlMmAllocateHeap(Size); 101 } 102 103 VOID 104 NTAPI 105 CmpFree ( 106 _In_ PVOID Ptr, 107 _In_ ULONG Quota 108 ) 109 { 110 UNREFERENCED_PARAMETER(Quota); 111 112 /* Call the heap allocator */ 113 BlMmFreeHeap(Ptr); 114 } 115 116 VOID 117 BiDereferenceHive ( 118 _In_ HANDLE KeyHandle 119 ) 120 { 121 PBI_KEY_OBJECT KeyObject; 122 123 /* Get the key object */ 124 KeyObject = (PBI_KEY_OBJECT)KeyHandle; 125 126 /* Drop a reference on the parent hive */ 127 --KeyObject->KeyHive->ReferenceCount; 128 } 129 130 VOID 131 BiFlushHive ( 132 _In_ HANDLE KeyHandle 133 ) 134 { 135 /* Not yet implemented */ 136 EfiPrintf(L"NO reg flush\r\n"); 137 return; 138 } 139 140 VOID 141 BiCloseKey ( 142 _In_ HANDLE KeyHandle 143 ) 144 { 145 PBI_KEY_HIVE KeyHive; 146 PBI_KEY_OBJECT KeyObject; 147 148 /* Get the key object and hive */ 149 KeyObject = (PBI_KEY_OBJECT)KeyHandle; 150 KeyHive = KeyObject->KeyHive; 151 152 /* Check if we have a hive, or name, or key node */ 153 if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName)) 154 { 155 /* Drop a reference, see if it's the last one */ 156 BiDereferenceHive(KeyHandle); 157 if (!KeyHive->ReferenceCount) 158 { 159 /* Check if we should flush it */ 160 if (KeyHive->Flags & BI_FLUSH_HIVE) 161 { 162 BiFlushHive(KeyHandle); 163 } 164 165 /* Unmap the hive */ 166 MmPapFreePages(KeyHive->BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED); 167 168 /* Free the hive and hive path */ 169 BlMmFreeHeap(KeyHive->FilePath); 170 BlMmFreeHeap(KeyHive); 171 } 172 173 /* Check if a key name is present */ 174 if (KeyObject->KeyName) 175 { 176 /* Free it */ 177 BlMmFreeHeap(KeyObject->KeyName); 178 } 179 } 180 181 /* Free the object */ 182 BlMmFreeHeap(KeyObject); 183 } 184 185 NTSTATUS 186 BiOpenKey( 187 _In_ HANDLE ParentHandle, 188 _In_ PWCHAR KeyName, 189 _Out_ PHANDLE Handle 190 ) 191 { 192 PBI_KEY_OBJECT ParentKey, NewKey; 193 PBI_KEY_HIVE ParentHive; 194 NTSTATUS Status; 195 SIZE_T NameLength, SubNameLength, NameBytes; 196 PWCHAR NameStart, NameBuffer; 197 UNICODE_STRING KeyString; 198 HCELL_INDEX KeyCell; 199 PHHIVE Hive; 200 PCM_KEY_NODE ParentNode; 201 202 /* Convert from a handle to our key object */ 203 ParentKey = (PBI_KEY_OBJECT)ParentHandle; 204 205 /* Extract the hive and node information */ 206 ParentHive = ParentKey->KeyHive; 207 ParentNode = ParentKey->KeyNode; 208 Hive = &ParentKey->KeyHive->Hive.Hive; 209 210 /* Initialize variables */ 211 KeyCell = HCELL_NIL; 212 Status = STATUS_SUCCESS; 213 NameBuffer = NULL; 214 215 /* Loop as long as there's still portions of the key name in play */ 216 NameLength = wcslen(KeyName); 217 while (NameLength) 218 { 219 /* Find the first path separator */ 220 NameStart = wcschr(KeyName, OBJ_NAME_PATH_SEPARATOR); 221 if (NameStart) 222 { 223 /* Look only at the key before the separator */ 224 SubNameLength = NameStart - KeyName; 225 ++NameStart; 226 } 227 else 228 { 229 /* No path separator, this is the final leaf key */ 230 SubNameLength = NameLength; 231 } 232 233 /* Free the name buffer from the previous pass if needed */ 234 if (NameBuffer) 235 { 236 BlMmFreeHeap(NameBuffer); 237 } 238 239 /* Allocate a buffer to hold the name of this specific subkey only */ 240 NameBytes = SubNameLength * sizeof(WCHAR); 241 NameBuffer = BlMmAllocateHeap(NameBytes + sizeof(UNICODE_NULL)); 242 if (!NameBuffer) 243 { 244 Status = STATUS_NO_MEMORY; 245 goto Quickie; 246 } 247 248 /* Copy and null-terminate the name of the subkey */ 249 RtlCopyMemory(NameBuffer, KeyName, NameBytes); 250 NameBuffer[SubNameLength] = UNICODE_NULL; 251 252 /* Convert it into a UNICODE_STRING and try to find it */ 253 RtlInitUnicodeString(&KeyString, NameBuffer); 254 KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString); 255 if (KeyCell == HCELL_NIL) 256 { 257 Status = STATUS_OBJECT_NAME_NOT_FOUND; 258 goto Quickie; 259 } 260 261 /* We found it -- get the key node out of it */ 262 ParentNode = (PCM_KEY_NODE)HvGetCell(Hive, KeyCell); 263 if (!ParentNode) 264 { 265 Status = STATUS_REGISTRY_CORRUPT; 266 goto Quickie; 267 } 268 269 /* Update the key name to the next remaining path element */ 270 KeyName = NameStart; 271 if (NameStart) 272 { 273 /* Update the length to the remainder of the path */ 274 NameLength += -1 - SubNameLength; 275 } 276 else 277 { 278 /* There's nothing left, this was the leaf key */ 279 NameLength = 0; 280 } 281 } 282 283 /* Allocate a key object */ 284 NewKey = BlMmAllocateHeap(sizeof(*NewKey)); 285 if (!NewKey) 286 { 287 /* Bail out if we had no memory for it */ 288 Status = STATUS_NO_MEMORY; 289 goto Quickie; 290 } 291 292 /* Fill out the key object data */ 293 NewKey->KeyNode = ParentNode; 294 NewKey->KeyHive = ParentHive; 295 NewKey->KeyName = NameBuffer; 296 NewKey->KeyCell = KeyCell; 297 298 /* Add a reference to the hive */ 299 ++ParentHive->ReferenceCount; 300 301 /* Return the object back to the caller */ 302 *Handle = NewKey; 303 304 Quickie: 305 /* If we had a name buffer, free it */ 306 if (NameBuffer) 307 { 308 BlMmFreeHeap(NameBuffer); 309 } 310 311 /* Return status of the open operation */ 312 return Status; 313 } 314 315 NTSTATUS 316 BiInitializeAndValidateHive ( 317 _In_ PBI_KEY_HIVE Hive 318 ) 319 { 320 ULONG HiveSize; 321 NTSTATUS Status; 322 323 /* Make sure the hive is at least the size of a base block */ 324 if (Hive->HiveSize < sizeof(HBASE_BLOCK)) 325 { 326 return STATUS_REGISTRY_CORRUPT; 327 } 328 329 /* Make sure that the base block accurately describes the size of the hive */ 330 HiveSize = Hive->BaseBlock->Length + sizeof(HBASE_BLOCK); 331 if ((HiveSize < sizeof(HBASE_BLOCK)) || (HiveSize > Hive->HiveSize)) 332 { 333 return STATUS_REGISTRY_CORRUPT; 334 } 335 336 /* Initialize a flat memory hive */ 337 RtlZeroMemory(&Hive->Hive, sizeof(Hive->Hive)); 338 Status = HvInitialize(&Hive->Hive.Hive, 339 HINIT_FLAT, 340 0, 341 0, 342 Hive->BaseBlock, 343 CmpAllocate, 344 CmpFree, 345 NULL, 346 NULL, 347 NULL, 348 NULL, 349 0, 350 NULL); 351 if (NT_SUCCESS(Status)) 352 { 353 /* Cleanup volatile/old data */ 354 CmPrepareHive(&Hive->Hive.Hive); // CmCheckRegistry 355 Status = STATUS_SUCCESS; 356 } 357 358 /* Return the final status */ 359 return Status; 360 } 361 362 NTSTATUS 363 BiLoadHive ( 364 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath, 365 _Out_ PHANDLE HiveHandle 366 ) 367 { 368 ULONG DeviceId; 369 PHBASE_BLOCK BaseBlock, NewBaseBlock; 370 PBI_KEY_OBJECT KeyObject; 371 PBI_KEY_HIVE BcdHive; 372 PBL_DEVICE_DESCRIPTOR BcdDevice; 373 ULONG PathLength, DeviceLength, HiveSize, HiveLength, NewHiveSize; 374 PWCHAR HiveName, LogName; 375 BOOLEAN HaveWriteAccess; 376 NTSTATUS Status; 377 PVOID LogData; 378 PHHIVE Hive; 379 UNICODE_STRING KeyString; 380 PCM_KEY_NODE RootNode; 381 HCELL_INDEX CellIndex; 382 383 /* Initialize variables */ 384 DeviceId = -1; 385 BaseBlock = NULL; 386 BcdHive = NULL; 387 KeyObject = NULL; 388 LogData = NULL; 389 LogName = NULL; 390 391 /* Initialize the crypto seed */ 392 if (!BiHiveHashLibraryInitialized) 393 { 394 HvSymcryptSeed = 0x82EF4D887A4E55C5; 395 BiHiveHashLibraryInitialized = TRUE; 396 } 397 398 /* Extract and validate the input path */ 399 BcdDevice = (PBL_DEVICE_DESCRIPTOR)&FilePath->Path; 400 PathLength = FilePath->Length; 401 DeviceLength = BcdDevice->Size; 402 HiveName = (PWCHAR)((ULONG_PTR)BcdDevice + BcdDevice->Size); 403 if (PathLength <= DeviceLength) 404 { 405 /* Doesn't make sense, bail out */ 406 Status = STATUS_INVALID_PARAMETER; 407 goto Quickie; 408 } 409 410 /* Attempt to open the underlying device for RW access */ 411 HaveWriteAccess = TRUE; 412 Status = BlpDeviceOpen(BcdDevice, 413 BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS, 414 0, 415 &DeviceId); 416 if (!NT_SUCCESS(Status)) 417 { 418 /* Try for RO access instead */ 419 HaveWriteAccess = FALSE; 420 Status = BlpDeviceOpen(BcdDevice, BL_DEVICE_READ_ACCESS, 0, &DeviceId); 421 if (!NT_SUCCESS(Status)) 422 { 423 /* No access at all -- bail out */ 424 goto Quickie; 425 } 426 } 427 428 /* Now try to load the hive on disk */ 429 Status = BlImgLoadImageWithProgress2(DeviceId, 430 BlLoaderRegistry, 431 HiveName, 432 (PVOID*)&BaseBlock, 433 &HiveSize, 434 0, 435 FALSE, 436 NULL, 437 NULL); 438 if (!NT_SUCCESS(Status)) 439 { 440 EfiPrintf(L"Hive read failure: % lx\r\n", Status); 441 goto Quickie; 442 } 443 444 /* Allocate a hive structure */ 445 BcdHive = BlMmAllocateHeap(sizeof(*BcdHive)); 446 if (!BcdHive) 447 { 448 Status = STATUS_NO_MEMORY; 449 goto Quickie; 450 } 451 452 /* Initialize it */ 453 RtlZeroMemory(BcdHive, sizeof(*BcdHive)); 454 BcdHive->BaseBlock = BaseBlock; 455 BcdHive->HiveSize = HiveSize; 456 if (HaveWriteAccess) 457 { 458 BcdHive->Flags |= BI_HIVE_WRITEABLE; 459 } 460 461 /* Make sure the hive was at least one bin long */ 462 if (HiveSize < sizeof(*BaseBlock)) 463 { 464 Status = STATUS_REGISTRY_CORRUPT; 465 goto Quickie; 466 } 467 468 /* Make sure the hive contents are at least one bin long */ 469 HiveLength = BaseBlock->Length; 470 if (BaseBlock->Length < sizeof(*BaseBlock)) 471 { 472 Status = STATUS_REGISTRY_CORRUPT; 473 goto Quickie; 474 } 475 476 /* Validate the initial bin (the base block) */ 477 if (!HvIsInPlaceBaseBlockValid(BaseBlock)) 478 { 479 EfiPrintf(L"Recovery not implemented\r\n"); 480 Status = STATUS_REGISTRY_CORRUPT; 481 goto Quickie; 482 } 483 484 /* Check if there's log recovery that needs to happen */ 485 if (BaseBlock->Sequence1 != BaseBlock->Sequence2) 486 { 487 EfiPrintf(L"Log fix not implemented: %lx %lx\r\n"); 488 Status = STATUS_REGISTRY_CORRUPT; 489 goto Quickie; 490 } 491 492 /* 493 * Check if the whole hive doesn't fit in the buffer. 494 * Note: HiveLength does not include the size of the baseblock itself 495 */ 496 if (HiveSize < (HiveLength + sizeof(*BaseBlock))) 497 { 498 EfiPrintf(L"Need bigger hive buffer path\r\n"); 499 500 /* Allocate a slightly bigger buffer */ 501 NewHiveSize = HiveLength + sizeof(*BaseBlock); 502 Status = MmPapAllocatePagesInRange((PVOID*)&NewBaseBlock, 503 BlLoaderRegistry, 504 NewHiveSize >> PAGE_SHIFT, 505 0, 506 0, 507 NULL, 508 0); 509 if (!NT_SUCCESS(Status)) 510 { 511 goto Quickie; 512 } 513 514 /* Copy the current data in there */ 515 RtlCopyMemory(NewBaseBlock, BaseBlock, HiveSize); 516 517 /* Free the old data */ 518 MmPapFreePages(BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED); 519 520 /* Update our pointers */ 521 BaseBlock = NewBaseBlock; 522 HiveSize = NewHiveSize; 523 BcdHive->BaseBlock = BaseBlock; 524 BcdHive->HiveSize = HiveSize; 525 } 526 527 /* Check if any log stuff needs to happen */ 528 if (LogData) 529 { 530 EfiPrintf(L"Log fix not implemented: %lx %lx\r\n"); 531 Status = STATUS_REGISTRY_CORRUPT; 532 goto Quickie; 533 } 534 535 /* Call Hv to setup the hive library */ 536 Status = BiInitializeAndValidateHive(BcdHive); 537 if (!NT_SUCCESS(Status)) 538 { 539 goto Quickie; 540 } 541 542 /* Now get the root node */ 543 Hive = &BcdHive->Hive.Hive; 544 RootNode = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell); 545 if (!RootNode) 546 { 547 Status = STATUS_OBJECT_NAME_NOT_FOUND; 548 goto Quickie; 549 } 550 551 /* Find the Objects subkey under it to see if it's a real BCD hive */ 552 RtlInitUnicodeString(&KeyString, L"Objects"); 553 CellIndex = CmpFindSubKeyByName(Hive, RootNode, &KeyString); 554 if (CellIndex == HCELL_NIL) 555 { 556 EfiPrintf(L"No OBJECTS subkey found!\r\n"); 557 Status = STATUS_OBJECT_NAME_NOT_FOUND; 558 goto Quickie; 559 } 560 561 /* This is a valid BCD hive, store its root node here */ 562 BcdHive->RootNode = RootNode; 563 564 /* Allocate a copy of the file path */ 565 BcdHive->FilePath = BlMmAllocateHeap(FilePath->Length); 566 if (!BcdHive->FilePath) 567 { 568 Status = STATUS_NO_MEMORY; 569 goto Quickie; 570 } 571 572 /* Make a copy of it */ 573 RtlCopyMemory(BcdHive->FilePath, FilePath, FilePath->Length); 574 575 /* Create a key object to describe the rot */ 576 KeyObject = BlMmAllocateHeap(sizeof(*KeyObject)); 577 if (!KeyObject) 578 { 579 Status = STATUS_NO_MEMORY; 580 goto Quickie; 581 } 582 583 /* Fill out the details */ 584 KeyObject->KeyNode = RootNode; 585 KeyObject->KeyHive = BcdHive; 586 KeyObject->KeyName = NULL; 587 KeyObject->KeyCell = Hive->BaseBlock->RootCell; 588 589 /* One reference for the key object, plus one lifetime reference */ 590 BcdHive->ReferenceCount = 2; 591 592 /* This is the hive handle */ 593 *HiveHandle = KeyObject; 594 595 /* We're all good */ 596 Status = STATUS_SUCCESS; 597 598 Quickie: 599 /* If we had a log name, free it */ 600 if (LogName) 601 { 602 BlMmFreeHeap(LogName); 603 } 604 605 /* If we had logging data, free it */ 606 if (LogData) 607 { 608 MmPapFreePages(LogData, BL_MM_INCLUDE_MAPPED_ALLOCATED); 609 } 610 611 /* Check if this is the failure path */ 612 if (!NT_SUCCESS(Status)) 613 { 614 /* If we mapped the hive, free it */ 615 if (BaseBlock) 616 { 617 MmPapFreePages(BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED); 618 } 619 620 /* If we opened the device, close it */ 621 if (DeviceId != -1) 622 { 623 BlDeviceClose(DeviceId); 624 } 625 626 /* Did we create a hive object? */ 627 if (BcdHive) 628 { 629 /* Free the file path if we made a copy of it */ 630 if (BcdHive->FilePath) 631 { 632 BlMmFreeHeap(BcdHive->FilePath); 633 } 634 635 /* Free the hive itself */ 636 BlMmFreeHeap(BcdHive); 637 } 638 639 /* Finally, free the root key object if we created one */ 640 if (KeyObject) 641 { 642 BlMmFreeHeap(KeyObject); 643 } 644 } 645 646 /* Return the final status */ 647 return Status; 648 } 649 650 NTSTATUS 651 BiGetRegistryValue ( 652 _In_ HANDLE KeyHandle, 653 _In_ PWCHAR ValueName, 654 _In_ ULONG Type, 655 _Out_ PVOID* Buffer, 656 _Out_ PULONG ValueLength 657 ) 658 { 659 PCM_KEY_NODE KeyNode; 660 PHHIVE KeyHive; 661 UNICODE_STRING ValueString; 662 PBI_KEY_OBJECT KeyObject; 663 PCM_KEY_VALUE KeyValue; 664 PVOID ValueCopy; 665 ULONG Size; 666 HCELL_INDEX CellIndex; 667 PCELL_DATA ValueData; 668 669 /* Get the key object, node,and hive */ 670 KeyObject = (PBI_KEY_OBJECT)KeyHandle; 671 KeyNode = KeyObject->KeyNode; 672 KeyHive = &KeyObject->KeyHive->Hive.Hive; 673 674 /* Find the value cell index in the list of values */ 675 RtlInitUnicodeString(&ValueString, ValueName); 676 CmpFindNameInList(KeyHive, 677 &KeyNode->ValueList, 678 &ValueString, 679 NULL, 680 &CellIndex); 681 if (CellIndex == HCELL_NIL) 682 { 683 return STATUS_OBJECT_NAME_NOT_FOUND; 684 } 685 686 /* Get the cell data for it */ 687 KeyValue = (PCM_KEY_VALUE)HvGetCell(KeyHive, CellIndex); 688 if (!KeyValue) 689 { 690 return STATUS_REGISTRY_CORRUPT; 691 } 692 693 /* Make sure the type matches */ 694 if (KeyValue->Type != Type) 695 { 696 return STATUS_OBJECT_TYPE_MISMATCH; 697 } 698 699 /* Now get the data cell */ 700 ValueData = CmpValueToData(KeyHive, KeyValue, &Size); 701 702 /* Make a copy of it */ 703 ValueCopy = BlMmAllocateHeap(Size); 704 if (!ValueCopy) 705 { 706 return STATUS_NO_MEMORY; 707 } 708 709 /* Copy it in the buffer, and return it and its size */ 710 RtlCopyMemory(ValueCopy, ValueData, Size); 711 *Buffer = ValueCopy; 712 *ValueLength = Size; 713 return STATUS_SUCCESS; 714 } 715 716 NTSTATUS 717 BiEnumerateSubKeys ( 718 _In_ HANDLE KeyHandle, 719 _Out_ PWCHAR** SubKeyList, 720 _Out_ PULONG SubKeyCount 721 ) 722 { 723 PCM_KEY_NODE KeyNode, Node; 724 PBI_KEY_OBJECT KeyObject; 725 ULONG KeyCount; 726 ULONG NameLength, NewTotalNameLength, FinalLength, TotalNameLength; 727 PHHIVE Hive; 728 PWCHAR KeyName, NameEnd; 729 HCELL_INDEX CellIndex; 730 PWCHAR* SubKeys; 731 NTSTATUS Status; 732 ULONG i; 733 734 /* Get the key object, node, and hive */ 735 KeyObject = (PBI_KEY_OBJECT)KeyHandle; 736 KeyNode = KeyObject->KeyNode; 737 Hive = &KeyObject->KeyHive->Hive.Hive; 738 739 /* Assume it's empty */ 740 *SubKeyList = 0; 741 *SubKeyCount = 0; 742 743 /* Initialize locals */ 744 KeyCount = 0; 745 SubKeys = 0; 746 TotalNameLength = 0; 747 748 /* Find the first subkey cell index */ 749 CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount); 750 while (CellIndex != HCELL_NIL) 751 { 752 /* Move to the next one */ 753 KeyCount++; 754 755 /* Get the cell data for it */ 756 Node = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 757 if (!Node) 758 { 759 return STATUS_REGISTRY_CORRUPT; 760 } 761 762 /* Check if the value is compressed */ 763 if (Node->Flags & KEY_COMP_NAME) 764 { 765 /* Get the compressed name size */ 766 NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength); 767 } 768 else 769 { 770 /* Get the real size */ 771 NameLength = Node->NameLength; 772 } 773 774 /* Add up the new length, protecting against overflow */ 775 NewTotalNameLength = TotalNameLength + NameLength + sizeof(UNICODE_NULL); 776 if (NewTotalNameLength < TotalNameLength) 777 { 778 Status = STATUS_NAME_TOO_LONG; 779 goto Quickie; 780 } 781 782 /* We're good, use the new length */ 783 TotalNameLength = NewTotalNameLength; 784 785 /* Find the next subkey cell index */ 786 CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount); 787 } 788 789 /* Were there no keys? We're done, if so */ 790 if (!KeyCount) 791 { 792 return STATUS_SUCCESS; 793 } 794 795 /* Safely compute the size of the array needed */ 796 Status = RtlULongLongToULong(sizeof(PWCHAR) * KeyCount, &FinalLength); 797 if (!NT_SUCCESS(Status)) 798 { 799 goto Quickie; 800 } 801 802 /* Safely add that to the name length */ 803 Status = RtlULongAdd(TotalNameLength, FinalLength, &FinalLength); 804 if (!NT_SUCCESS(Status)) 805 { 806 goto Quickie; 807 } 808 809 /* Allocate an array big enough for the names and pointers */ 810 SubKeys = BlMmAllocateHeap(FinalLength); 811 if (!SubKeys) 812 { 813 Status = STATUS_NO_MEMORY; 814 goto Quickie; 815 } 816 817 /* Go over each key again */ 818 NameEnd = (PWCHAR)&SubKeys[KeyCount]; 819 for (i = 0; i < KeyCount; i++) 820 { 821 /* Get the cell index for this subkey */ 822 CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, i); 823 if (CellIndex == HCELL_NIL) 824 { 825 break; 826 } 827 828 /* Get the cell data for it */ 829 Node = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 830 if (!Node) 831 { 832 Status = STATUS_REGISTRY_CORRUPT; 833 goto Quickie; 834 } 835 836 /* Check if the value is compressed */ 837 KeyName = Node->Name; 838 if (Node->Flags & KEY_COMP_NAME) 839 { 840 /* Get the compressed name size */ 841 NameLength = CmpCompressedNameSize(KeyName, Node->NameLength); 842 CmpCopyCompressedName(NameEnd, NameLength, KeyName, Node->NameLength); 843 } 844 else 845 { 846 /* Get the real size */ 847 NameLength = Node->NameLength; 848 RtlCopyMemory(NameEnd, KeyName, NameLength); 849 } 850 851 /* Move the name buffer to the next spot, and NULL-terminate */ 852 SubKeys[i] = NameEnd; 853 NameEnd += (NameLength / sizeof(WCHAR)); 854 *NameEnd = UNICODE_NULL; 855 856 /* Keep going */ 857 NameEnd++; 858 } 859 860 /* Check if the subkeys were empty */ 861 if (i == 0) 862 { 863 /* They disappeared in the middle of enumeration */ 864 Status = STATUS_OBJECT_NAME_NOT_FOUND; 865 goto Quickie; 866 } 867 868 /* Return the count and the array of names */ 869 *SubKeyList = SubKeys; 870 *SubKeyCount = i; 871 SubKeys = NULL; 872 Status = STATUS_SUCCESS; 873 874 Quickie: 875 /* On the failure path, free the subkeys if any exist */ 876 if (SubKeys) 877 { 878 BlMmFreeHeap(SubKeys); 879 } 880 881 /* All done, return the result */ 882 return Status; 883 } 884 885 NTSTATUS 886 BiDeleteKey ( 887 _In_ HANDLE KeyHandle 888 ) 889 { 890 NTSTATUS Status; 891 PBI_KEY_OBJECT KeyObject; 892 PHHIVE Hive; 893 ULONG SubKeyCount, i; 894 PWCHAR* SubKeyList; 895 HANDLE SubKeyHandle; 896 897 /* Get the key object and hive */ 898 KeyObject = (PBI_KEY_OBJECT)KeyHandle; 899 Hive = &KeyObject->KeyHive->Hive.Hive; 900 901 /* Make sure the hive is writeable */ 902 if (!(KeyObject->KeyHive->Flags & BI_HIVE_WRITEABLE)) 903 { 904 return STATUS_MEDIA_WRITE_PROTECTED; 905 } 906 907 /* Enumerate all of the subkeys */ 908 Status = BiEnumerateSubKeys(KeyHandle, &SubKeyList, &SubKeyCount); 909 if ((NT_SUCCESS(Status)) && (SubKeyCount > 0)) 910 { 911 /* Loop through each one */ 912 for (i = 0; i < SubKeyCount; i++) 913 { 914 /* Open a handle to it */ 915 Status = BiOpenKey(KeyHandle, SubKeyList[i], &SubKeyHandle); 916 if (NT_SUCCESS(Status)) 917 { 918 /* Recursively call us to delete it */ 919 Status = BiDeleteKey(SubKeyHandle); 920 if (Status != STATUS_SUCCESS) 921 { 922 /* Close the key on failure */ 923 BiCloseKey(SubKeyHandle); 924 } 925 } 926 } 927 } 928 929 /* Check if we had a list of subkeys */ 930 if (SubKeyList) 931 { 932 /* Free it */ 933 BlMmFreeHeap(SubKeyList); 934 } 935 936 /* Delete this key cell */ 937 Status = CmpFreeKeyByCell(Hive, KeyObject->KeyCell, TRUE); 938 if (NT_SUCCESS(Status)) 939 { 940 /* Mark the hive as requiring a flush */ 941 KeyObject->KeyHive->Flags |= BI_FLUSH_HIVE; 942 BiCloseKey(KeyHandle); 943 } 944 945 /* All done */ 946 return Status; 947 }