xref: /reactos/sdk/lib/cmlib/hiveinit.c (revision ee5338ff)
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
HvpVerifyHiveHeader(_In_ PHBASE_BLOCK BaseBlock,_In_ ULONG FileType)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
HvpFreeHiveBins(_In_ PHHIVE Hive)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
HvpAllocBaseBlockAligned(_In_ PHHIVE Hive,_In_ BOOLEAN Paged,_In_ ULONG Tag)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
HvpInitFileName(_Inout_ PHBASE_BLOCK BaseBlock,_In_opt_ PCUNICODE_STRING FileName)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
HvpCreateHive(_Inout_ PHHIVE RegistryHive,_In_opt_ PCUNICODE_STRING FileName)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
HvpInitializeMemoryHive(_In_ PHHIVE Hive,_In_ PHBASE_BLOCK ChunkBase,_In_opt_ PCUNICODE_STRING FileName)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
HvpInitializeFlatHive(_In_ PHHIVE Hive,_In_ PHBASE_BLOCK ChunkBase)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
HvpGetHiveHeader(_In_ PHHIVE Hive,_Inout_ PHBASE_BLOCK * HiveBaseBlock,_Inout_ PLARGE_INTEGER TimeStamp)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
HvpQueryHiveSize(_In_ PHHIVE Hive)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
HvpRecoverHeaderFromLog(_In_ PHHIVE Hive,_In_ PLARGE_INTEGER TimeStamp,_Inout_ PHBASE_BLOCK * BaseBlock)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
HvpRecoverDataFromLog(_In_ PHHIVE Hive,_In_ PHBASE_BLOCK BaseBlock)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 %u)\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 %u)\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
HvLoadHive(_In_ PHHIVE Hive,_In_opt_ PCUNICODE_STRING FileName)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
HvInitialize(_Inout_ PHHIVE RegistryHive,_In_ ULONG OperationType,_In_ ULONG HiveFlags,_In_ ULONG FileType,_In_opt_ PVOID HiveData,_In_opt_ PALLOCATE_ROUTINE Allocate,_In_opt_ PFREE_ROUTINE Free,_In_opt_ PFILE_SET_SIZE_ROUTINE FileSetSize,_In_opt_ PFILE_WRITE_ROUTINE FileWrite,_In_opt_ PFILE_READ_ROUTINE FileRead,_In_opt_ PFILE_FLUSH_ROUTINE FileFlush,_In_ ULONG Cluster,_In_opt_ PCUNICODE_STRING FileName)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 = %u)\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
HvFree(_In_ PHHIVE RegistryHive)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