1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Configuration Manager Library - Registry Hive Loading & Initialization 5 * COPYRIGHT: Copyright 2001 - 2005 Eric Kohl 6 * Copyright 2005 Filip Navara <navaraf@reactos.org> 7 * Copyright 2021 Max Korostil 8 * Copyright 2022 George Bișoc <george.bisoc@reactos.org> 9 */ 10 11 #include "cmlib.h" 12 #define NDEBUG 13 #include <debug.h> 14 15 /* ENUMERATIONS *************************************************************/ 16 17 typedef enum _RESULT 18 { 19 NotHive, 20 Fail, 21 NoMemory, 22 HiveSuccess, 23 RecoverHeader, 24 RecoverData, 25 SelfHeal 26 } RESULT; 27 28 /* PRIVATE FUNCTIONS ********************************************************/ 29 30 /** 31 * @brief 32 * Validates the base block header of a registry 33 * file (hive or log). 34 * 35 * @param[in] BaseBlock 36 * A pointer to a base block header to 37 * be validated. 38 * 39 * @param[in] FileType 40 * The file type of a registry file to check 41 * against the file type of the base block. 42 * 43 * @return 44 * Returns TRUE if the base block header is valid, 45 * FALSE otherwise. 46 */ 47 BOOLEAN 48 CMAPI 49 HvpVerifyHiveHeader( 50 _In_ PHBASE_BLOCK BaseBlock, 51 _In_ ULONG FileType) 52 { 53 if (BaseBlock->Signature != HV_HBLOCK_SIGNATURE || 54 BaseBlock->Major != HSYS_MAJOR || 55 BaseBlock->Minor < HSYS_MINOR || 56 BaseBlock->Type != FileType || 57 BaseBlock->Format != HBASE_FORMAT_MEMORY || 58 BaseBlock->Cluster != 1 || 59 BaseBlock->Sequence1 != BaseBlock->Sequence2 || 60 HvpHiveHeaderChecksum(BaseBlock) != BaseBlock->CheckSum) 61 { 62 DPRINT1("Verify Hive Header failed:\n"); 63 DPRINT1(" Signature: 0x%x, expected 0x%x; Major: 0x%x, expected 0x%x\n", 64 BaseBlock->Signature, HV_HBLOCK_SIGNATURE, BaseBlock->Major, HSYS_MAJOR); 65 DPRINT1(" Minor: 0x%x expected to be >= 0x%x; Type: 0x%x, expected 0x%x\n", 66 BaseBlock->Minor, HSYS_MINOR, BaseBlock->Type, FileType); 67 DPRINT1(" Format: 0x%x, expected 0x%x; Cluster: 0x%x, expected 1\n", 68 BaseBlock->Format, HBASE_FORMAT_MEMORY, BaseBlock->Cluster); 69 DPRINT1(" Sequence: 0x%x, expected 0x%x; Checksum: 0x%x, expected 0x%x\n", 70 BaseBlock->Sequence1, BaseBlock->Sequence2, 71 HvpHiveHeaderChecksum(BaseBlock), BaseBlock->CheckSum); 72 73 return FALSE; 74 } 75 76 return TRUE; 77 } 78 79 /** 80 * @brief 81 * Frees all the bins within storage space 82 * associated with a hive descriptor. 83 * 84 * @param[in] Hive 85 * A pointer to a hive descriptor where 86 * all the bins are to be freed. 87 */ 88 VOID 89 CMAPI 90 HvpFreeHiveBins( 91 _In_ PHHIVE Hive) 92 { 93 ULONG i; 94 PHBIN Bin; 95 ULONG Storage; 96 97 for (Storage = 0; Storage < Hive->StorageTypeCount; Storage++) 98 { 99 Bin = NULL; 100 for (i = 0; i < Hive->Storage[Storage].Length; i++) 101 { 102 if (Hive->Storage[Storage].BlockList[i].BinAddress == (ULONG_PTR)NULL) 103 continue; 104 if (Hive->Storage[Storage].BlockList[i].BinAddress != (ULONG_PTR)Bin) 105 { 106 Bin = (PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress; 107 Hive->Free((PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress, 0); 108 } 109 Hive->Storage[Storage].BlockList[i].BinAddress = (ULONG_PTR)NULL; 110 Hive->Storage[Storage].BlockList[i].BlockAddress = (ULONG_PTR)NULL; 111 } 112 113 if (Hive->Storage[Storage].Length) 114 Hive->Free(Hive->Storage[Storage].BlockList, 0); 115 } 116 } 117 118 /** 119 * @brief 120 * Allocates a cluster-aligned hive base header block. 121 * 122 * @param[in] Hive 123 * A pointer to a hive descriptor where 124 * the header block allocator function is to 125 * be gathered from. 126 * 127 * @param[in] Paged 128 * If set to TRUE, the allocated base block will reside 129 * in paged pool, otherwise it will reside in non paged 130 * pool. 131 * 132 * @param[in] Tag 133 * A tag name to supply for the allocated memory block 134 * for identification. This is for debugging purposes. 135 * 136 * @return 137 * Returns an allocated base block header if the function 138 * succeeds, otherwise it returns NULL. 139 */ 140 static 141 __inline 142 PHBASE_BLOCK 143 HvpAllocBaseBlockAligned( 144 _In_ PHHIVE Hive, 145 _In_ BOOLEAN Paged, 146 _In_ ULONG Tag) 147 { 148 PHBASE_BLOCK BaseBlock; 149 ULONG Alignment; 150 151 ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster)); 152 153 /* Allocate the buffer */ 154 BaseBlock = Hive->Allocate(Hive->BaseBlockAlloc, Paged, Tag); 155 if (!BaseBlock) return NULL; 156 157 /* Check for, and enforce, alignment */ 158 Alignment = Hive->Cluster * HSECTOR_SIZE -1; 159 if ((ULONG_PTR)BaseBlock & Alignment) 160 { 161 /* Free the old header and reallocate a new one, always paged */ 162 Hive->Free(BaseBlock, Hive->BaseBlockAlloc); 163 BaseBlock = Hive->Allocate(PAGE_SIZE, TRUE, Tag); 164 if (!BaseBlock) return NULL; 165 166 Hive->BaseBlockAlloc = PAGE_SIZE; 167 } 168 169 return BaseBlock; 170 } 171 172 /** 173 * @brief 174 * Initializes a NULL-terminated Unicode hive file name 175 * of a hive header by copying the last 31 characters of 176 * the hive file name. Mainly used for debugging purposes. 177 * 178 * @param[in,out] BaseBlock 179 * A pointer to a base block header where the hive 180 * file name is to be copied to. 181 * 182 * @param[in] FileName 183 * A pointer to a Unicode string structure containing 184 * the hive file name to be copied from. If this argument 185 * is NULL, the base block will not have any hive file name. 186 */ 187 static 188 VOID 189 HvpInitFileName( 190 _Inout_ PHBASE_BLOCK BaseBlock, 191 _In_opt_ PCUNICODE_STRING FileName) 192 { 193 ULONG_PTR Offset; 194 SIZE_T Length; 195 196 /* Always NULL-initialize */ 197 RtlZeroMemory(BaseBlock->FileName, (HIVE_FILENAME_MAXLEN + 1) * sizeof(WCHAR)); 198 199 /* Copy the 31 last characters of the hive file name if any */ 200 if (!FileName) return; 201 202 if (FileName->Length / sizeof(WCHAR) <= HIVE_FILENAME_MAXLEN) 203 { 204 Offset = 0; 205 Length = FileName->Length; 206 } 207 else 208 { 209 Offset = FileName->Length / sizeof(WCHAR) - HIVE_FILENAME_MAXLEN; 210 Length = HIVE_FILENAME_MAXLEN * sizeof(WCHAR); 211 } 212 213 RtlCopyMemory(BaseBlock->FileName, FileName->Buffer + Offset, Length); 214 } 215 216 /** 217 * @brief 218 * Initializes a hive descriptor structure for a 219 * newly created hive in memory. 220 * 221 * @param[in,out] RegistryHive 222 * A pointer to a registry hive descriptor where 223 * the internal structures field are to be initialized 224 * for the said hive. 225 * 226 * @param[in] FileName 227 * A pointer to a Unicode string structure containing 228 * the hive file name to be copied from. If this argument 229 * is NULL, the base block will not have any hive file name. 230 * 231 * @return 232 * Returns STATUS_SUCCESS if the function has created the 233 * hive descriptor successfully. STATUS_NO_MEMORY is returned 234 * if the base header block could not be allocated. 235 */ 236 NTSTATUS 237 CMAPI 238 HvpCreateHive( 239 _Inout_ PHHIVE RegistryHive, 240 _In_opt_ PCUNICODE_STRING FileName) 241 { 242 PHBASE_BLOCK BaseBlock; 243 ULONG Index; 244 245 /* Allocate the base block */ 246 BaseBlock = HvpAllocBaseBlockAligned(RegistryHive, FALSE, TAG_CM); 247 if (BaseBlock == NULL) 248 return STATUS_NO_MEMORY; 249 250 /* Clear it */ 251 RtlZeroMemory(BaseBlock, RegistryHive->BaseBlockAlloc); 252 253 BaseBlock->Signature = HV_HBLOCK_SIGNATURE; 254 BaseBlock->Major = HSYS_MAJOR; 255 BaseBlock->Minor = HSYS_MINOR; 256 BaseBlock->Type = HFILE_TYPE_PRIMARY; 257 BaseBlock->Format = HBASE_FORMAT_MEMORY; 258 BaseBlock->Cluster = 1; 259 BaseBlock->RootCell = HCELL_NIL; 260 BaseBlock->Length = 0; 261 BaseBlock->Sequence1 = 1; 262 BaseBlock->Sequence2 = 1; 263 BaseBlock->TimeStamp.QuadPart = 0ULL; 264 265 /* 266 * No need to compute the checksum since 267 * the hive resides only in memory so far. 268 */ 269 BaseBlock->CheckSum = 0; 270 271 /* Set default boot type */ 272 BaseBlock->BootType = HBOOT_TYPE_REGULAR; 273 274 /* Setup hive data */ 275 RegistryHive->BaseBlock = BaseBlock; 276 RegistryHive->Version = BaseBlock->Minor; // == HSYS_MINOR 277 278 for (Index = 0; Index < 24; Index++) 279 { 280 RegistryHive->Storage[Stable].FreeDisplay[Index] = HCELL_NIL; 281 RegistryHive->Storage[Volatile].FreeDisplay[Index] = HCELL_NIL; 282 } 283 284 HvpInitFileName(BaseBlock, FileName); 285 286 return STATUS_SUCCESS; 287 } 288 289 /** 290 * @brief 291 * Initializes a hive descriptor from an already loaded 292 * registry hive stored in memory. The data of the hive is 293 * copied and it is prepared for read/write access. 294 * 295 * @param[in] Hive 296 * A pointer to a registry hive descriptor where 297 * the internal structures field are to be initialized 298 * from hive data that is already loaded in memory. 299 * 300 * @param[in] ChunkBase 301 * A pointer to a valid base block header containing 302 * registry header data for initialization. 303 * 304 * @param[in] FileName 305 * A pointer to a Unicode string structure containing 306 * the hive file name to be copied from. If this argument 307 * is NULL, the base block will not have any hive file name. 308 * 309 * @return 310 * Returns STATUS_SUCCESS if the function has initialized the 311 * hive descriptor successfully. STATUS_REGISTRY_CORRUPT is 312 * returned if the base block header contains invalid header 313 * data. STATUS_NO_MEMORY is returned if memory could not 314 * be allocated for registry stuff. 315 */ 316 NTSTATUS 317 CMAPI 318 HvpInitializeMemoryHive( 319 _In_ PHHIVE Hive, 320 _In_ PHBASE_BLOCK ChunkBase, 321 _In_opt_ PCUNICODE_STRING FileName) 322 { 323 SIZE_T BlockIndex; 324 PHBIN Bin, NewBin; 325 ULONG i; 326 ULONG BitmapSize; 327 PULONG BitmapBuffer; 328 SIZE_T ChunkSize; 329 330 ChunkSize = ChunkBase->Length; 331 DPRINT("ChunkSize: %zx\n", ChunkSize); 332 333 if (ChunkSize < sizeof(HBASE_BLOCK) || 334 !HvpVerifyHiveHeader(ChunkBase, HFILE_TYPE_PRIMARY)) 335 { 336 DPRINT1("Registry is corrupt: ChunkSize 0x%zx < sizeof(HBASE_BLOCK) 0x%zx, " 337 "or HvpVerifyHiveHeader() failed\n", ChunkSize, sizeof(HBASE_BLOCK)); 338 return STATUS_REGISTRY_CORRUPT; 339 } 340 341 /* Allocate the base block */ 342 Hive->BaseBlock = HvpAllocBaseBlockAligned(Hive, FALSE, TAG_CM); 343 if (Hive->BaseBlock == NULL) 344 return STATUS_NO_MEMORY; 345 346 RtlCopyMemory(Hive->BaseBlock, ChunkBase, sizeof(HBASE_BLOCK)); 347 348 /* Setup hive data */ 349 Hive->Version = ChunkBase->Minor; 350 351 /* 352 * Build a block list from the in-memory chunk and copy the data as 353 * we go. 354 */ 355 356 Hive->Storage[Stable].Length = (ULONG)(ChunkSize / HBLOCK_SIZE); 357 Hive->Storage[Stable].BlockList = 358 Hive->Allocate(Hive->Storage[Stable].Length * 359 sizeof(HMAP_ENTRY), FALSE, TAG_CM); 360 if (Hive->Storage[Stable].BlockList == NULL) 361 { 362 DPRINT1("Allocating block list failed\n"); 363 Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc); 364 return STATUS_NO_MEMORY; 365 } 366 367 for (BlockIndex = 0; BlockIndex < Hive->Storage[Stable].Length; ) 368 { 369 Bin = (PHBIN)((ULONG_PTR)ChunkBase + (BlockIndex + 1) * HBLOCK_SIZE); 370 if (Bin->Signature != HV_HBIN_SIGNATURE || 371 (Bin->Size % HBLOCK_SIZE) != 0 || 372 (Bin->FileOffset / HBLOCK_SIZE) != BlockIndex) 373 { 374 /* 375 * Bin is toast but luckily either the signature, size or offset 376 * is out of order. For the signature it is obvious what we are going 377 * to do, for the offset we are re-positioning the bin back to where it 378 * was and for the size we will set it up to a block size, since technically 379 * a hive bin is large as a block itself to accommodate cells. 380 */ 381 if (!CmIsSelfHealEnabled(FALSE)) 382 { 383 DPRINT1("Invalid bin at BlockIndex %lu, Signature 0x%x, Size 0x%x. Self-heal not possible!\n", 384 (unsigned long)BlockIndex, (unsigned)Bin->Signature, (unsigned)Bin->Size); 385 Hive->Free(Hive->Storage[Stable].BlockList, 0); 386 Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc); 387 return STATUS_REGISTRY_CORRUPT; 388 } 389 390 /* Fix this bin */ 391 Bin->Signature = HV_HBIN_SIGNATURE; 392 Bin->Size = HBLOCK_SIZE; 393 Bin->FileOffset = BlockIndex * HBLOCK_SIZE; 394 ChunkBase->BootType |= HBOOT_TYPE_SELF_HEAL; 395 DPRINT1("Bin at index %lu is corrupt and it has been repaired!\n", (unsigned long)BlockIndex); 396 } 397 398 NewBin = Hive->Allocate(Bin->Size, TRUE, TAG_CM); 399 if (NewBin == NULL) 400 { 401 Hive->Free(Hive->Storage[Stable].BlockList, 0); 402 Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc); 403 return STATUS_NO_MEMORY; 404 } 405 406 Hive->Storage[Stable].BlockList[BlockIndex].BinAddress = (ULONG_PTR)NewBin; 407 Hive->Storage[Stable].BlockList[BlockIndex].BlockAddress = (ULONG_PTR)NewBin; 408 409 RtlCopyMemory(NewBin, Bin, Bin->Size); 410 411 if (Bin->Size > HBLOCK_SIZE) 412 { 413 for (i = 1; i < Bin->Size / HBLOCK_SIZE; i++) 414 { 415 Hive->Storage[Stable].BlockList[BlockIndex + i].BinAddress = (ULONG_PTR)NewBin; 416 Hive->Storage[Stable].BlockList[BlockIndex + i].BlockAddress = 417 ((ULONG_PTR)NewBin + (i * HBLOCK_SIZE)); 418 } 419 } 420 421 BlockIndex += Bin->Size / HBLOCK_SIZE; 422 } 423 424 if (!NT_SUCCESS(HvpCreateHiveFreeCellList(Hive))) 425 { 426 HvpFreeHiveBins(Hive); 427 Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc); 428 return STATUS_NO_MEMORY; 429 } 430 431 BitmapSize = ROUND_UP(Hive->Storage[Stable].Length, 432 sizeof(ULONG) * 8) / 8; 433 BitmapBuffer = (PULONG)Hive->Allocate(BitmapSize, TRUE, TAG_CM); 434 if (BitmapBuffer == NULL) 435 { 436 HvpFreeHiveBins(Hive); 437 Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc); 438 return STATUS_NO_MEMORY; 439 } 440 441 RtlInitializeBitMap(&Hive->DirtyVector, BitmapBuffer, BitmapSize * 8); 442 RtlClearAllBits(&Hive->DirtyVector); 443 444 /* 445 * Mark the entire hive as dirty. Indeed we understand if we charged up 446 * the alternate variant of the primary hive (e.g. SYSTEM.ALT) because 447 * FreeLdr could not load the main SYSTEM hive, due to corruptions, and 448 * repairing it with a LOG did not help at all. 449 */ 450 if (ChunkBase->BootRecover == HBOOT_BOOT_RECOVERED_BY_ALTERNATE_HIVE) 451 { 452 RtlSetAllBits(&Hive->DirtyVector); 453 Hive->DirtyCount = Hive->DirtyVector.SizeOfBitMap; 454 } 455 456 HvpInitFileName(Hive->BaseBlock, FileName); 457 458 return STATUS_SUCCESS; 459 } 460 461 /** 462 * @brief 463 * Initializes a hive descriptor for an already loaded hive 464 * that is stored in memory. This descriptor serves to denote 465 * such hive as being "flat", that is, the data and properties 466 * can be only read and not written into. 467 * 468 * @param[in] Hive 469 * A pointer to a registry hive descriptor where 470 * the internal structures fields are to be initialized 471 * from hive data that is already loaded in memory. Such 472 * hive descriptor will become read-only and flat. 473 * 474 * @param[in] ChunkBase 475 * A pointer to a valid base block header containing 476 * registry header data for initialization. 477 * 478 * @return 479 * Returns STATUS_SUCCESS if the function has initialized the 480 * flat hive descriptor. STATUS_REGISTRY_CORRUPT is returned if 481 * the base block header contains invalid header data. 482 */ 483 NTSTATUS 484 CMAPI 485 HvpInitializeFlatHive( 486 _In_ PHHIVE Hive, 487 _In_ PHBASE_BLOCK ChunkBase) 488 { 489 if (!HvpVerifyHiveHeader(ChunkBase, HFILE_TYPE_PRIMARY)) 490 return STATUS_REGISTRY_CORRUPT; 491 492 /* Setup hive data */ 493 Hive->BaseBlock = ChunkBase; 494 Hive->Version = ChunkBase->Minor; 495 Hive->Flat = TRUE; 496 Hive->ReadOnly = TRUE; 497 498 Hive->StorageTypeCount = 1; 499 500 /* Set default boot type */ 501 ChunkBase->BootType = HBOOT_TYPE_REGULAR; 502 503 return STATUS_SUCCESS; 504 } 505 506 /** 507 * @brief 508 * Retrieves the base block hive header from the 509 * primary hive file stored in physical backing storage. 510 * This function may invoke a self-healing warning if 511 * hive header couldn't be obtained. See Return and Remarks 512 * sections for further information. 513 * 514 * @param[in] Hive 515 * A pointer to a registry hive descriptor that points 516 * to the primary hive being loaded. This descriptor is 517 * needed to obtain the hive header block from said hive. 518 * 519 * @param[in,out] HiveBaseBlock 520 * A pointer returned by the function that contains 521 * the hive header base block buffer obtained from 522 * the primary hive file pointed by the Hive argument. 523 * This parameter must not be NULL! 524 * 525 * @param[in,out] TimeStamp 526 * A pointer returned by the function that contains 527 * the time-stamp of the registry hive file at the 528 * moment of creation or modification. This parameter 529 * must not be NULL! 530 * 531 * @return 532 * This function returns a result indicator. That is, 533 * HiveSuccess is returned if the hive header was obtained 534 * successfully. NoMemory is returned if the hive base block 535 * could not be allocated. NotHive is returned if the hive file 536 * that's been read isn't actually a hive. RecoverHeader is 537 * returned if the header needs to be recovered. RecoverData 538 * is returned if the hive data needs to be returned. 539 * 540 * @remarks 541 * RecoverHeader and RecoverData are status indicators that 542 * invoke a self-healing procedure if the hive header could not 543 * be obtained in a normal way and as a matter of fact the whole 544 * registry initialization procedure is orchestrated. RecoverHeader 545 * implies that the base block header of a hive is corrupt and it 546 * needs to be recovered, whereas RecoverData implies the registry 547 * data is corrupt. The latter status indicator is less severe unlike 548 * the former because the system can cope with data loss. 549 */ 550 RESULT 551 CMAPI 552 HvpGetHiveHeader( 553 _In_ PHHIVE Hive, 554 _Inout_ PHBASE_BLOCK *HiveBaseBlock, 555 _Inout_ PLARGE_INTEGER TimeStamp) 556 { 557 PHBASE_BLOCK BaseBlock; 558 ULONG Result; 559 ULONG FileOffset; 560 PHBIN FirstBin; 561 562 ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster)); 563 564 /* Assume failure and allocate the base block */ 565 *HiveBaseBlock = NULL; 566 BaseBlock = HvpAllocBaseBlockAligned(Hive, TRUE, TAG_CM); 567 if (!BaseBlock) 568 { 569 DPRINT1("Failed to allocate an aligned base block buffer\n"); 570 return NoMemory; 571 } 572 573 /* Clear it */ 574 RtlZeroMemory(BaseBlock, sizeof(HBASE_BLOCK)); 575 576 /* Now read it from disk */ 577 FileOffset = 0; 578 Result = Hive->FileRead(Hive, 579 HFILE_TYPE_PRIMARY, 580 &FileOffset, 581 BaseBlock, 582 Hive->Cluster * HSECTOR_SIZE); 583 if (!Result) 584 { 585 /* 586 * Don't assume the hive is ultimately destroyed 587 * but instead try to read the first block of 588 * the first bin hive. So that we're sure of 589 * ourselves we can somewhat recover this hive. 590 */ 591 FileOffset = HBLOCK_SIZE; 592 Result = Hive->FileRead(Hive, 593 HFILE_TYPE_PRIMARY, 594 &FileOffset, 595 (PVOID)BaseBlock, 596 Hive->Cluster * HSECTOR_SIZE); 597 if (!Result) 598 { 599 DPRINT1("Failed to read the first block of the first bin hive (hive too corrupt)\n"); 600 Hive->Free(BaseBlock, Hive->BaseBlockAlloc); 601 return NotHive; 602 } 603 604 /* 605 * Deconstruct the casted buffer we got 606 * into a hive bin. Check if the offset 607 * position is in the right place (namely 608 * its offset must be 0 because it's the first 609 * bin) and it should have a sane signature. 610 */ 611 FirstBin = (PHBIN)BaseBlock; 612 if (FirstBin->Signature != HV_HBIN_SIGNATURE || 613 FirstBin->FileOffset != 0) 614 { 615 DPRINT1("Failed to read the first block of the first bin hive (hive too corrupt)\n"); 616 Hive->Free(BaseBlock, Hive->BaseBlockAlloc); 617 return NotHive; 618 } 619 620 /* 621 * There's still hope for this hive so acknowledge the 622 * caller this hive needs a recoverable header. 623 */ 624 *TimeStamp = BaseBlock->TimeStamp; 625 DPRINT1("The hive is not fully corrupt, the base block needs to be RECOVERED\n"); 626 return RecoverHeader; 627 } 628 629 /* 630 * This hive has a base block that's not maimed 631 * but is the header data valid? 632 * 633 * FIXME: We must check if primary and secondary 634 * sequences mismatch separately and fire up RecoverData 635 * in that case but due to a hack in HvLoadHive, let 636 * HvpVerifyHiveHeader check the sequences for now. 637 */ 638 if (!HvpVerifyHiveHeader(BaseBlock, HFILE_TYPE_PRIMARY)) 639 { 640 DPRINT1("The hive base header block needs to be RECOVERED\n"); 641 *TimeStamp = BaseBlock->TimeStamp; 642 Hive->Free(BaseBlock, Hive->BaseBlockAlloc); 643 return RecoverHeader; 644 } 645 646 /* Return information */ 647 *HiveBaseBlock = BaseBlock; 648 *TimeStamp = BaseBlock->TimeStamp; 649 return HiveSuccess; 650 } 651 652 /* 653 * FIXME: Disable compilation for AMD64 for now since it makes 654 * the FreeLdr binary size so large it makes booting impossible. 655 */ 656 #if !defined(_M_AMD64) 657 /** 658 * @brief 659 * Computes the hive space size by querying 660 * the file size of the associated hive file. 661 * 662 * @param[in] Hive 663 * A pointer to a hive descriptor where the 664 * hive length size is to be calculated. 665 * 666 * @return 667 * Returns the computed hive size. 668 */ 669 ULONG 670 CMAPI 671 HvpQueryHiveSize( 672 _In_ PHHIVE Hive) 673 { 674 #if !defined(CMLIB_HOST) && !defined(_BLDR_) 675 NTSTATUS Status; 676 FILE_STANDARD_INFORMATION FileStandard; 677 IO_STATUS_BLOCK IoStatusBlock; 678 #endif 679 ULONG HiveSize = 0; 680 681 /* 682 * Query the file size of the physical hive 683 * file. We need that information in order 684 * to ensure how big the hive actually is. 685 */ 686 #if !defined(CMLIB_HOST) && !defined(_BLDR_) 687 Status = ZwQueryInformationFile(((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY], 688 &IoStatusBlock, 689 &FileStandard, 690 sizeof(FILE_STANDARD_INFORMATION), 691 FileStandardInformation); 692 if (!NT_SUCCESS(Status)) 693 { 694 DPRINT1("ZwQueryInformationFile returned 0x%lx\n", Status); 695 return HiveSize; 696 } 697 698 /* Now compute the hive size */ 699 HiveSize = FileStandard.EndOfFile.u.LowPart - HBLOCK_SIZE; 700 #endif 701 return HiveSize; 702 } 703 704 /** 705 * @brief 706 * Recovers the base block header by obtaining 707 * it from a log file associated with the hive. 708 * 709 * @param[in] Hive 710 * A pointer to a hive descriptor associated 711 * with the log file where the hive header is 712 * to be read from. 713 * 714 * @param[in] TimeStamp 715 * A pointer to a time-stamp used to check 716 * if the provided time matches with that 717 * of the hive. 718 * 719 * @param[in,out] BaseBlock 720 * A pointer returned by the caller that contains 721 * the base block header that was read from the log. 722 * This base block could be also made manually by hand. 723 * See Remarks for further information. 724 * 725 * @return 726 * Returns HiveSuccess if the header was obtained 727 * normally from the log. NoMemory is returned if 728 * the base block header could not be allocated. 729 * Fail is returned if self-healing mode is disabled 730 * and the log couldn't be read or a write attempt 731 * to the primary hive has failed. SelfHeal is returned 732 * to indicate that self-heal mode goes further. 733 * 734 * @remarks 735 * When SelfHeal is returned this indicates that 736 * even the log we have gotten at hand is corrupt 737 * but since we do have at least a log our only hope 738 * is to reconstruct the pieces of the base header 739 * by hand. 740 */ 741 RESULT 742 CMAPI 743 HvpRecoverHeaderFromLog( 744 _In_ PHHIVE Hive, 745 _In_ PLARGE_INTEGER TimeStamp, 746 _Inout_ PHBASE_BLOCK *BaseBlock) 747 { 748 BOOLEAN Success; 749 PHBASE_BLOCK LogHeader; 750 ULONG FileOffset; 751 ULONG HiveSize; 752 BOOLEAN HeaderResuscitated; 753 754 /* 755 * The cluster must not be greater than what the 756 * base block can permit. 757 */ 758 ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster)); 759 760 /* Assume we haven't resuscitated the header */ 761 HeaderResuscitated = FALSE; 762 763 /* Allocate an aligned buffer for the log header */ 764 LogHeader = HvpAllocBaseBlockAligned(Hive, TRUE, TAG_CM); 765 if (!LogHeader) 766 { 767 DPRINT1("Failed to allocate memory for the log header\n"); 768 return NoMemory; 769 } 770 771 /* Zero out our header buffer */ 772 RtlZeroMemory(LogHeader, HSECTOR_SIZE); 773 774 /* Get the base header from the log */ 775 FileOffset = 0; 776 Success = Hive->FileRead(Hive, 777 HFILE_TYPE_LOG, 778 &FileOffset, 779 LogHeader, 780 Hive->Cluster * HSECTOR_SIZE); 781 if (!Success || 782 !HvpVerifyHiveHeader(LogHeader, HFILE_TYPE_LOG) || 783 TimeStamp->HighPart != LogHeader->TimeStamp.HighPart || 784 TimeStamp->LowPart != LogHeader->TimeStamp.LowPart) 785 { 786 /* 787 * We failed to read the base block header from 788 * the log, or the header itself or timestamp is 789 * invalid. Check if self healing is enabled. 790 */ 791 if (!CmIsSelfHealEnabled(FALSE)) 792 { 793 DPRINT1("The log couldn't be read and self-healing mode is disabled\n"); 794 Hive->Free(LogHeader, Hive->BaseBlockAlloc); 795 return Fail; 796 } 797 798 /* 799 * Determine the size of this hive so that 800 * we can estabilish the length of the base 801 * block we are trying to resuscitate. 802 */ 803 HiveSize = HvpQueryHiveSize(Hive); 804 if (HiveSize == 0) 805 { 806 DPRINT1("Failed to query the hive size\n"); 807 Hive->Free(LogHeader, Hive->BaseBlockAlloc); 808 return Fail; 809 } 810 811 /* 812 * We can still resuscitate the base header if we 813 * could not grab one from the log by reconstructing 814 * the header internals by hand (this assumes the 815 * root cell is not NIL nor damaged). CmCheckRegistry 816 * does the ultimate judgement whether the root cell 817 * is fatally kaput or not after the hive has been 818 * initialized and loaded. 819 * 820 * For more information about base block header 821 * resuscitation, see https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md#notes-4. 822 */ 823 LogHeader->Signature = HV_HBLOCK_SIGNATURE; 824 LogHeader->Sequence1 = 1; 825 LogHeader->Sequence2 = 1; 826 LogHeader->Cluster = 1; 827 LogHeader->Length = HiveSize; 828 LogHeader->CheckSum = HvpHiveHeaderChecksum(LogHeader); 829 830 /* 831 * Acknowledge that we have resuscitated 832 * the header. 833 */ 834 HeaderResuscitated = TRUE; 835 DPRINT1("Header has been resuscitated, triggering self-heal mode\n"); 836 } 837 838 /* 839 * Tag this log header as a primary hive before 840 * writing it to the hive. 841 */ 842 LogHeader->Type = HFILE_TYPE_PRIMARY; 843 844 /* 845 * If we have not made attempts of recovering 846 * the header due to log corruption then we 847 * have to compute the checksum. This is 848 * already done when the header has been resuscitated 849 * so don't try to do it twice. 850 */ 851 if (!HeaderResuscitated) 852 { 853 LogHeader->CheckSum = HvpHiveHeaderChecksum(LogHeader); 854 } 855 856 /* Write the header back to hive now */ 857 Success = Hive->FileWrite(Hive, 858 HFILE_TYPE_PRIMARY, 859 &FileOffset, 860 LogHeader, 861 Hive->Cluster * HSECTOR_SIZE); 862 if (!Success) 863 { 864 DPRINT1("Couldn't write the base header to primary hive\n"); 865 Hive->Free(LogHeader, Hive->BaseBlockAlloc); 866 return Fail; 867 } 868 869 *BaseBlock = LogHeader; 870 return HeaderResuscitated ? SelfHeal : HiveSuccess; 871 } 872 873 /** 874 * @brief 875 * Recovers the registry data by obtaining it 876 * from a log that is associated with the hive. 877 * 878 * @param[in] Hive 879 * A pointer to a hive descriptor associated 880 * with the log file where the hive data is to 881 * be read from. 882 * 883 * @param[in] BaseBlock 884 * A pointer to a base block header. 885 * 886 * @return 887 * Returns HiveSuccess if the data was obtained 888 * normally from the log. Fail is returned if 889 * self-healing is disabled and we couldn't be 890 * able to read the data from the log or the 891 * dirty vector signature is garbage or we 892 * failed to write the data block to the primary 893 * hive. SelfHeal is returned to indicate that 894 * the log is corrupt and the system will continue 895 * to be recovered at the expense of data loss. 896 */ 897 RESULT 898 CMAPI 899 HvpRecoverDataFromLog( 900 _In_ PHHIVE Hive, 901 _In_ PHBASE_BLOCK BaseBlock) 902 { 903 BOOLEAN Success; 904 ULONG FileOffset; 905 ULONG BlockIndex; 906 ULONG LogIndex; 907 ULONG StorageLength; 908 UCHAR DirtyVector[HSECTOR_SIZE]; 909 UCHAR Buffer[HBLOCK_SIZE]; 910 911 /* Read the dirty data from the log */ 912 FileOffset = HV_LOG_HEADER_SIZE; 913 Success = Hive->FileRead(Hive, 914 HFILE_TYPE_LOG, 915 &FileOffset, 916 DirtyVector, 917 HSECTOR_SIZE); 918 if (!Success) 919 { 920 if (!CmIsSelfHealEnabled(FALSE)) 921 { 922 DPRINT1("The log couldn't be read and self-healing mode is disabled\n"); 923 return Fail; 924 } 925 926 /* 927 * There's nothing we can do on a situation 928 * where dirty data could not be read from 929 * the log. It does not make much sense to 930 * behead the system on such scenario so 931 * trigger a self-heal and go on. The worst 932 * thing that can happen? Data loss, that's it. 933 */ 934 DPRINT1("Triggering self-heal mode, DATA LOSS IS IMMINENT\n"); 935 return SelfHeal; 936 } 937 938 /* Check the dirty vector */ 939 if (*((PULONG)DirtyVector) != HV_LOG_DIRTY_SIGNATURE) 940 { 941 if (!CmIsSelfHealEnabled(FALSE)) 942 { 943 DPRINT1("The log's dirty vector signature is not valid\n"); 944 return Fail; 945 } 946 947 /* 948 * Trigger a self-heal like above. If the 949 * vector signature is garbage then logically 950 * whatever comes after the signature is also 951 * garbage. 952 */ 953 DPRINT1("Triggering self-heal mode, DATA LOSS IS IMMINENT\n"); 954 return SelfHeal; 955 } 956 957 /* Now read each data individually and write it back to hive */ 958 LogIndex = 0; 959 StorageLength = BaseBlock->Length / HBLOCK_SIZE; 960 for (BlockIndex = 0; BlockIndex < StorageLength; BlockIndex++) 961 { 962 /* Skip this block if it's not dirty and go to the next one */ 963 if (DirtyVector[BlockIndex + sizeof(HV_LOG_DIRTY_SIGNATURE)] != HV_LOG_DIRTY_BLOCK) 964 { 965 continue; 966 } 967 968 FileOffset = HSECTOR_SIZE + HSECTOR_SIZE + LogIndex * HBLOCK_SIZE; 969 Success = Hive->FileRead(Hive, 970 HFILE_TYPE_LOG, 971 &FileOffset, 972 Buffer, 973 HBLOCK_SIZE); 974 if (!Success) 975 { 976 DPRINT1("Failed to read the dirty block (index %lu)\n", BlockIndex); 977 return Fail; 978 } 979 980 FileOffset = HBLOCK_SIZE + BlockIndex * HBLOCK_SIZE; 981 Success = Hive->FileWrite(Hive, 982 HFILE_TYPE_PRIMARY, 983 &FileOffset, 984 Buffer, 985 HBLOCK_SIZE); 986 if (!Success) 987 { 988 DPRINT1("Failed to write dirty block to hive (index %lu)\n", BlockIndex); 989 return Fail; 990 } 991 992 /* Increment the index in log as we continue further */ 993 LogIndex++; 994 } 995 996 return HiveSuccess; 997 } 998 #endif 999 1000 /** 1001 * @brief 1002 * Loads a registry hive from a physical hive file 1003 * within the physical backing storage. Base block 1004 * and registry data are read from the said physical 1005 * hive file. This function can perform registry recovery 1006 * if hive loading could not be done normally. 1007 * 1008 * @param[in] Hive 1009 * A pointer to a hive descriptor where the said hive 1010 * is to be loaded from the physical hive file. 1011 * 1012 * @param[in] FileName 1013 * A pointer to a NULL-terminated Unicode string structure 1014 * containing the hive file name to be copied from. 1015 * 1016 * @return 1017 * STATUS_SUCCESS is returned if the hive has been loaded 1018 * successfully. STATUS_INSUFFICIENT_RESOURCES is returned 1019 * if there's not enough memory resources to satisfy registry 1020 * operations and/or requests. STATUS_NOT_REGISTRY_FILE is returned 1021 * if the hive is not actually a hive file. STATUS_REGISTRY_CORRUPT 1022 * is returned if the hive has subdued previous damage and 1023 * the hive could not be recovered because there's no 1024 * log present or self healing is disabled. STATUS_REGISTRY_RECOVERED 1025 * is returned if the hive has been recovered. An eventual flush 1026 * of the registry is needed after the hive's been fully loaded. 1027 */ 1028 NTSTATUS 1029 CMAPI 1030 HvLoadHive( 1031 _In_ PHHIVE Hive, 1032 _In_opt_ PCUNICODE_STRING FileName) 1033 { 1034 NTSTATUS Status; 1035 BOOLEAN Success; 1036 PHBASE_BLOCK BaseBlock = NULL; 1037 /* FIXME: See the comment above (near HvpQueryHiveSize) */ 1038 #if defined(_M_AMD64) 1039 ULONG Result; 1040 #else 1041 ULONG Result, Result2; 1042 #endif 1043 LARGE_INTEGER TimeStamp; 1044 ULONG Offset = 0; 1045 PVOID HiveData; 1046 ULONG FileSize; 1047 BOOLEAN HiveSelfHeal = FALSE; 1048 1049 /* Get the hive header */ 1050 Result = HvpGetHiveHeader(Hive, &BaseBlock, &TimeStamp); 1051 switch (Result) 1052 { 1053 /* Out of memory */ 1054 case NoMemory: 1055 { 1056 /* Fail */ 1057 DPRINT1("There's no enough memory to get the header\n"); 1058 return STATUS_INSUFFICIENT_RESOURCES; 1059 } 1060 1061 /* Not a hive */ 1062 case NotHive: 1063 { 1064 /* Fail */ 1065 DPRINT1("The hive is not an actual registry hive file\n"); 1066 return STATUS_NOT_REGISTRY_FILE; 1067 } 1068 1069 /* Hive data needs a repair */ 1070 case RecoverData: 1071 { 1072 /* 1073 * FIXME: We must be handling this status 1074 * case if the header isn't corrupt but 1075 * the counter sequences do not match but 1076 * due to a hack in HvLoadHive we have 1077 * to do both a header + data recovery. 1078 * RecoverHeader also implies RecoverData 1079 * anyway. When HvLoadHive gets rid of 1080 * that hack, data recovery must be done 1081 * after we read the hive block by block. 1082 */ 1083 break; 1084 } 1085 1086 /* Hive header needs a repair */ 1087 case RecoverHeader: 1088 /* FIXME: See the comment above (near HvpQueryHiveSize) */ 1089 #if defined(_M_AMD64) 1090 { 1091 return STATUS_REGISTRY_CORRUPT; 1092 } 1093 #else 1094 { 1095 /* Check if this hive has a log at hand to begin with */ 1096 #if (NTDDI_VERSION < NTDDI_VISTA) 1097 if (!Hive->Log) 1098 { 1099 DPRINT1("The hive has no log for header recovery\n"); 1100 return STATUS_REGISTRY_CORRUPT; 1101 } 1102 #endif 1103 1104 /* The header needs to be recovered so do it */ 1105 DPRINT1("Attempting to heal the header...\n"); 1106 Result2 = HvpRecoverHeaderFromLog(Hive, &TimeStamp, &BaseBlock); 1107 if (Result2 == NoMemory) 1108 { 1109 DPRINT1("There's no enough memory to recover header from log\n"); 1110 return STATUS_INSUFFICIENT_RESOURCES; 1111 } 1112 1113 /* Did we fail? */ 1114 if (Result2 == Fail) 1115 { 1116 DPRINT1("Failed to recover the hive header\n"); 1117 return STATUS_REGISTRY_CORRUPT; 1118 } 1119 1120 /* Did we trigger the self-heal mode? */ 1121 if (Result2 == SelfHeal) 1122 { 1123 HiveSelfHeal = TRUE; 1124 } 1125 1126 /* Now recover the data */ 1127 Result2 = HvpRecoverDataFromLog(Hive, BaseBlock); 1128 if (Result2 == Fail) 1129 { 1130 DPRINT1("Failed to recover the hive data\n"); 1131 return STATUS_REGISTRY_CORRUPT; 1132 } 1133 1134 /* Tag the boot as self heal if we haven't done it before */ 1135 if ((Result2 == SelfHeal) && (!HiveSelfHeal)) 1136 { 1137 HiveSelfHeal = TRUE; 1138 } 1139 1140 break; 1141 } 1142 #endif 1143 } 1144 1145 /* Set the boot type */ 1146 BaseBlock->BootType = HiveSelfHeal ? HBOOT_TYPE_SELF_HEAL : HBOOT_TYPE_REGULAR; 1147 1148 /* Setup hive data */ 1149 Hive->BaseBlock = BaseBlock; 1150 Hive->Version = BaseBlock->Minor; 1151 1152 /* Allocate a buffer large enough to hold the hive */ 1153 FileSize = HBLOCK_SIZE + BaseBlock->Length; // == sizeof(HBASE_BLOCK) + BaseBlock->Length; 1154 HiveData = Hive->Allocate(FileSize, TRUE, TAG_CM); 1155 if (!HiveData) 1156 { 1157 Hive->Free(BaseBlock, Hive->BaseBlockAlloc); 1158 DPRINT1("There's no enough memory to allocate hive data\n"); 1159 return STATUS_INSUFFICIENT_RESOURCES; 1160 } 1161 1162 /* HACK (see explanation below): Now read the whole hive */ 1163 Success = Hive->FileRead(Hive, 1164 HFILE_TYPE_PRIMARY, 1165 &Offset, 1166 HiveData, 1167 FileSize); 1168 if (!Success) 1169 { 1170 DPRINT1("Failed to read the whole hive\n"); 1171 Hive->Free(HiveData, FileSize); 1172 Hive->Free(BaseBlock, Hive->BaseBlockAlloc); 1173 return STATUS_NOT_REGISTRY_FILE; 1174 } 1175 1176 /* 1177 * HACK (FIXME): Free our base block... it's useless in 1178 * this implementation. 1179 * 1180 * And it's useless because while the idea of reading the 1181 * hive from physical file is correct, the implementation 1182 * is hacky and incorrect. Instead of reading the whole hive, 1183 * we should be instead reading the hive block by block, 1184 * deconstruct the block buffer and enlist the bins and 1185 * prepare the storage for the hive. What we currently do 1186 * is we try to initialize the hive storage and bins enlistment 1187 * by calling HvpInitializeMemoryHive below. This mixes 1188 * HINIT_FILE and HINIT_MEMORY together which is disgusting 1189 * because HINIT_FILE implementation shouldn't be calling 1190 * HvpInitializeMemoryHive. 1191 */ 1192 Hive->Free(BaseBlock, Hive->BaseBlockAlloc); 1193 Status = HvpInitializeMemoryHive(Hive, HiveData, FileName); 1194 if (!NT_SUCCESS(Status)) 1195 { 1196 DPRINT1("Failed to initialize hive from memory\n"); 1197 Hive->Free(HiveData, FileSize); 1198 return Status; 1199 } 1200 1201 /* 1202 * If we have done some sort of recovery against 1203 * the hive we were going to load it from file, 1204 * tell the caller we did recover it. The caller 1205 * is responsible to flush the data later on. 1206 */ 1207 return (Result == RecoverHeader) ? STATUS_REGISTRY_RECOVERED : STATUS_SUCCESS; 1208 } 1209 1210 /** 1211 * @brief 1212 * Initializes a registry hive. It allocates a hive 1213 * descriptor and sets up the hive type depending 1214 * on the type chosen by the caller. 1215 * 1216 * @param[in,out] RegistryHive 1217 * A pointer to a hive descriptor to be initialized. 1218 * 1219 * @param[in] OperationType 1220 * The operation type to choose for hive initialization. 1221 * For further information about this, see Remarks. 1222 * 1223 * @param[in] HiveFlags 1224 * A hive flag. Such flag is used to determine what kind 1225 * of action must be taken into the hive or what aspects 1226 * must be taken into account for such hive. For further 1227 * information, see Remarks. 1228 * 1229 * @param[in] FileType 1230 * Hive file type. For the newly initialized hive, you can 1231 * choose from three different types for the hive: 1232 * 1233 * HFILE_TYPE_PRIMARY - Initializes a hive as primary hive 1234 * of the system. 1235 * 1236 * HFILE_TYPE_LOG - The newly created hive is a hive log. 1237 * Logs don't exist per se but they're accompanied with their 1238 * associated primary hives. The Log field member of the hive 1239 * descriptor is set to TRUE. 1240 * 1241 * HFILE_TYPE_EXTERNAL - The newly created hive is a portable 1242 * hive, that can be used and copied for different machines, 1243 * unlike primary hives. 1244 * 1245 * HFILE_TYPE_ALTERNATE - The newly created hive is an alternate hive. 1246 * Technically speaking it is the same as a primary hive (the representation 1247 * of on-disk image of the registry header is HFILE_TYPE_PRIMARY), with 1248 * the purpose is to serve as a backup hive. The Alternate field of the 1249 * hive descriptor is set to TRUE. Only the SYSTEM hive has a backup 1250 * alternate hive. 1251 * 1252 * @param[in] HiveData 1253 * An arbitrary pointer that points to the hive data. Usually this 1254 * data is in form of a hive base block given by the caller of this 1255 * function. 1256 * 1257 * @param[in] Allocate 1258 * A pointer to a ALLOCATE_ROUTINE function that describes 1259 * the main allocation routine for this hive. This parameter 1260 * can be NULL. 1261 * 1262 * @param[in] Free 1263 * A pointer to a FREE_ROUTINE function that describes the 1264 * the main memory freeing routine for this hive. This parameter 1265 * can be NULL. 1266 * 1267 * @param[in] FileSetSize 1268 * A pointer to a FILE_SET_SIZE_ROUTINE function that describes 1269 * the file set size routine for this hive. This parameter 1270 * can be NULL. 1271 * 1272 * @param[in] FileWrite 1273 * A pointer to a FILE_WRITE_ROUTINE function that describes 1274 * the file writing routine for this hive. This parameter 1275 * can be NULL. 1276 * 1277 * @param[in] FileRead 1278 * A pointer to a FILE_READ_ROUTINE function that describes 1279 * the file reading routine for this hive. This parameter 1280 * can be NULL. 1281 * 1282 * @param[in] FileFlush 1283 * A pointer to a FILE_FLUSH_ROUTINE function that describes 1284 * the file flushing routine for this hive. This parameter 1285 * can be NULL. 1286 * 1287 * @param[in] Cluster 1288 * The registry hive cluster to be set. Usually this value 1289 * is set to 1. 1290 * 1291 * @param[in] FileName 1292 * A to a NULL-terminated Unicode string structure containing 1293 * the hive file name. This parameter can be NULL. 1294 * 1295 * @return 1296 * Returns STATUS_SUCCESS if the function has successfully 1297 * initialized the hive. STATUS_REGISTRY_RECOVERED is returned 1298 * if the hive has subdued previous damage and it's been recovered. 1299 * This function will perform a hive writing and flushing with 1300 * healthy and recovered data in that case. STATUS_REGISTRY_IO_FAILED 1301 * is returned if registry hive writing/flushing of recovered data 1302 * has failed. STATUS_INVALID_PARAMETER is returned if an invalid 1303 * operation type pointed by OperationType parameter has been 1304 * submitted. A failure NTSTATUS code is returned otherwise. 1305 * 1306 * @remarks 1307 * OperationType parameter influences how should the hive be 1308 * initialized. These are the following supported operation 1309 * types: 1310 * 1311 * HINIT_CREATE -- Creates a new fresh hive. 1312 * 1313 * HINIT_MEMORY -- Initializes a registry hive that already exists 1314 * from memory. The hive data is copied from the 1315 * loaded hive in memory and used for read/write 1316 * access. 1317 * 1318 * HINIT_FLAT -- Initializes a flat registry hive, with data that can 1319 * only be read and not written into. Cells are always 1320 * allocated on a flat hive. 1321 * 1322 * HINIT_FILE -- Initializes a hive from a hive file from the physical 1323 * backing storage of the system. In this situation the 1324 * function will perform self-healing and resuscitation 1325 * procedures if data read from the physical hive file 1326 * is corrupt. 1327 * 1328 * HINIT_MEMORY_INPLACE -- This operation type is similar to HINIT_FLAT, 1329 * with the difference is that the hive is initialized 1330 * with hive data from memory. The hive can only be read 1331 * and not written into. 1332 * 1333 * HINIT_MAPFILE -- Initializes a hive from a hive file from the physical 1334 * backing storage of the system. Unlike HINIT_FILE, the 1335 * initialized hive is not backed to paged pool in memory 1336 * but rather through mapping views. 1337 * 1338 * Alongside the operation type, the hive flags also influence the aspect 1339 * of the newly initialized hive. These are the following supported hive 1340 * flags: 1341 * 1342 * HIVE_VOLATILE -- Tells the function that this hive will be volatile, that 1343 * is, the data stored inside the hive space resides only 1344 * in volatile memory of the system, aka the RAM, and the 1345 * data will be erased upon shutdown of the system. 1346 * 1347 * HIVE_NOLAZYFLUSH -- Tells the function that no lazy flushing must be 1348 * done to this hive. 1349 */ 1350 NTSTATUS 1351 CMAPI 1352 HvInitialize( 1353 _Inout_ PHHIVE RegistryHive, 1354 _In_ ULONG OperationType, 1355 _In_ ULONG HiveFlags, 1356 _In_ ULONG FileType, 1357 _In_opt_ PVOID HiveData, 1358 _In_opt_ PALLOCATE_ROUTINE Allocate, 1359 _In_opt_ PFREE_ROUTINE Free, 1360 _In_opt_ PFILE_SET_SIZE_ROUTINE FileSetSize, 1361 _In_opt_ PFILE_WRITE_ROUTINE FileWrite, 1362 _In_opt_ PFILE_READ_ROUTINE FileRead, 1363 _In_opt_ PFILE_FLUSH_ROUTINE FileFlush, 1364 _In_ ULONG Cluster, 1365 _In_opt_ PCUNICODE_STRING FileName) 1366 { 1367 NTSTATUS Status; 1368 PHHIVE Hive = RegistryHive; 1369 1370 /* 1371 * Create a new hive structure that will hold all the maintenance data. 1372 */ 1373 1374 RtlZeroMemory(Hive, sizeof(HHIVE)); 1375 Hive->Signature = HV_HHIVE_SIGNATURE; 1376 1377 Hive->Allocate = Allocate; 1378 Hive->Free = Free; 1379 Hive->FileSetSize = FileSetSize; 1380 Hive->FileWrite = FileWrite; 1381 Hive->FileRead = FileRead; 1382 Hive->FileFlush = FileFlush; 1383 1384 Hive->RefreshCount = 0; 1385 Hive->StorageTypeCount = HTYPE_COUNT; 1386 Hive->Cluster = Cluster; 1387 Hive->BaseBlockAlloc = sizeof(HBASE_BLOCK); // == HBLOCK_SIZE 1388 1389 Hive->Version = HSYS_MINOR; 1390 #if (NTDDI_VERSION < NTDDI_VISTA) 1391 Hive->Log = (FileType == HFILE_TYPE_LOG); 1392 Hive->Alternate = (FileType == HFILE_TYPE_ALTERNATE); 1393 #endif 1394 Hive->HiveFlags = HiveFlags & ~HIVE_NOLAZYFLUSH; 1395 1396 // TODO: The CellRoutines point to different callbacks 1397 // depending on the OperationType. 1398 Hive->GetCellRoutine = HvpGetCellData; 1399 Hive->ReleaseCellRoutine = NULL; 1400 1401 switch (OperationType) 1402 { 1403 case HINIT_CREATE: 1404 { 1405 /* Create a new fresh hive */ 1406 Status = HvpCreateHive(Hive, FileName); 1407 break; 1408 } 1409 1410 case HINIT_MEMORY: 1411 { 1412 /* Initialize a hive from memory */ 1413 Status = HvpInitializeMemoryHive(Hive, HiveData, FileName); 1414 break; 1415 } 1416 1417 case HINIT_FLAT: 1418 { 1419 /* Initialize a flat read-only hive */ 1420 Status = HvpInitializeFlatHive(Hive, HiveData); 1421 break; 1422 } 1423 1424 case HINIT_FILE: 1425 { 1426 /* Initialize a hive by loading it from physical file in backing storage */ 1427 Status = HvLoadHive(Hive, FileName); 1428 if ((Status != STATUS_SUCCESS) && 1429 (Status != STATUS_REGISTRY_RECOVERED)) 1430 { 1431 /* Unrecoverable failure */ 1432 DPRINT1("Registry hive couldn't be initialized, it's corrupt (hive 0x%p)\n", Hive); 1433 return Status; 1434 } 1435 1436 /* FIXME: See the comment above (near HvpQueryHiveSize) */ 1437 #if !defined(_M_AMD64) 1438 /* 1439 * Check if we have recovered this hive. We are responsible to 1440 * flush the primary hive back to backing storage afterwards. 1441 */ 1442 if (Status == STATUS_REGISTRY_RECOVERED) 1443 { 1444 if (!HvSyncHiveFromRecover(Hive)) 1445 { 1446 DPRINT1("Fail to write healthy data back to hive\n"); 1447 return STATUS_REGISTRY_IO_FAILED; 1448 } 1449 1450 /* 1451 * We are saved from hell, now clear out the 1452 * dirty bits and dirty count. 1453 * 1454 * FIXME: We must as well clear out the log 1455 * and reset its size to 0 but we are lacking 1456 * in code that deals with log growing/shrinking 1457 * management. When the time comes to implement 1458 * this stuff we must set the LogSize and file size 1459 * to 0 here. 1460 */ 1461 RtlClearAllBits(&Hive->DirtyVector); 1462 Hive->DirtyCount = 0; 1463 1464 /* 1465 * Masquerade the status code as success. 1466 * STATUS_REGISTRY_RECOVERED is not a failure 1467 * code but not STATUS_SUCCESS either so the caller 1468 * thinks we failed at our job. 1469 */ 1470 Status = STATUS_SUCCESS; 1471 } 1472 #endif 1473 break; 1474 } 1475 1476 case HINIT_MEMORY_INPLACE: 1477 { 1478 // Status = HvpInitializeMemoryInplaceHive(Hive, HiveData); 1479 // break; 1480 DPRINT1("HINIT_MEMORY_INPLACE is UNIMPLEMENTED\n"); 1481 return STATUS_NOT_IMPLEMENTED; 1482 } 1483 1484 case HINIT_MAPFILE: 1485 { 1486 DPRINT1("HINIT_MAPFILE is UNIMPLEMENTED\n"); 1487 return STATUS_NOT_IMPLEMENTED; 1488 } 1489 1490 default: 1491 { 1492 DPRINT1("Invalid operation type (OperationType = %lu)\n", OperationType); 1493 return STATUS_INVALID_PARAMETER; 1494 } 1495 } 1496 1497 return Status; 1498 } 1499 1500 /** 1501 * @brief 1502 * Frees all the bins within the storage, the dirty vector 1503 * and the base block associated with the given registry 1504 * hive descriptor. 1505 * 1506 * @param[in] RegistryHive 1507 * A pointer to a hive descriptor where all of its data 1508 * is to be freed. 1509 */ 1510 VOID 1511 CMAPI 1512 HvFree( 1513 _In_ PHHIVE RegistryHive) 1514 { 1515 if (!RegistryHive->ReadOnly) 1516 { 1517 /* Release hive bitmap */ 1518 if (RegistryHive->DirtyVector.Buffer) 1519 { 1520 RegistryHive->Free(RegistryHive->DirtyVector.Buffer, 0); 1521 } 1522 1523 HvpFreeHiveBins(RegistryHive); 1524 1525 /* Free the BaseBlock */ 1526 if (RegistryHive->BaseBlock) 1527 { 1528 RegistryHive->Free(RegistryHive->BaseBlock, RegistryHive->BaseBlockAlloc); 1529 RegistryHive->BaseBlock = NULL; 1530 } 1531 } 1532 } 1533 1534 /* EOF */ 1535