1 /* 2 * FreeLoader 3 * 4 * Copyright (C) 2014 Timo Kreuzer <timo.kreuzer@reactos.org> 5 * 2022 George Bișoc <george.bisoc@reactos.org> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 */ 21 22 #include <freeldr.h> 23 #include <cmlib.h> 24 #include "registry.h" 25 #include <internal/cmboot.h> 26 27 #include <debug.h> 28 DBG_DEFAULT_CHANNEL(REGISTRY); 29 30 static PCMHIVE CmSystemHive; 31 static HCELL_INDEX SystemRootCell; 32 33 PHHIVE SystemHive = NULL; 34 HKEY CurrentControlSetKey = NULL; 35 36 #define HCI_TO_HKEY(CellIndex) ((HKEY)(ULONG_PTR)(CellIndex)) 37 #ifndef HKEY_TO_HCI // See also registry.h 38 #define HKEY_TO_HCI(hKey) ((HCELL_INDEX)(ULONG_PTR)(hKey)) 39 #endif 40 41 #define GET_HHIVE(CmHive) (&((CmHive)->Hive)) 42 #define GET_HHIVE_FROM_HKEY(hKey) GET_HHIVE(CmSystemHive) 43 #define GET_CM_KEY_NODE(hHive, hKey) ((PCM_KEY_NODE)HvGetCell(hHive, HKEY_TO_HCI(hKey))) 44 45 #define GET_HBASE_BLOCK(ChunkBase) ((PHBASE_BLOCK)ChunkBase) 46 47 PVOID 48 NTAPI 49 CmpAllocate( 50 IN SIZE_T Size, 51 IN BOOLEAN Paged, 52 IN ULONG Tag) 53 { 54 UNREFERENCED_PARAMETER(Paged); 55 return FrLdrHeapAlloc(Size, Tag); 56 } 57 58 VOID 59 NTAPI 60 CmpFree( 61 IN PVOID Ptr, 62 IN ULONG Quota) 63 { 64 UNREFERENCED_PARAMETER(Quota); 65 FrLdrHeapFree(Ptr, 0); 66 } 67 68 /** 69 * @brief 70 * Initializes a flat hive descriptor for the 71 * hive and validates the registry hive. 72 * Volatile data is purged during this procedure 73 * for initialization. 74 * 75 * @param[in] CmHive 76 * A pointer to a CM (in-memory) hive descriptor 77 * containing the hive descriptor to be initialized. 78 * 79 * @param[in] ChunkBase 80 * An arbitrary pointer that points to the registry 81 * chunk base. This pointer serves as the base block 82 * containing the hive file header data. 83 * 84 * @param[in] LoadAlternate 85 * If set to TRUE, the function will initialize the 86 * hive as an alternate hive, otherwise FALSE to initialize 87 * it as primary. 88 * 89 * @return 90 * Returns TRUE if the hive has been initialized 91 * and registry data inside the hive is valid, FALSE 92 * otherwise. 93 */ 94 static 95 BOOLEAN 96 RegInitializeHive( 97 _In_ PCMHIVE CmHive, 98 _In_ PVOID ChunkBase, 99 _In_ BOOLEAN LoadAlternate) 100 { 101 NTSTATUS Status; 102 /* 103 * FIXME: Disable compilation of some parts of code for AMD64 for now, 104 * since it makes the FreeLdr binary size so large that it prevents 105 * x64 ROS from booting. 106 */ 107 #if !defined(_M_AMD64) 108 CM_CHECK_REGISTRY_STATUS CmStatusCode; 109 #endif 110 111 /* Initialize the hive */ 112 Status = HvInitialize(GET_HHIVE(CmHive), 113 HINIT_FLAT, // HINIT_MEMORY_INPLACE 114 0, 115 LoadAlternate ? HFILE_TYPE_ALTERNATE : HFILE_TYPE_PRIMARY, 116 ChunkBase, 117 CmpAllocate, 118 CmpFree, 119 NULL, 120 NULL, 121 NULL, 122 NULL, 123 1, 124 NULL); 125 if (!NT_SUCCESS(Status)) 126 { 127 ERR("Failed to initialize the flat hive (Status 0x%lx)\n", Status); 128 return FALSE; 129 } 130 131 /* FIXME: See the comment above */ 132 #if !defined(_M_AMD64) 133 /* Now check the hive and purge volatile data */ 134 CmStatusCode = CmCheckRegistry(CmHive, CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 135 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 136 { 137 ERR("CmCheckRegistry detected problems with the loaded flat hive (check code %lu)\n", CmStatusCode); 138 return FALSE; 139 } 140 #endif 141 142 return TRUE; 143 } 144 145 /* FIXME: See the comment above */ 146 #if !defined(_M_AMD64) 147 /** 148 * @brief 149 * Loads and reads a hive log at specified 150 * file offset. 151 * 152 * @param[in] DirectoryPath 153 * A pointer to a string that denotes the directory 154 * path of the hives and logs location. 155 * 156 * @param[in] LogFileOffset 157 * The file offset of which this function uses to 158 * seek at specific position during read. 159 * 160 * @param[in] LogName 161 * A pointer to a string that denotes the name of 162 * the desired hive log (e.g. "SYSTEM"). 163 * 164 * @param[out] LogData 165 * A pointer to the returned hive log data that was 166 * read. The following data varies depending on the 167 * specified offset set up by the caller, that is used 168 * to where to start reading from the hive log. 169 * 170 * @return 171 * Returns TRUE if the hive log was loaded and read 172 * successfully, FALSE otherwise. 173 * 174 * @remarks 175 * The returned log data pointer to the caller is a 176 * virtual address. You must use VaToPa that converts 177 * the address to a physical one in order to actually 178 * use it! 179 */ 180 static 181 BOOLEAN 182 RegLoadHiveLog( 183 _In_ PCSTR DirectoryPath, 184 _In_ ULONG LogFileOffset, 185 _In_ PCSTR LogName, 186 _Out_ PVOID *LogData) 187 { 188 ARC_STATUS Status; 189 ULONG LogId; 190 CHAR LogPath[MAX_PATH]; 191 ULONG LogFileSize; 192 FILEINFORMATION FileInfo; 193 LARGE_INTEGER Position; 194 ULONG BytesRead; 195 PVOID LogDataVirtual; 196 PVOID LogDataPhysical; 197 198 /* Build the full path to the hive log */ 199 RtlStringCbCopyA(LogPath, sizeof(LogPath), DirectoryPath); 200 RtlStringCbCatA(LogPath, sizeof(LogPath), LogName); 201 202 /* Open the file */ 203 Status = ArcOpen(LogPath, OpenReadOnly, &LogId); 204 if (Status != ESUCCESS) 205 { 206 ERR("Failed to open %s (ARC code %lu)\n", LogName, Status); 207 return FALSE; 208 } 209 210 /* Get the file length */ 211 Status = ArcGetFileInformation(LogId, &FileInfo); 212 if (Status != ESUCCESS) 213 { 214 ERR("Failed to get file information from %s (ARC code %lu)\n", LogName, Status); 215 ArcClose(LogId); 216 return FALSE; 217 } 218 219 /* Capture the size of the hive log file */ 220 LogFileSize = FileInfo.EndingAddress.LowPart; 221 if (LogFileSize == 0) 222 { 223 ERR("LogFileSize is 0, %s is corrupt\n", LogName); 224 ArcClose(LogId); 225 return FALSE; 226 } 227 228 /* Allocate memory blocks for our log data */ 229 LogDataPhysical = MmAllocateMemoryWithType( 230 MM_SIZE_TO_PAGES(LogFileSize + MM_PAGE_SIZE - 1) << MM_PAGE_SHIFT, 231 LoaderRegistryData); 232 if (LogDataPhysical == NULL) 233 { 234 ERR("Failed to allocate memory for log data\n"); 235 ArcClose(LogId); 236 return FALSE; 237 } 238 239 /* Convert the address to virtual so that it can be useable */ 240 LogDataVirtual = PaToVa(LogDataPhysical); 241 242 /* Seek within the log file at desired position */ 243 Position.QuadPart = LogFileOffset; 244 Status = ArcSeek(LogId, &Position, SeekAbsolute); 245 if (Status != ESUCCESS) 246 { 247 ERR("Failed to seek at %s (ARC code %lu)\n", LogName, Status); 248 ArcClose(LogId); 249 return FALSE; 250 } 251 252 /* And read the actual data from the log */ 253 Status = ArcRead(LogId, LogDataPhysical, LogFileSize, &BytesRead); 254 if (Status != ESUCCESS) 255 { 256 ERR("Failed to read %s (ARC code %lu)\n", LogName, Status); 257 ArcClose(LogId); 258 return FALSE; 259 } 260 261 *LogData = LogDataVirtual; 262 ArcClose(LogId); 263 return TRUE; 264 } 265 266 /** 267 * @brief 268 * Recovers the header base block of a flat 269 * registry hive. 270 * 271 * @param[in] ChunkBase 272 * A pointer to the registry hive chunk base of 273 * which the damaged header block is to be recovered. 274 * 275 * @param[in] DirectoryPath 276 * A pointer to a string that denotes the directory 277 * path of the hives and logs location. 278 * 279 * @param[in] LogName 280 * A pointer to a string that denotes the name of 281 * the desired hive log (e.g. "SYSTEM"). 282 * 283 * @return 284 * Returns TRUE if the header base block was successfully 285 * recovered, FALSE otherwise. 286 */ 287 static 288 BOOLEAN 289 RegRecoverHeaderHive( 290 _Inout_ PVOID ChunkBase, 291 _In_ PCSTR DirectoryPath, 292 _In_ PCSTR LogName) 293 { 294 BOOLEAN Success; 295 CHAR FullLogFileName[MAX_PATH]; 296 PVOID LogData; 297 PHBASE_BLOCK HiveBaseBlock; 298 PHBASE_BLOCK LogBaseBlock; 299 300 /* Build the complete path of the hive log */ 301 RtlStringCbCopyA(FullLogFileName, sizeof(FullLogFileName), LogName); 302 RtlStringCbCatA(FullLogFileName, sizeof(FullLogFileName), ".LOG"); 303 Success = RegLoadHiveLog(DirectoryPath, 0, FullLogFileName, &LogData); 304 if (!Success) 305 { 306 ERR("Failed to read the hive log\n"); 307 return FALSE; 308 } 309 310 /* Make sure the header from the hive log is actually sane */ 311 LogData = VaToPa(LogData); 312 LogBaseBlock = GET_HBASE_BLOCK(LogData); 313 if (!HvpVerifyHiveHeader(LogBaseBlock, HFILE_TYPE_LOG)) 314 { 315 ERR("The hive log has corrupt base block\n"); 316 return FALSE; 317 } 318 319 /* Copy the healthy header base block into the primary hive */ 320 HiveBaseBlock = GET_HBASE_BLOCK(ChunkBase); 321 WARN("Recovering the hive base block...\n"); 322 RtlCopyMemory(HiveBaseBlock, 323 LogBaseBlock, 324 LogBaseBlock->Cluster * HSECTOR_SIZE); 325 HiveBaseBlock->Type = HFILE_TYPE_PRIMARY; 326 return TRUE; 327 } 328 329 /** 330 * @brief 331 * Recovers the corrupt data of a primary flat 332 * registry hive. 333 * 334 * @param[in] ChunkBase 335 * A pointer to the registry hive chunk base of 336 * which the damaged hive data is to be replaced 337 * with healthy data from the corresponding hive log. 338 * 339 * @param[in] DirectoryPath 340 * A pointer to a string that denotes the directory 341 * path of the hives and logs location. 342 * 343 * @param[in] LogName 344 * A pointer to a string that denotes the name of 345 * the desired hive log (e.g. "SYSTEM"). 346 * 347 * @return 348 * Returns TRUE if the hive data was successfully 349 * recovered, FALSE otherwise. 350 * 351 * @remarks 352 * Data recovery of the target hive does not always 353 * guarantee the primary hive is fully recovered. 354 * It could happen a block from a hive log is not 355 * marked dirty (pending to be written to disk) that 356 * has healthy data therefore the following bad block 357 * would still remain in corrupt state in the main primary 358 * hive. In such scenarios an alternate hive must be replayed. 359 */ 360 static 361 BOOLEAN 362 RegRecoverDataHive( 363 _Inout_ PVOID ChunkBase, 364 _In_ PCSTR DirectoryPath, 365 _In_ PCSTR LogName) 366 { 367 BOOLEAN Success; 368 ULONG StorageLength; 369 ULONG BlockIndex, LogIndex; 370 PUCHAR BlockPtr, BlockDest; 371 CHAR FullLogFileName[MAX_PATH]; 372 PVOID LogData; 373 PUCHAR LogDataPhysical; 374 PHBASE_BLOCK HiveBaseBlock; 375 376 /* Build the complete path of the hive log */ 377 RtlStringCbCopyA(FullLogFileName, sizeof(FullLogFileName), LogName); 378 RtlStringCbCatA(FullLogFileName, sizeof(FullLogFileName), ".LOG"); 379 Success = RegLoadHiveLog(DirectoryPath, HV_LOG_HEADER_SIZE, FullLogFileName, &LogData); 380 if (!Success) 381 { 382 ERR("Failed to read the hive log\n"); 383 return FALSE; 384 } 385 386 /* Make sure the dirty vector signature is there otherwise the hive log is corrupt */ 387 LogDataPhysical = (PUCHAR)VaToPa(LogData); 388 if (*((PULONG)LogDataPhysical) != HV_LOG_DIRTY_SIGNATURE) 389 { 390 ERR("The hive log dirty signature could not be found\n"); 391 return FALSE; 392 } 393 394 /* Copy the dirty data into the primary hive */ 395 LogIndex = 0; 396 BlockIndex = 0; 397 HiveBaseBlock = GET_HBASE_BLOCK(ChunkBase); 398 StorageLength = HiveBaseBlock->Length / HBLOCK_SIZE; 399 for (; BlockIndex < StorageLength; ++BlockIndex) 400 { 401 /* Skip this block if it's not dirty and go to the next one */ 402 if (LogDataPhysical[BlockIndex + sizeof(HV_LOG_DIRTY_SIGNATURE)] != HV_LOG_DIRTY_BLOCK) 403 { 404 continue; 405 } 406 407 /* Read the dirty block and copy it at right offsets */ 408 BlockPtr = (PUCHAR)((ULONG_PTR)LogDataPhysical + 2 * HSECTOR_SIZE + LogIndex * HBLOCK_SIZE); 409 BlockDest = (PUCHAR)((ULONG_PTR)ChunkBase + (BlockIndex + 1) * HBLOCK_SIZE); 410 RtlCopyMemory(BlockDest, BlockPtr, HBLOCK_SIZE); 411 412 /* Increment the index in log as we continue further */ 413 LogIndex++; 414 } 415 416 /* Fix the secondary sequence of the primary hive and compute a new checksum */ 417 HiveBaseBlock->Sequence2 = HiveBaseBlock->Sequence1; 418 HiveBaseBlock->CheckSum = HvpHiveHeaderChecksum(HiveBaseBlock); 419 return TRUE; 420 } 421 #endif 422 423 /** 424 * @brief 425 * Imports the SYSTEM binary hive from 426 * the registry base chunk that's been 427 * provided by the loader block. 428 * 429 * @param[in] ChunkBase 430 * A pointer to the registry base chunk 431 * that serves for SYSTEM hive initialization. 432 * 433 * @param[in] ChunkSize 434 * The size of the registry base chunk. This 435 * parameter refers to the actual size of 436 * the SYSTEM hive. This parameter is currently 437 * unused. 438 * 439 * @param[in] LoadAlternate 440 * If set to TRUE, the function will initialize the 441 * hive as an alternate hive, otherwise FALSE to initialize 442 * it as primary. 443 * 444 * @return 445 * Returns TRUE if hive importing and initialization 446 * have succeeded, FALSE otherwise. 447 */ 448 BOOLEAN 449 RegImportBinaryHive( 450 _In_ PVOID ChunkBase, 451 _In_ ULONG ChunkSize, 452 _In_ PCSTR SearchPath, 453 _In_ BOOLEAN LoadAlternate) 454 { 455 BOOLEAN Success; 456 PCM_KEY_NODE KeyNode; 457 458 TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize); 459 460 /* Assume that we don't need boot recover, unless we have to */ 461 ((PHBASE_BLOCK)ChunkBase)->BootRecover = HBOOT_NO_BOOT_RECOVER; 462 463 /* Allocate and initialize the hive */ 464 CmSystemHive = FrLdrTempAlloc(sizeof(CMHIVE), 'eviH'); 465 Success = RegInitializeHive(CmSystemHive, ChunkBase, LoadAlternate); 466 if (!Success) 467 /* FIXME: See the comment above */ 468 #if defined(_M_AMD64) 469 { 470 ERR("Corrupted hive %p!\n", ChunkBase); 471 FrLdrTempFree(CmSystemHive, 'eviH'); 472 return FALSE; 473 } 474 #else 475 { 476 /* Free the buffer and retry again */ 477 FrLdrTempFree(CmSystemHive, 'eviH'); 478 CmSystemHive = NULL; 479 480 if (!RegRecoverHeaderHive(ChunkBase, SearchPath, "SYSTEM")) 481 { 482 ERR("Failed to recover the hive header block\n"); 483 return FALSE; 484 } 485 486 if (!RegRecoverDataHive(ChunkBase, SearchPath, "SYSTEM")) 487 { 488 ERR("Failed to recover the hive data\n"); 489 return FALSE; 490 } 491 492 /* Now retry initializing the hive again */ 493 CmSystemHive = FrLdrTempAlloc(sizeof(CMHIVE), 'eviH'); 494 Success = RegInitializeHive(CmSystemHive, ChunkBase, LoadAlternate); 495 if (!Success) 496 { 497 ERR("Corrupted hive (despite recovery) %p\n", ChunkBase); 498 FrLdrTempFree(CmSystemHive, 'eviH'); 499 return FALSE; 500 } 501 502 /* 503 * Acknowledge the kernel we recovered the SYSTEM hive 504 * on our side by applying log data. 505 */ 506 ((PHBASE_BLOCK)ChunkBase)->BootRecover = HBOOT_BOOT_RECOVERED_BY_HIVE_LOG; 507 } 508 #endif 509 510 /* Save the root key node */ 511 SystemHive = GET_HHIVE(CmSystemHive); 512 SystemRootCell = SystemHive->BaseBlock->RootCell; 513 ASSERT(SystemRootCell != HCELL_NIL); 514 515 /* Verify it is accessible */ 516 KeyNode = (PCM_KEY_NODE)HvGetCell(SystemHive, SystemRootCell); 517 ASSERT(KeyNode); 518 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 519 HvReleaseCell(SystemHive, SystemRootCell); 520 521 return TRUE; 522 } 523 524 BOOLEAN 525 RegInitCurrentControlSet( 526 _In_ BOOLEAN LastKnownGood) 527 { 528 UNICODE_STRING ControlSetName; 529 HCELL_INDEX ControlCell; 530 PCM_KEY_NODE KeyNode; 531 BOOLEAN AutoSelect; 532 533 TRACE("RegInitCurrentControlSet\n"); 534 535 /* Choose which control set to open and set it as the new "Current" */ 536 RtlInitUnicodeString(&ControlSetName, 537 LastKnownGood ? L"LastKnownGood" 538 : L"Default"); 539 540 ControlCell = CmpFindControlSet(SystemHive, 541 SystemRootCell, 542 &ControlSetName, 543 &AutoSelect); 544 if (ControlCell == HCELL_NIL) 545 { 546 ERR("CmpFindControlSet('%wZ') failed\n", &ControlSetName); 547 return FALSE; 548 } 549 550 CurrentControlSetKey = HCI_TO_HKEY(ControlCell); 551 552 /* Verify it is accessible */ 553 KeyNode = (PCM_KEY_NODE)HvGetCell(SystemHive, ControlCell); 554 ASSERT(KeyNode); 555 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 556 HvReleaseCell(SystemHive, ControlCell); 557 558 return TRUE; 559 } 560 561 static 562 BOOLEAN 563 GetNextPathElement( 564 _Out_ PUNICODE_STRING NextElement, 565 _Inout_ PUNICODE_STRING RemainingPath) 566 { 567 /* Check if there are any characters left */ 568 if (RemainingPath->Length < sizeof(WCHAR)) 569 { 570 /* Nothing left, bail out early */ 571 return FALSE; 572 } 573 574 /* The next path elements starts with the remaining path */ 575 NextElement->Buffer = RemainingPath->Buffer; 576 577 /* Loop until the path element ends */ 578 while ((RemainingPath->Length >= sizeof(WCHAR)) && 579 (RemainingPath->Buffer[0] != '\\')) 580 { 581 /* Skip this character */ 582 RemainingPath->Buffer++; 583 RemainingPath->Length -= sizeof(WCHAR); 584 } 585 586 NextElement->Length = (USHORT)(RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR); 587 NextElement->MaximumLength = NextElement->Length; 588 589 /* Check if the path element ended with a path separator */ 590 if (RemainingPath->Length >= sizeof(WCHAR)) 591 { 592 /* Skip the path separator */ 593 ASSERT(RemainingPath->Buffer[0] == '\\'); 594 RemainingPath->Buffer++; 595 RemainingPath->Length -= sizeof(WCHAR); 596 } 597 598 /* Return whether we got any characters */ 599 return TRUE; 600 } 601 602 #if 0 603 LONG 604 RegEnumKey( 605 _In_ HKEY Key, 606 _In_ ULONG Index, 607 _Out_ PWCHAR Name, 608 _Inout_ PULONG NameSize, 609 _Out_opt_ PHKEY SubKey) 610 { 611 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key); 612 PCM_KEY_NODE KeyNode, SubKeyNode; 613 HCELL_INDEX CellIndex; 614 USHORT NameLength; 615 616 TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n", 617 Key, Index, Name, NameSize, NameSize ? *NameSize : 0); 618 619 /* Get the key node */ 620 KeyNode = GET_CM_KEY_NODE(Hive, Key); 621 ASSERT(KeyNode); 622 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 623 624 CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, Index); 625 if (CellIndex == HCELL_NIL) 626 { 627 TRACE("RegEnumKey index out of bounds (%d) in key (%.*s)\n", 628 Index, KeyNode->NameLength, KeyNode->Name); 629 HvReleaseCell(Hive, HKEY_TO_HCI(Key)); 630 return ERROR_NO_MORE_ITEMS; 631 } 632 HvReleaseCell(Hive, HKEY_TO_HCI(Key)); 633 634 /* Get the value cell */ 635 SubKeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 636 ASSERT(SubKeyNode != NULL); 637 ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE); 638 639 if (SubKeyNode->Flags & KEY_COMP_NAME) 640 { 641 NameLength = CmpCompressedNameSize(SubKeyNode->Name, SubKeyNode->NameLength); 642 643 /* Compressed name */ 644 CmpCopyCompressedName(Name, 645 *NameSize, 646 SubKeyNode->Name, 647 SubKeyNode->NameLength); 648 } 649 else 650 { 651 NameLength = SubKeyNode->NameLength; 652 653 /* Normal name */ 654 RtlCopyMemory(Name, SubKeyNode->Name, 655 min(*NameSize, SubKeyNode->NameLength)); 656 } 657 658 if (*NameSize >= NameLength + sizeof(WCHAR)) 659 { 660 Name[NameLength / sizeof(WCHAR)] = UNICODE_NULL; 661 } 662 663 *NameSize = NameLength + sizeof(WCHAR); 664 665 HvReleaseCell(Hive, CellIndex); 666 667 if (SubKey != NULL) 668 *SubKey = HCI_TO_HKEY(CellIndex); 669 670 TRACE("RegEnumKey done -> %u, '%.*S'\n", *NameSize, *NameSize, Name); 671 return ERROR_SUCCESS; 672 } 673 #endif 674 675 LONG 676 RegOpenKey( 677 _In_ HKEY ParentKey, 678 _In_z_ PCWSTR KeyName, 679 _Out_ PHKEY Key) 680 { 681 UNICODE_STRING RemainingPath, SubKeyName; 682 UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet"); 683 PHHIVE Hive = (ParentKey ? GET_HHIVE_FROM_HKEY(ParentKey) : GET_HHIVE(CmSystemHive)); 684 PCM_KEY_NODE KeyNode; 685 HCELL_INDEX CellIndex; 686 687 TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key); 688 689 /* Initialize the remaining path name */ 690 RtlInitUnicodeString(&RemainingPath, KeyName); 691 692 /* Check if we have a parent key */ 693 if (ParentKey == NULL) 694 { 695 UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3; 696 UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry"); 697 UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE"); 698 UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM"); 699 700 TRACE("RegOpenKey: absolute path\n"); 701 702 if ((RemainingPath.Length < sizeof(WCHAR)) || 703 RemainingPath.Buffer[0] != '\\') 704 { 705 /* The key path is not absolute */ 706 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath); 707 return ERROR_PATH_NOT_FOUND; 708 } 709 710 /* Skip initial path separator */ 711 RemainingPath.Buffer++; 712 RemainingPath.Length -= sizeof(WCHAR); 713 714 /* Get the first 3 path elements */ 715 GetNextPathElement(&SubKeyName1, &RemainingPath); 716 GetNextPathElement(&SubKeyName2, &RemainingPath); 717 GetNextPathElement(&SubKeyName3, &RemainingPath); 718 TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3); 719 720 /* Check if we have the correct path */ 721 if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) || 722 !RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) || 723 !RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE)) 724 { 725 /* The key path is not inside HKLM\Machine\System */ 726 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath); 727 return ERROR_PATH_NOT_FOUND; 728 } 729 730 /* Use the root key */ 731 CellIndex = SystemRootCell; 732 } 733 else 734 { 735 /* Use the parent key */ 736 CellIndex = HKEY_TO_HCI(ParentKey); 737 } 738 739 /* Check if this is the root key */ 740 if (CellIndex == SystemRootCell) 741 { 742 UNICODE_STRING TempPath = RemainingPath; 743 744 /* Get the first path element */ 745 GetNextPathElement(&SubKeyName, &TempPath); 746 747 /* Check if this is CurrentControlSet */ 748 if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE)) 749 { 750 /* Use the CurrentControlSetKey and update the remaining path */ 751 CellIndex = HKEY_TO_HCI(CurrentControlSetKey); 752 RemainingPath = TempPath; 753 } 754 } 755 756 /* Get the key node */ 757 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 758 ASSERT(KeyNode); 759 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 760 761 TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath); 762 763 /* Loop while there are path elements */ 764 while (GetNextPathElement(&SubKeyName, &RemainingPath)) 765 { 766 HCELL_INDEX NextCellIndex; 767 768 TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName); 769 770 /* Get the next sub key */ 771 NextCellIndex = CmpFindSubKeyByName(Hive, KeyNode, &SubKeyName); 772 HvReleaseCell(Hive, CellIndex); 773 CellIndex = NextCellIndex; 774 if (CellIndex == HCELL_NIL) 775 { 776 WARN("Did not find sub key '%wZ' (full: %S)\n", &SubKeyName, KeyName); 777 return ERROR_PATH_NOT_FOUND; 778 } 779 780 /* Get the found key */ 781 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 782 ASSERT(KeyNode); 783 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 784 } 785 786 HvReleaseCell(Hive, CellIndex); 787 *Key = HCI_TO_HKEY(CellIndex); 788 789 return ERROR_SUCCESS; 790 } 791 792 static 793 VOID 794 RepGetValueData( 795 _In_ PHHIVE Hive, 796 _In_ PCM_KEY_VALUE ValueCell, 797 _Out_opt_ PULONG Type, 798 _Out_opt_ PUCHAR Data, 799 _Inout_opt_ PULONG DataSize) 800 { 801 ULONG DataLength; 802 PVOID DataCell; 803 804 /* Does the caller want the type? */ 805 if (Type != NULL) 806 *Type = ValueCell->Type; 807 808 /* Does the caller provide DataSize? */ 809 if (DataSize != NULL) 810 { 811 // NOTE: CmpValueToData doesn't support big data (the function will 812 // bugcheck if so), FreeLdr is not supposed to read such data. 813 // If big data is needed, use instead CmpGetValueData. 814 // CmpGetValueData(Hive, ValueCell, DataSize, &DataCell, ...); 815 DataCell = CmpValueToData(Hive, ValueCell, &DataLength); 816 817 /* Does the caller want the data? */ 818 if ((Data != NULL) && (*DataSize != 0)) 819 { 820 RtlCopyMemory(Data, 821 DataCell, 822 min(*DataSize, DataLength)); 823 } 824 825 /* Return the actual data length */ 826 *DataSize = DataLength; 827 } 828 } 829 830 LONG 831 RegQueryValue( 832 _In_ HKEY Key, 833 _In_z_ PCWSTR ValueName, 834 _Out_opt_ PULONG Type, 835 _Out_opt_ PUCHAR Data, 836 _Inout_opt_ PULONG DataSize) 837 { 838 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key); 839 PCM_KEY_NODE KeyNode; 840 PCM_KEY_VALUE ValueCell; 841 HCELL_INDEX CellIndex; 842 UNICODE_STRING ValueNameString; 843 844 TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n", 845 Key, ValueName, Type, Data, DataSize); 846 847 /* Get the key node */ 848 KeyNode = GET_CM_KEY_NODE(Hive, Key); 849 ASSERT(KeyNode); 850 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 851 852 /* Initialize value name string */ 853 RtlInitUnicodeString(&ValueNameString, ValueName); 854 CellIndex = CmpFindValueByName(Hive, KeyNode, &ValueNameString); 855 if (CellIndex == HCELL_NIL) 856 { 857 TRACE("RegQueryValue value not found in key (%.*s)\n", 858 KeyNode->NameLength, KeyNode->Name); 859 HvReleaseCell(Hive, HKEY_TO_HCI(Key)); 860 return ERROR_FILE_NOT_FOUND; 861 } 862 HvReleaseCell(Hive, HKEY_TO_HCI(Key)); 863 864 /* Get the value cell */ 865 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex); 866 ASSERT(ValueCell != NULL); 867 868 RepGetValueData(Hive, ValueCell, Type, Data, DataSize); 869 870 HvReleaseCell(Hive, CellIndex); 871 872 return ERROR_SUCCESS; 873 } 874 875 /* 876 * NOTE: This function is currently unused in FreeLdr; however it is kept here 877 * as an implementation reference of RegEnumValue using CMLIB that may be used 878 * elsewhere in ReactOS. 879 */ 880 #if 0 881 LONG 882 RegEnumValue( 883 _In_ HKEY Key, 884 _In_ ULONG Index, 885 _Out_ PWCHAR ValueName, 886 _Inout_ PULONG NameSize, 887 _Out_opt_ PULONG Type, 888 _Out_opt_ PUCHAR Data, 889 _Inout_opt_ PULONG DataSize) 890 { 891 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key); 892 PCM_KEY_NODE KeyNode; 893 PCELL_DATA ValueListCell; 894 PCM_KEY_VALUE ValueCell; 895 USHORT NameLength; 896 897 TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n", 898 Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize); 899 900 /* Get the key node */ 901 KeyNode = GET_CM_KEY_NODE(Hive, Key); 902 ASSERT(KeyNode); 903 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 904 905 /* Check if the index is valid */ 906 if ((KeyNode->ValueList.Count == 0) || 907 (KeyNode->ValueList.List == HCELL_NIL) || 908 (Index >= KeyNode->ValueList.Count)) 909 { 910 ERR("RegEnumValue: index invalid\n"); 911 HvReleaseCell(Hive, HKEY_TO_HCI(Key)); 912 return ERROR_NO_MORE_ITEMS; 913 } 914 915 ValueListCell = (PCELL_DATA)HvGetCell(Hive, KeyNode->ValueList.List); 916 ASSERT(ValueListCell != NULL); 917 918 /* Get the value cell */ 919 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, ValueListCell->KeyList[Index]); 920 ASSERT(ValueCell != NULL); 921 ASSERT(ValueCell->Signature == CM_KEY_VALUE_SIGNATURE); 922 923 if (ValueCell->Flags & VALUE_COMP_NAME) 924 { 925 NameLength = CmpCompressedNameSize(ValueCell->Name, ValueCell->NameLength); 926 927 /* Compressed name */ 928 CmpCopyCompressedName(ValueName, 929 *NameSize, 930 ValueCell->Name, 931 ValueCell->NameLength); 932 } 933 else 934 { 935 NameLength = ValueCell->NameLength; 936 937 /* Normal name */ 938 RtlCopyMemory(ValueName, ValueCell->Name, 939 min(*NameSize, ValueCell->NameLength)); 940 } 941 942 if (*NameSize >= NameLength + sizeof(WCHAR)) 943 { 944 ValueName[NameLength / sizeof(WCHAR)] = UNICODE_NULL; 945 } 946 947 *NameSize = NameLength + sizeof(WCHAR); 948 949 RepGetValueData(Hive, ValueCell, Type, Data, DataSize); 950 951 HvReleaseCell(Hive, ValueListCell->KeyList[Index]); 952 HvReleaseCell(Hive, KeyNode->ValueList.List); 953 HvReleaseCell(Hive, HKEY_TO_HCI(Key)); 954 955 TRACE("RegEnumValue done -> %u, '%.*S'\n", *NameSize, *NameSize, ValueName); 956 return ERROR_SUCCESS; 957 } 958 #endif 959 960 /* EOF */ 961