[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / sdk / lib / cmlib / hiveinit.c
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 */