xref: /reactos/sdk/lib/cmlib/hiveinit.c (revision 3c797b31)
1 /*
2  * PROJECT:   Registry manipulation library
3  * LICENSE:   GPL - See COPYING in the top level directory
4  * COPYRIGHT: Copyright 2005 Filip Navara <navaraf@reactos.org>
5  *            Copyright 2001 - 2005 Eric Kohl
6  */
7 
8 #include "cmlib.h"
9 #define NDEBUG
10 #include <debug.h>
11 
12 /**
13  * @name HvpVerifyHiveHeader
14  *
15  * Internal function to verify that a hive header has valid format.
16  */
17 BOOLEAN CMAPI
18 HvpVerifyHiveHeader(
19     IN PHBASE_BLOCK BaseBlock)
20 {
21     if (BaseBlock->Signature != HV_HBLOCK_SIGNATURE ||
22         BaseBlock->Major != HSYS_MAJOR ||
23         BaseBlock->Minor < HSYS_MINOR ||
24         BaseBlock->Type != HFILE_TYPE_PRIMARY ||
25         BaseBlock->Format != HBASE_FORMAT_MEMORY ||
26         BaseBlock->Cluster != 1 ||
27         BaseBlock->Sequence1 != BaseBlock->Sequence2 ||
28         HvpHiveHeaderChecksum(BaseBlock) != BaseBlock->CheckSum)
29     {
30         DPRINT1("Verify Hive Header failed:\n");
31         DPRINT1("    Signature: 0x%x, expected 0x%x; Major: 0x%x, expected 0x%x\n",
32                 BaseBlock->Signature, HV_HBLOCK_SIGNATURE, BaseBlock->Major, HSYS_MAJOR);
33         DPRINT1("    Minor: 0x%x expected to be >= 0x%x; Type: 0x%x, expected 0x%x\n",
34                 BaseBlock->Minor, HSYS_MINOR, BaseBlock->Type, HFILE_TYPE_PRIMARY);
35         DPRINT1("    Format: 0x%x, expected 0x%x; Cluster: 0x%x, expected 1\n",
36                 BaseBlock->Format, HBASE_FORMAT_MEMORY, BaseBlock->Cluster);
37         DPRINT1("    Sequence: 0x%x, expected 0x%x; Checksum: 0x%x, expected 0x%x\n",
38                 BaseBlock->Sequence1, BaseBlock->Sequence2,
39                 HvpHiveHeaderChecksum(BaseBlock), BaseBlock->CheckSum);
40 
41         return FALSE;
42     }
43 
44     return TRUE;
45 }
46 
47 /**
48  * @name HvpFreeHiveBins
49  *
50  * Internal function to free all bin storage associated with a hive descriptor.
51  */
52 VOID CMAPI
53 HvpFreeHiveBins(
54     PHHIVE Hive)
55 {
56     ULONG i;
57     PHBIN Bin;
58     ULONG Storage;
59 
60     for (Storage = 0; Storage < Hive->StorageTypeCount; Storage++)
61     {
62         Bin = NULL;
63         for (i = 0; i < Hive->Storage[Storage].Length; i++)
64         {
65             if (Hive->Storage[Storage].BlockList[i].BinAddress == (ULONG_PTR)NULL)
66                 continue;
67             if (Hive->Storage[Storage].BlockList[i].BinAddress != (ULONG_PTR)Bin)
68             {
69                 Bin = (PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress;
70                 Hive->Free((PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress, 0);
71             }
72             Hive->Storage[Storage].BlockList[i].BinAddress = (ULONG_PTR)NULL;
73             Hive->Storage[Storage].BlockList[i].BlockAddress = (ULONG_PTR)NULL;
74         }
75 
76         if (Hive->Storage[Storage].Length)
77             Hive->Free(Hive->Storage[Storage].BlockList, 0);
78     }
79 }
80 
81 /**
82  * @name HvpAllocBaseBlockAligned
83  *
84  * Internal helper function to allocate cluster-aligned hive base blocks.
85  */
86 static __inline PHBASE_BLOCK
87 HvpAllocBaseBlockAligned(
88     IN PHHIVE Hive,
89     IN BOOLEAN Paged,
90     IN ULONG Tag)
91 {
92     PHBASE_BLOCK BaseBlock;
93     ULONG Alignment;
94 
95     ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
96 
97     /* Allocate the buffer */
98     BaseBlock = Hive->Allocate(Hive->BaseBlockAlloc, Paged, Tag);
99     if (!BaseBlock) return NULL;
100 
101     /* Check for, and enforce, alignment */
102     Alignment = Hive->Cluster * HSECTOR_SIZE -1;
103     if ((ULONG_PTR)BaseBlock & Alignment)
104     {
105         /* Free the old header and reallocate a new one, always paged */
106         Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
107         BaseBlock = Hive->Allocate(PAGE_SIZE, TRUE, Tag);
108         if (!BaseBlock) return NULL;
109 
110         Hive->BaseBlockAlloc = PAGE_SIZE;
111     }
112 
113     return BaseBlock;
114 }
115 
116 /**
117  * @name HvpInitFileName
118  *
119  * Internal function to initialize the UNICODE NULL-terminated hive file name
120  * member of a hive header by copying the last 31 characters of the file name.
121  * Mainly used for debugging purposes.
122  */
123 static VOID
124 HvpInitFileName(
125     IN OUT PHBASE_BLOCK BaseBlock,
126     IN PCUNICODE_STRING FileName OPTIONAL)
127 {
128     ULONG_PTR Offset;
129     SIZE_T    Length;
130 
131     /* Always NULL-initialize */
132     RtlZeroMemory(BaseBlock->FileName, (HIVE_FILENAME_MAXLEN + 1) * sizeof(WCHAR));
133 
134     /* Copy the 31 last characters of the hive file name if any */
135     if (!FileName) return;
136 
137     if (FileName->Length / sizeof(WCHAR) <= HIVE_FILENAME_MAXLEN)
138     {
139         Offset = 0;
140         Length = FileName->Length;
141     }
142     else
143     {
144         Offset = FileName->Length / sizeof(WCHAR) - HIVE_FILENAME_MAXLEN;
145         Length = HIVE_FILENAME_MAXLEN * sizeof(WCHAR);
146     }
147 
148     RtlCopyMemory(BaseBlock->FileName, FileName->Buffer + Offset, Length);
149 }
150 
151 /**
152  * @name HvpCreateHive
153  *
154  * Internal helper function to initialize a hive descriptor structure
155  * for a newly created hive in memory.
156  *
157  * @see HvInitialize
158  */
159 NTSTATUS CMAPI
160 HvpCreateHive(
161     IN OUT PHHIVE RegistryHive,
162     IN PCUNICODE_STRING FileName OPTIONAL)
163 {
164     PHBASE_BLOCK BaseBlock;
165     ULONG Index;
166 
167     /* Allocate the base block */
168     BaseBlock = HvpAllocBaseBlockAligned(RegistryHive, FALSE, TAG_CM);
169     if (BaseBlock == NULL)
170         return STATUS_NO_MEMORY;
171 
172     /* Clear it */
173     RtlZeroMemory(BaseBlock, RegistryHive->BaseBlockAlloc);
174 
175     BaseBlock->Signature = HV_HBLOCK_SIGNATURE;
176     BaseBlock->Major = HSYS_MAJOR;
177     BaseBlock->Minor = HSYS_MINOR;
178     BaseBlock->Type = HFILE_TYPE_PRIMARY;
179     BaseBlock->Format = HBASE_FORMAT_MEMORY;
180     BaseBlock->Cluster = 1;
181     BaseBlock->RootCell = HCELL_NIL;
182     BaseBlock->Length = 0;
183     BaseBlock->Sequence1 = 1;
184     BaseBlock->Sequence2 = 1;
185     BaseBlock->TimeStamp.QuadPart = 0ULL;
186 
187     /*
188      * No need to compute the checksum since
189      * the hive resides only in memory so far.
190      */
191     BaseBlock->CheckSum = 0;
192 
193     /* Set default boot type */
194     BaseBlock->BootType = 0;
195 
196     /* Setup hive data */
197     RegistryHive->BaseBlock = BaseBlock;
198     RegistryHive->Version = BaseBlock->Minor; // == HSYS_MINOR
199 
200     for (Index = 0; Index < 24; Index++)
201     {
202         RegistryHive->Storage[Stable].FreeDisplay[Index] = HCELL_NIL;
203         RegistryHive->Storage[Volatile].FreeDisplay[Index] = HCELL_NIL;
204     }
205 
206     HvpInitFileName(BaseBlock, FileName);
207 
208     return STATUS_SUCCESS;
209 }
210 
211 /**
212  * @name HvpInitializeMemoryHive
213  *
214  * Internal helper function to initialize hive descriptor structure for
215  * an existing hive stored in memory. The data of the hive is copied
216  * and it is prepared for read/write access.
217  *
218  * @see HvInitialize
219  */
220 NTSTATUS CMAPI
221 HvpInitializeMemoryHive(
222     PHHIVE Hive,
223     PHBASE_BLOCK ChunkBase,
224     IN PCUNICODE_STRING FileName OPTIONAL)
225 {
226     SIZE_T BlockIndex;
227     PHBIN Bin, NewBin;
228     ULONG i;
229     ULONG BitmapSize;
230     PULONG BitmapBuffer;
231     SIZE_T ChunkSize;
232 
233     ChunkSize = ChunkBase->Length;
234     DPRINT("ChunkSize: %zx\n", ChunkSize);
235 
236     if (ChunkSize < sizeof(HBASE_BLOCK) ||
237         !HvpVerifyHiveHeader(ChunkBase))
238     {
239         DPRINT1("Registry is corrupt: ChunkSize 0x%zx < sizeof(HBASE_BLOCK) 0x%zx, "
240                 "or HvpVerifyHiveHeader() failed\n", ChunkSize, sizeof(HBASE_BLOCK));
241         return STATUS_REGISTRY_CORRUPT;
242     }
243 
244     /* Allocate the base block */
245     Hive->BaseBlock = HvpAllocBaseBlockAligned(Hive, FALSE, TAG_CM);
246     if (Hive->BaseBlock == NULL)
247         return STATUS_NO_MEMORY;
248 
249     RtlCopyMemory(Hive->BaseBlock, ChunkBase, sizeof(HBASE_BLOCK));
250 
251     /* Setup hive data */
252     Hive->Version = ChunkBase->Minor;
253 
254     /*
255      * Build a block list from the in-memory chunk and copy the data as
256      * we go.
257      */
258 
259     Hive->Storage[Stable].Length = (ULONG)(ChunkSize / HBLOCK_SIZE);
260     Hive->Storage[Stable].BlockList =
261         Hive->Allocate(Hive->Storage[Stable].Length *
262                        sizeof(HMAP_ENTRY), FALSE, TAG_CM);
263     if (Hive->Storage[Stable].BlockList == NULL)
264     {
265         DPRINT1("Allocating block list failed\n");
266         Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
267         return STATUS_NO_MEMORY;
268     }
269 
270     for (BlockIndex = 0; BlockIndex < Hive->Storage[Stable].Length; )
271     {
272         Bin = (PHBIN)((ULONG_PTR)ChunkBase + (BlockIndex + 1) * HBLOCK_SIZE);
273         if (Bin->Signature != HV_HBIN_SIGNATURE ||
274            (Bin->Size % HBLOCK_SIZE) != 0)
275         {
276             DPRINT1("Invalid bin at BlockIndex %lu, Signature 0x%x, Size 0x%x\n",
277                     (unsigned long)BlockIndex, (unsigned)Bin->Signature, (unsigned)Bin->Size);
278             Hive->Free(Hive->Storage[Stable].BlockList, 0);
279             Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
280             return STATUS_REGISTRY_CORRUPT;
281         }
282 
283         NewBin = Hive->Allocate(Bin->Size, TRUE, TAG_CM);
284         if (NewBin == NULL)
285         {
286             Hive->Free(Hive->Storage[Stable].BlockList, 0);
287             Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
288             return STATUS_NO_MEMORY;
289         }
290 
291         Hive->Storage[Stable].BlockList[BlockIndex].BinAddress = (ULONG_PTR)NewBin;
292         Hive->Storage[Stable].BlockList[BlockIndex].BlockAddress = (ULONG_PTR)NewBin;
293 
294         RtlCopyMemory(NewBin, Bin, Bin->Size);
295 
296         if (Bin->Size > HBLOCK_SIZE)
297         {
298             for (i = 1; i < Bin->Size / HBLOCK_SIZE; i++)
299             {
300                 Hive->Storage[Stable].BlockList[BlockIndex + i].BinAddress = (ULONG_PTR)NewBin;
301                 Hive->Storage[Stable].BlockList[BlockIndex + i].BlockAddress =
302                     ((ULONG_PTR)NewBin + (i * HBLOCK_SIZE));
303             }
304         }
305 
306         BlockIndex += Bin->Size / HBLOCK_SIZE;
307     }
308 
309     if (!NT_SUCCESS(HvpCreateHiveFreeCellList(Hive)))
310     {
311         HvpFreeHiveBins(Hive);
312         Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
313         return STATUS_NO_MEMORY;
314     }
315 
316     BitmapSize = ROUND_UP(Hive->Storage[Stable].Length,
317                           sizeof(ULONG) * 8) / 8;
318     BitmapBuffer = (PULONG)Hive->Allocate(BitmapSize, TRUE, TAG_CM);
319     if (BitmapBuffer == NULL)
320     {
321         HvpFreeHiveBins(Hive);
322         Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
323         return STATUS_NO_MEMORY;
324     }
325 
326     RtlInitializeBitMap(&Hive->DirtyVector, BitmapBuffer, BitmapSize * 8);
327     RtlClearAllBits(&Hive->DirtyVector);
328 
329     HvpInitFileName(Hive->BaseBlock, FileName);
330 
331     return STATUS_SUCCESS;
332 }
333 
334 /**
335  * @name HvpInitializeFlatHive
336  *
337  * Internal helper function to initialize hive descriptor structure for
338  * a hive stored in memory. The in-memory data of the hive are directly
339  * used and it is read-only accessible.
340  *
341  * @see HvInitialize
342  */
343 NTSTATUS CMAPI
344 HvpInitializeFlatHive(
345     PHHIVE Hive,
346     PHBASE_BLOCK ChunkBase)
347 {
348     if (!HvpVerifyHiveHeader(ChunkBase))
349         return STATUS_REGISTRY_CORRUPT;
350 
351     /* Setup hive data */
352     Hive->BaseBlock = ChunkBase;
353     Hive->Version = ChunkBase->Minor;
354     Hive->Flat = TRUE;
355     Hive->ReadOnly = TRUE;
356 
357     Hive->StorageTypeCount = 1;
358 
359     /* Set default boot type */
360     ChunkBase->BootType = 0;
361 
362     return STATUS_SUCCESS;
363 }
364 
365 typedef enum _RESULT
366 {
367     NotHive,
368     Fail,
369     NoMemory,
370     HiveSuccess,
371     RecoverHeader,
372     RecoverData,
373     SelfHeal
374 } RESULT;
375 
376 RESULT CMAPI
377 HvpGetHiveHeader(IN PHHIVE Hive,
378                  IN PHBASE_BLOCK *HiveBaseBlock,
379                  IN PLARGE_INTEGER TimeStamp)
380 {
381     PHBASE_BLOCK BaseBlock;
382     ULONG Result;
383     ULONG Offset = 0;
384 
385     ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
386 
387     /* Assume failure and allocate the base block */
388     *HiveBaseBlock = NULL;
389     BaseBlock = HvpAllocBaseBlockAligned(Hive, TRUE, TAG_CM);
390     if (!BaseBlock) return NoMemory;
391 
392     /* Clear it */
393     RtlZeroMemory(BaseBlock, sizeof(HBASE_BLOCK));
394 
395     /* Now read it from disk */
396     Result = Hive->FileRead(Hive,
397                             HFILE_TYPE_PRIMARY,
398                             &Offset,
399                             BaseBlock,
400                             Hive->Cluster * HSECTOR_SIZE);
401 
402     /* Couldn't read: assume it's not a hive */
403     if (!Result) return NotHive;
404 
405     /* Do validation */
406     if (!HvpVerifyHiveHeader(BaseBlock)) return NotHive;
407 
408     /* Return information */
409     *HiveBaseBlock = BaseBlock;
410     *TimeStamp = BaseBlock->TimeStamp;
411     return HiveSuccess;
412 }
413 
414 NTSTATUS CMAPI
415 HvLoadHive(IN PHHIVE Hive,
416            IN PCUNICODE_STRING FileName OPTIONAL)
417 {
418     NTSTATUS Status;
419     PHBASE_BLOCK BaseBlock = NULL;
420     ULONG Result;
421     LARGE_INTEGER TimeStamp;
422     ULONG Offset = 0;
423     PVOID HiveData;
424     ULONG FileSize;
425 
426     /* Get the hive header */
427     Result = HvpGetHiveHeader(Hive, &BaseBlock, &TimeStamp);
428     switch (Result)
429     {
430         /* Out of memory */
431         case NoMemory:
432 
433             /* Fail */
434             return STATUS_INSUFFICIENT_RESOURCES;
435 
436         /* Not a hive */
437         case NotHive:
438 
439             /* Fail */
440             return STATUS_NOT_REGISTRY_FILE;
441 
442         /* Has recovery data */
443         case RecoverData:
444         case RecoverHeader:
445 
446             /* Fail */
447             return STATUS_REGISTRY_CORRUPT;
448     }
449 
450     /* Set default boot type */
451     BaseBlock->BootType = 0;
452 
453     /* Setup hive data */
454     Hive->BaseBlock = BaseBlock;
455     Hive->Version = BaseBlock->Minor;
456 
457     /* Allocate a buffer large enough to hold the hive */
458     FileSize = HBLOCK_SIZE + BaseBlock->Length; // == sizeof(HBASE_BLOCK) + BaseBlock->Length;
459     HiveData = Hive->Allocate(FileSize, TRUE, TAG_CM);
460     if (!HiveData)
461     {
462         Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
463         return STATUS_INSUFFICIENT_RESOURCES;
464     }
465 
466     /* Now read the whole hive */
467     Result = Hive->FileRead(Hive,
468                             HFILE_TYPE_PRIMARY,
469                             &Offset,
470                             HiveData,
471                             FileSize);
472     if (!Result)
473     {
474         Hive->Free(HiveData, FileSize);
475         Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
476         return STATUS_NOT_REGISTRY_FILE;
477     }
478 
479     // This is a HACK!
480     /* Free our base block... it's usless in this implementation */
481     Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
482 
483     /* Initialize the hive directly from memory */
484     Status = HvpInitializeMemoryHive(Hive, HiveData, FileName);
485     if (!NT_SUCCESS(Status))
486         Hive->Free(HiveData, FileSize);
487 
488     return Status;
489 }
490 
491 /**
492  * @name HvInitialize
493  *
494  * Allocate a new hive descriptor structure and intialize it.
495  *
496  * @param RegistryHive
497  *        Output variable to store pointer to the hive descriptor.
498  * @param OperationType
499  *        - HV_OPERATION_CREATE_HIVE
500  *          Create a new hive for read/write access.
501  *        - HV_OPERATION_MEMORY
502  *          Load and copy in-memory hive for read/write access. The
503  *          pointer to data passed to this routine can be freed after
504  *          the function is executed.
505  *        - HV_OPERATION_MEMORY_INPLACE
506  *          Load an in-memory hive for read-only access. The pointer
507  *          to data passed to this routine MUSTN'T be freed until
508  *          HvFree is called.
509  * @param ChunkBase
510  *        Pointer to hive data.
511  * @param ChunkSize
512  *        Size of passed hive data.
513  *
514  * @return
515  *    STATUS_NO_MEMORY - A memory allocation failed.
516  *    STATUS_REGISTRY_CORRUPT - Registry corruption was detected.
517  *    STATUS_SUCCESS
518  *
519  * @see HvFree
520  */
521 NTSTATUS CMAPI
522 HvInitialize(
523     PHHIVE RegistryHive,
524     ULONG OperationType,
525     ULONG HiveFlags,
526     ULONG FileType,
527     PVOID HiveData OPTIONAL,
528     PALLOCATE_ROUTINE Allocate,
529     PFREE_ROUTINE Free,
530     PFILE_SET_SIZE_ROUTINE FileSetSize,
531     PFILE_WRITE_ROUTINE FileWrite,
532     PFILE_READ_ROUTINE FileRead,
533     PFILE_FLUSH_ROUTINE FileFlush,
534     ULONG Cluster OPTIONAL,
535     PCUNICODE_STRING FileName OPTIONAL)
536 {
537     NTSTATUS Status;
538     PHHIVE Hive = RegistryHive;
539 
540     /*
541      * Create a new hive structure that will hold all the maintenance data.
542      */
543 
544     RtlZeroMemory(Hive, sizeof(HHIVE));
545     Hive->Signature = HV_HHIVE_SIGNATURE;
546 
547     Hive->Allocate = Allocate;
548     Hive->Free = Free;
549     Hive->FileSetSize = FileSetSize;
550     Hive->FileWrite = FileWrite;
551     Hive->FileRead = FileRead;
552     Hive->FileFlush = FileFlush;
553 
554     Hive->RefreshCount = 0;
555     Hive->StorageTypeCount = HTYPE_COUNT;
556     Hive->Cluster = Cluster;
557     Hive->BaseBlockAlloc = sizeof(HBASE_BLOCK); // == HBLOCK_SIZE
558 
559     Hive->Version = HSYS_MINOR;
560 #if (NTDDI_VERSION < NTDDI_VISTA)
561     Hive->Log = (FileType == HFILE_TYPE_LOG);
562 #endif
563     Hive->HiveFlags = HiveFlags & ~HIVE_NOLAZYFLUSH;
564 
565     // TODO: The CellRoutines point to different callbacks
566     // depending on the OperationType.
567     Hive->GetCellRoutine = HvpGetCellData;
568     Hive->ReleaseCellRoutine = NULL;
569 
570     switch (OperationType)
571     {
572         case HINIT_CREATE:
573             Status = HvpCreateHive(Hive, FileName);
574             break;
575 
576         case HINIT_MEMORY:
577             Status = HvpInitializeMemoryHive(Hive, HiveData, FileName);
578             break;
579 
580         case HINIT_FLAT:
581             Status = HvpInitializeFlatHive(Hive, HiveData);
582             break;
583 
584         case HINIT_FILE:
585         {
586             Status = HvLoadHive(Hive, FileName);
587             if ((Status != STATUS_SUCCESS) &&
588                 (Status != STATUS_REGISTRY_RECOVERED))
589             {
590                 /* Unrecoverable failure */
591                 return Status;
592             }
593 
594             /* Check for previous damage */
595             ASSERT(Status != STATUS_REGISTRY_RECOVERED);
596             break;
597         }
598 
599         case HINIT_MEMORY_INPLACE:
600             // Status = HvpInitializeMemoryInplaceHive(Hive, HiveData);
601             // break;
602 
603         case HINIT_MAPFILE:
604 
605         default:
606         /* FIXME: A better return status value is needed */
607         Status = STATUS_NOT_IMPLEMENTED;
608         ASSERT(FALSE);
609     }
610 
611     if (!NT_SUCCESS(Status)) return Status;
612 
613     /* HACK: ROS: Init root key cell and prepare the hive */
614     // r31253
615     // if (OperationType == HINIT_CREATE) CmCreateRootNode(Hive, L"");
616     if (OperationType != HINIT_CREATE) CmPrepareHive(Hive);
617 
618     return Status;
619 }
620 
621 /**
622  * @name HvFree
623  *
624  * Free all stroage and handles associated with hive descriptor.
625  * But do not free the hive descriptor itself.
626  */
627 VOID CMAPI
628 HvFree(
629     PHHIVE RegistryHive)
630 {
631     if (!RegistryHive->ReadOnly)
632     {
633         /* Release hive bitmap */
634         if (RegistryHive->DirtyVector.Buffer)
635         {
636             RegistryHive->Free(RegistryHive->DirtyVector.Buffer, 0);
637         }
638 
639         HvpFreeHiveBins(RegistryHive);
640 
641         /* Free the BaseBlock */
642         if (RegistryHive->BaseBlock)
643         {
644             RegistryHive->Free(RegistryHive->BaseBlock, RegistryHive->BaseBlockAlloc);
645             RegistryHive->BaseBlock = NULL;
646         }
647     }
648 }
649 
650 /* EOF */
651