xref: /reactos/sdk/lib/cmlib/hivewrt.c (revision c5f93c50)
1c2c66affSColin Finck /*
2cc63d8f4SGeorge Bișoc  * PROJECT:     ReactOS Kernel
3cc63d8f4SGeorge Bișoc  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4f3141fb2SGeorge Bișoc  * PURPOSE:     Configuration Manager Library - Registry Syncing & Hive/Log/Alternate Writing
5cc63d8f4SGeorge Bișoc  * COPYRIGHT:   Copyright 2001 - 2005 Eric Kohl
6cc63d8f4SGeorge Bișoc  *              Copyright 2005 Filip Navara <navaraf@reactos.org>
7cc63d8f4SGeorge Bișoc  *              Copyright 2021 Max Korostil
8cc63d8f4SGeorge Bișoc  *              Copyright 2022 George Bișoc <george.bisoc@reactos.org>
9c2c66affSColin Finck  */
10c2c66affSColin Finck 
11c2c66affSColin Finck #include "cmlib.h"
12c2c66affSColin Finck #define NDEBUG
13c2c66affSColin Finck #include <debug.h>
14c2c66affSColin Finck 
15cc63d8f4SGeorge Bișoc /* DECLARATIONS *************************************************************/
16cc63d8f4SGeorge Bișoc 
17cc63d8f4SGeorge Bișoc #if !defined(CMLIB_HOST) && !defined(_BLDR_)
18cc63d8f4SGeorge Bișoc BOOLEAN
19cc63d8f4SGeorge Bișoc NTAPI
20cc63d8f4SGeorge Bișoc IoSetThreadHardErrorMode(
21cc63d8f4SGeorge Bișoc     _In_ BOOLEAN HardErrorEnabled);
22cc63d8f4SGeorge Bișoc #endif
23cc63d8f4SGeorge Bișoc 
24feb67576SHermès Bélusca-Maïto /* GLOBALS ******************************************************************/
25cc63d8f4SGeorge Bișoc 
26cc63d8f4SGeorge Bișoc /* PRIVATE FUNCTIONS ********************************************************/
27cc63d8f4SGeorge Bișoc 
28cc63d8f4SGeorge Bișoc /**
29cc63d8f4SGeorge Bișoc  * @brief
30cc63d8f4SGeorge Bișoc  * Validates the base block header of a primary
31cc63d8f4SGeorge Bișoc  * hive for consistency.
32cc63d8f4SGeorge Bișoc  *
33cc63d8f4SGeorge Bișoc  * @param[in] RegistryHive
34cc63d8f4SGeorge Bișoc  * A pointer to a hive descriptor to look
35cc63d8f4SGeorge Bișoc  * for the header block.
36cc63d8f4SGeorge Bișoc  */
37cc63d8f4SGeorge Bișoc static
38cc63d8f4SGeorge Bișoc VOID
HvpValidateBaseHeader(_In_ PHHIVE RegistryHive)39cc63d8f4SGeorge Bișoc HvpValidateBaseHeader(
40cc63d8f4SGeorge Bișoc     _In_ PHHIVE RegistryHive)
41c2c66affSColin Finck {
42cc63d8f4SGeorge Bișoc     PHBASE_BLOCK BaseBlock;
43cc63d8f4SGeorge Bișoc 
44cc63d8f4SGeorge Bișoc     /*
45cc63d8f4SGeorge Bișoc      * Cache the base block and validate it.
46cc63d8f4SGeorge Bișoc      * Especially...
47cc63d8f4SGeorge Bișoc      *
48cc63d8f4SGeorge Bișoc      * 1. It must must have a valid signature.
49cc63d8f4SGeorge Bișoc      * 2. It must have a valid format.
50cc63d8f4SGeorge Bișoc      * 3. It must be of an adequate major version,
51cc63d8f4SGeorge Bișoc      *    not anything else.
52cc63d8f4SGeorge Bișoc      */
53cc63d8f4SGeorge Bișoc     BaseBlock = RegistryHive->BaseBlock;
54cc63d8f4SGeorge Bișoc     ASSERT(BaseBlock->Signature == HV_HBLOCK_SIGNATURE);
55cc63d8f4SGeorge Bișoc     ASSERT(BaseBlock->Format == HBASE_FORMAT_MEMORY);
56cc63d8f4SGeorge Bișoc     ASSERT(BaseBlock->Major == HSYS_MAJOR);
57cc63d8f4SGeorge Bișoc }
58cc63d8f4SGeorge Bișoc 
59cc63d8f4SGeorge Bișoc /**
60cc63d8f4SGeorge Bișoc  * @unimplemented
61cc63d8f4SGeorge Bișoc  * @brief
62cc63d8f4SGeorge Bișoc  * Writes dirty data in a transacted way to a hive
63cc63d8f4SGeorge Bișoc  * log file during hive syncing operation. Log
64cc63d8f4SGeorge Bișoc  * files are used by the kernel/bootloader to
65cc63d8f4SGeorge Bișoc  * perform recovery operations against a
66cc63d8f4SGeorge Bișoc  * damaged primary hive.
67cc63d8f4SGeorge Bișoc  *
68cc63d8f4SGeorge Bișoc  * @param[in] RegistryHive
69cc63d8f4SGeorge Bișoc  * A pointer to a hive descriptor where the log
70cc63d8f4SGeorge Bișoc  * belongs to and of which we write data into the
71cc63d8f4SGeorge Bișoc  * said log.
72cc63d8f4SGeorge Bișoc  *
73cc63d8f4SGeorge Bișoc  * @return
74cc63d8f4SGeorge Bișoc  * Returns TRUE if log transaction writing has succeeded,
75cc63d8f4SGeorge Bișoc  * FALSE otherwise.
76cc63d8f4SGeorge Bișoc  *
77cc63d8f4SGeorge Bișoc  * @remarks
78cc63d8f4SGeorge Bișoc  * The function is not completely implemented, that is,
79cc63d8f4SGeorge Bișoc  * it lacks the implementation for growing the log file size.
80cc63d8f4SGeorge Bișoc  * See the FIXME comment below for further details.
81cc63d8f4SGeorge Bișoc  */
82cc63d8f4SGeorge Bișoc static
83cc63d8f4SGeorge Bișoc BOOLEAN
84cc63d8f4SGeorge Bișoc CMAPI
HvpWriteLog(_In_ PHHIVE RegistryHive)85cc63d8f4SGeorge Bișoc HvpWriteLog(
86cc63d8f4SGeorge Bișoc     _In_ PHHIVE RegistryHive)
87cc63d8f4SGeorge Bișoc {
88cc63d8f4SGeorge Bișoc     BOOLEAN Success;
89c2c66affSColin Finck     ULONG FileOffset;
90c2c66affSColin Finck     ULONG BlockIndex;
91c2c66affSColin Finck     ULONG LastIndex;
92cc63d8f4SGeorge Bișoc     PVOID Block;
93cc63d8f4SGeorge Bișoc     UINT32 BitmapSize, BufferSize;
94cc63d8f4SGeorge Bișoc     PUCHAR HeaderBuffer, Ptr;
957f80d5eeSJoachim Henze 
96cc63d8f4SGeorge Bișoc     /*
97cc63d8f4SGeorge Bișoc      * The hive log we are going to write data into
98cc63d8f4SGeorge Bișoc      * has to be writable and with a sane storage.
99cc63d8f4SGeorge Bișoc      */
10076f1da56SHermès Bélusca-Maïto     ASSERT(!RegistryHive->ReadOnly);
101c2c66affSColin Finck     ASSERT(RegistryHive->BaseBlock->Length ==
102c2c66affSColin Finck            RegistryHive->Storage[Stable].Length * HBLOCK_SIZE);
103c2c66affSColin Finck 
104cc63d8f4SGeorge Bișoc     /* Validate the base header before we go further */
105cc63d8f4SGeorge Bișoc     HvpValidateBaseHeader(RegistryHive);
106c2c66affSColin Finck 
107cc63d8f4SGeorge Bișoc     /*
10876f1da56SHermès Bélusca-Maïto      * The sequences can diverge during a forced system shutdown
10976f1da56SHermès Bélusca-Maïto      * occurrence, such as during a power failure, a hardware
11076f1da56SHermès Bélusca-Maïto      * failure or during a system crash, and when one of the
11176f1da56SHermès Bélusca-Maïto      * sequences have been modified during writing into the log
11276f1da56SHermès Bélusca-Maïto      * or hive. In such cases the hive needs a repair.
113cc63d8f4SGeorge Bișoc      */
114c2c66affSColin Finck     if (RegistryHive->BaseBlock->Sequence1 !=
115c2c66affSColin Finck         RegistryHive->BaseBlock->Sequence2)
116c2c66affSColin Finck     {
117cc63d8f4SGeorge Bișoc         DPRINT1("The sequences DO NOT MATCH (Sequence1 == 0x%x, Sequence2 == 0x%x)\n",
118cc63d8f4SGeorge Bișoc                 RegistryHive->BaseBlock->Sequence1, RegistryHive->BaseBlock->Sequence2);
119c2c66affSColin Finck         return FALSE;
120c2c66affSColin Finck     }
121c2c66affSColin Finck 
122cc63d8f4SGeorge Bișoc     /*
123cc63d8f4SGeorge Bișoc      * FIXME: We must set a new file size for this log
124cc63d8f4SGeorge Bișoc      * here but ReactOS lacks the necessary code implementation
125cc63d8f4SGeorge Bișoc      * that manages the growing and shrinking of a hive's log
126cc63d8f4SGeorge Bișoc      * size. So for now don't set any new size for the log.
127cc63d8f4SGeorge Bișoc      */
128c2c66affSColin Finck 
129cc63d8f4SGeorge Bișoc     /*
130cc63d8f4SGeorge Bișoc      * Now calculate the bitmap and buffer sizes to hold up our
131cc63d8f4SGeorge Bișoc      * contents in a buffer.
132cc63d8f4SGeorge Bișoc      */
133*c5f93c50STimo Kreuzer     BitmapSize = ROUND_UP(sizeof(ULONG) + RegistryHive->DirtyVector.SizeOfBitMap, HSECTOR_SIZE);
134cc63d8f4SGeorge Bișoc     BufferSize = HV_LOG_HEADER_SIZE + BitmapSize;
135c2c66affSColin Finck 
136cc63d8f4SGeorge Bișoc     /* Now allocate the base header block buffer */
137cc63d8f4SGeorge Bișoc     HeaderBuffer = RegistryHive->Allocate(BufferSize, TRUE, TAG_CM);
138cc63d8f4SGeorge Bișoc     if (!HeaderBuffer)
139c2c66affSColin Finck     {
140cc63d8f4SGeorge Bișoc         DPRINT1("Couldn't allocate buffer for base header block\n");
141c2c66affSColin Finck         return FALSE;
142c2c66affSColin Finck     }
143c2c66affSColin Finck 
144cc63d8f4SGeorge Bișoc     /* Great, now zero out the buffer */
145cc63d8f4SGeorge Bișoc     RtlZeroMemory(HeaderBuffer, BufferSize);
146cc63d8f4SGeorge Bișoc 
147cc63d8f4SGeorge Bișoc     /*
148cc63d8f4SGeorge Bișoc      * Update the base block of this hive and
149cc63d8f4SGeorge Bișoc      * increment the primary sequence number
150cc63d8f4SGeorge Bișoc      * as we are at the half of the work.
151cc63d8f4SGeorge Bișoc      */
152c2c66affSColin Finck     RegistryHive->BaseBlock->Type = HFILE_TYPE_LOG;
153c2c66affSColin Finck     RegistryHive->BaseBlock->Sequence1++;
154cc63d8f4SGeorge Bișoc     RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
155c2c66affSColin Finck 
156cc63d8f4SGeorge Bișoc     /* Copy the base block header */
157cc63d8f4SGeorge Bișoc     RtlCopyMemory(HeaderBuffer, RegistryHive->BaseBlock, HV_LOG_HEADER_SIZE);
158cc63d8f4SGeorge Bișoc     Ptr = HeaderBuffer + HV_LOG_HEADER_SIZE;
159c2c66affSColin Finck 
160cc63d8f4SGeorge Bișoc     /* Copy the dirty vector */
161cc63d8f4SGeorge Bișoc     *((PULONG)Ptr) = HV_LOG_DIRTY_SIGNATURE;
162cc63d8f4SGeorge Bișoc     Ptr += sizeof(HV_LOG_DIRTY_SIGNATURE);
163c2c66affSColin Finck 
164cc63d8f4SGeorge Bișoc     /*
165cc63d8f4SGeorge Bișoc      * FIXME: In ReactOS a vector contains one bit per block
166cc63d8f4SGeorge Bișoc      * whereas in Windows each bit within a vector is per
167cc63d8f4SGeorge Bișoc      * sector. Furthermore, the dirty blocks within a respective
168cc63d8f4SGeorge Bișoc      * hive has to be marked as such in an appropriate function
169cc63d8f4SGeorge Bișoc      * for this purpose (probably HvMarkDirty or similar).
170cc63d8f4SGeorge Bișoc      *
171cc63d8f4SGeorge Bișoc      * For the moment being, mark the relevant dirty blocks
172cc63d8f4SGeorge Bișoc      * here.
173cc63d8f4SGeorge Bișoc      */
174c2c66affSColin Finck     BlockIndex = 0;
175c2c66affSColin Finck     while (BlockIndex < RegistryHive->Storage[Stable].Length)
176c2c66affSColin Finck     {
177cc63d8f4SGeorge Bișoc         /* Check if the block is clean or we're past the last block */
178c2c66affSColin Finck         LastIndex = BlockIndex;
179c2c66affSColin Finck         BlockIndex = RtlFindSetBits(&RegistryHive->DirtyVector, 1, BlockIndex);
180cc63d8f4SGeorge Bișoc         if (BlockIndex == ~HV_CLEAN_BLOCK || BlockIndex < LastIndex)
181c2c66affSColin Finck         {
182c2c66affSColin Finck             break;
183c2c66affSColin Finck         }
184c2c66affSColin Finck 
185cc63d8f4SGeorge Bișoc         /*
186cc63d8f4SGeorge Bișoc          * Mark this block as dirty and go to the next one.
187cc63d8f4SGeorge Bișoc          *
188cc63d8f4SGeorge Bișoc          * FIXME: We should rather use RtlSetBits but that crashes
189cc63d8f4SGeorge Bișoc          * the system with a bugckeck. So for now mark blocks manually
190cc63d8f4SGeorge Bișoc          * by hand.
191cc63d8f4SGeorge Bișoc          */
192cc63d8f4SGeorge Bișoc         Ptr[BlockIndex] = HV_LOG_DIRTY_BLOCK;
193cc63d8f4SGeorge Bișoc         BlockIndex++;
194cc63d8f4SGeorge Bișoc     }
195c2c66affSColin Finck 
196cc63d8f4SGeorge Bișoc     /* Now write the hive header and block bitmap into the log */
197cc63d8f4SGeorge Bișoc     FileOffset = 0;
198c2c66affSColin Finck     Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_LOG,
199cc63d8f4SGeorge Bișoc                                       &FileOffset, HeaderBuffer, BufferSize);
200cc63d8f4SGeorge Bișoc     RegistryHive->Free(HeaderBuffer, 0);
201c2c66affSColin Finck     if (!Success)
202c2c66affSColin Finck     {
203cc63d8f4SGeorge Bișoc         DPRINT1("Failed to write the hive header block to log (primary sequence)\n");
204c2c66affSColin Finck         return FALSE;
205c2c66affSColin Finck     }
206c2c66affSColin Finck 
207cc63d8f4SGeorge Bișoc     /* Now write the actual dirty data to log */
208cc63d8f4SGeorge Bișoc     FileOffset = BufferSize;
209cc63d8f4SGeorge Bișoc     BlockIndex = 0;
210cc63d8f4SGeorge Bișoc     while (BlockIndex < RegistryHive->Storage[Stable].Length)
211cc63d8f4SGeorge Bișoc     {
212cc63d8f4SGeorge Bișoc         /* Check if the block is clean or we're past the last block */
213cc63d8f4SGeorge Bișoc         LastIndex = BlockIndex;
214cc63d8f4SGeorge Bișoc         BlockIndex = RtlFindSetBits(&RegistryHive->DirtyVector, 1, BlockIndex);
215cc63d8f4SGeorge Bișoc         if (BlockIndex == ~HV_CLEAN_BLOCK || BlockIndex < LastIndex)
216cc63d8f4SGeorge Bișoc         {
217cc63d8f4SGeorge Bișoc             break;
218cc63d8f4SGeorge Bișoc         }
219cc63d8f4SGeorge Bișoc 
220cc63d8f4SGeorge Bișoc         /* Get the block */
221cc63d8f4SGeorge Bișoc         Block = (PVOID)RegistryHive->Storage[Stable].BlockList[BlockIndex].BlockAddress;
222cc63d8f4SGeorge Bișoc 
223cc63d8f4SGeorge Bișoc         /* Write it to log */
224cc63d8f4SGeorge Bișoc         Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_LOG,
225cc63d8f4SGeorge Bișoc                                           &FileOffset, Block, HBLOCK_SIZE);
226cc63d8f4SGeorge Bișoc         if (!Success)
227cc63d8f4SGeorge Bișoc         {
228cc63d8f4SGeorge Bișoc             DPRINT1("Failed to write dirty block to log (block 0x%p, block index 0x%x)\n", Block, BlockIndex);
229cc63d8f4SGeorge Bișoc             return FALSE;
230cc63d8f4SGeorge Bișoc         }
231cc63d8f4SGeorge Bișoc 
232cc63d8f4SGeorge Bișoc         /* Grow up the file offset as we go to the next block */
233c2c66affSColin Finck         BlockIndex++;
234c2c66affSColin Finck         FileOffset += HBLOCK_SIZE;
235c2c66affSColin Finck     }
236c2c66affSColin Finck 
237cc63d8f4SGeorge Bișoc     /*
238cc63d8f4SGeorge Bișoc      * We wrote the header and body of log with dirty,
239cc63d8f4SGeorge Bișoc      * data do a flush immediately.
240cc63d8f4SGeorge Bișoc      */
241c2c66affSColin Finck     Success = RegistryHive->FileFlush(RegistryHive, HFILE_TYPE_LOG, NULL, 0);
242c2c66affSColin Finck     if (!Success)
243c2c66affSColin Finck     {
244cc63d8f4SGeorge Bișoc         DPRINT1("Failed to flush the log\n");
245cc63d8f4SGeorge Bișoc         return FALSE;
246c2c66affSColin Finck     }
247c2c66affSColin Finck 
248cc63d8f4SGeorge Bișoc     /*
249cc63d8f4SGeorge Bișoc      * OK, we're now at 80% of the work done.
250cc63d8f4SGeorge Bișoc      * Increment the secondary sequence and flush
251cc63d8f4SGeorge Bișoc      * the log again. We can have a fully successful
252cc63d8f4SGeorge Bișoc      * transacted write of a log if the sequences
253cc63d8f4SGeorge Bișoc      * are synced up properly.
254cc63d8f4SGeorge Bișoc      */
255c2c66affSColin Finck     RegistryHive->BaseBlock->Sequence2++;
256cc63d8f4SGeorge Bișoc     RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
257c2c66affSColin Finck 
258cc63d8f4SGeorge Bișoc     /* Write new stuff into log first */
259c2c66affSColin Finck     FileOffset = 0;
260c2c66affSColin Finck     Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_LOG,
261c2c66affSColin Finck                                       &FileOffset, RegistryHive->BaseBlock,
262c2c66affSColin Finck                                       HV_LOG_HEADER_SIZE);
263c2c66affSColin Finck     if (!Success)
264c2c66affSColin Finck     {
265cc63d8f4SGeorge Bișoc         DPRINT1("Failed to write the log file (secondary sequence)\n");
266c2c66affSColin Finck         return FALSE;
267c2c66affSColin Finck     }
268c2c66affSColin Finck 
269cc63d8f4SGeorge Bișoc     /* Flush it finally */
270c2c66affSColin Finck     Success = RegistryHive->FileFlush(RegistryHive, HFILE_TYPE_LOG, NULL, 0);
271c2c66affSColin Finck     if (!Success)
272c2c66affSColin Finck     {
273cc63d8f4SGeorge Bișoc         DPRINT1("Failed to flush the log\n");
274cc63d8f4SGeorge Bișoc         return FALSE;
275c2c66affSColin Finck     }
2767f80d5eeSJoachim Henze 
277c2c66affSColin Finck     return TRUE;
278c2c66affSColin Finck }
279c2c66affSColin Finck 
280cc63d8f4SGeorge Bișoc /**
281cc63d8f4SGeorge Bișoc  * @brief
282cc63d8f4SGeorge Bișoc  * Writes data (dirty or non) to a primary hive during
283cc63d8f4SGeorge Bișoc  * syncing operation. Hive writing is also performed
284cc63d8f4SGeorge Bișoc  * during a flush occurrence on request by the system.
285cc63d8f4SGeorge Bișoc  *
286cc63d8f4SGeorge Bișoc  * @param[in] RegistryHive
287cc63d8f4SGeorge Bișoc  * A pointer to a hive descriptor where the data is
288cc63d8f4SGeorge Bișoc  * to be written to that hive.
289cc63d8f4SGeorge Bișoc  *
290cc63d8f4SGeorge Bișoc  * @param[in] OnlyDirty
291cc63d8f4SGeorge Bișoc  * If set to TRUE, the function only looks for dirty
292cc63d8f4SGeorge Bișoc  * data to be written to the primary hive, otherwise if
293cc63d8f4SGeorge Bișoc  * it's set to FALSE then the function writes all the data.
294cc63d8f4SGeorge Bișoc  *
295f3141fb2SGeorge Bișoc  * @param[in] FileType
296f3141fb2SGeorge Bișoc  * The file type of a registry hive. This can be HFILE_TYPE_PRIMARY
297f3141fb2SGeorge Bișoc  * or HFILE_TYPE_ALTERNATE.
298f3141fb2SGeorge Bișoc  *
299cc63d8f4SGeorge Bișoc  * @return
300cc63d8f4SGeorge Bișoc  * Returns TRUE if writing to hive has succeeded,
301cc63d8f4SGeorge Bișoc  * FALSE otherwise.
302f3141fb2SGeorge Bișoc  *
303f3141fb2SGeorge Bișoc  * @remarks
304f3141fb2SGeorge Bișoc  * The on-disk header metadata of a hive is already written with type
305f3141fb2SGeorge Bișoc  * of HFILE_TYPE_PRIMARY, regardless of what file type the caller submits,
306f3141fb2SGeorge Bișoc  * as an alternate hive is basically a mirror of the primary hive.
307cc63d8f4SGeorge Bișoc  */
308cc63d8f4SGeorge Bișoc static
309cc63d8f4SGeorge Bișoc BOOLEAN
310cc63d8f4SGeorge Bișoc CMAPI
HvpWriteHive(_In_ PHHIVE RegistryHive,_In_ BOOLEAN OnlyDirty,_In_ ULONG FileType)311c2c66affSColin Finck HvpWriteHive(
312cc63d8f4SGeorge Bișoc     _In_ PHHIVE RegistryHive,
313f3141fb2SGeorge Bișoc     _In_ BOOLEAN OnlyDirty,
314f3141fb2SGeorge Bișoc     _In_ ULONG FileType)
315c2c66affSColin Finck {
316cc63d8f4SGeorge Bișoc     BOOLEAN Success;
317c2c66affSColin Finck     ULONG FileOffset;
318c2c66affSColin Finck     ULONG BlockIndex;
319c2c66affSColin Finck     ULONG LastIndex;
320cc63d8f4SGeorge Bișoc     PVOID Block;
321c2c66affSColin Finck 
32276f1da56SHermès Bélusca-Maïto     ASSERT(!RegistryHive->ReadOnly);
323c2c66affSColin Finck     ASSERT(RegistryHive->BaseBlock->Length ==
324c2c66affSColin Finck            RegistryHive->Storage[Stable].Length * HBLOCK_SIZE);
325cc63d8f4SGeorge Bișoc     ASSERT(RegistryHive->BaseBlock->RootCell != HCELL_NIL);
326c2c66affSColin Finck 
327cc63d8f4SGeorge Bișoc     /* Validate the base header before we go further */
328cc63d8f4SGeorge Bișoc     HvpValidateBaseHeader(RegistryHive);
329c2c66affSColin Finck 
330cc63d8f4SGeorge Bișoc     /*
33176f1da56SHermès Bélusca-Maïto      * The sequences can diverge during a forced system shutdown
33276f1da56SHermès Bélusca-Maïto      * occurrence, such as during a power failure, a hardware
33376f1da56SHermès Bélusca-Maïto      * failure or during a system crash, and when one of the
33476f1da56SHermès Bélusca-Maïto      * sequences have been modified during writing into the log
33576f1da56SHermès Bélusca-Maïto      * or hive. In such cases the hive needs a repair.
336cc63d8f4SGeorge Bișoc      */
337c2c66affSColin Finck     if (RegistryHive->BaseBlock->Sequence1 !=
338c2c66affSColin Finck         RegistryHive->BaseBlock->Sequence2)
339c2c66affSColin Finck     {
340cc63d8f4SGeorge Bișoc         DPRINT1("The sequences DO NOT MATCH (Sequence1 == 0x%x, Sequence2 == 0x%x)\n",
341cc63d8f4SGeorge Bișoc                 RegistryHive->BaseBlock->Sequence1, RegistryHive->BaseBlock->Sequence2);
342c2c66affSColin Finck         return FALSE;
343c2c66affSColin Finck     }
344c2c66affSColin Finck 
345cc63d8f4SGeorge Bișoc     /*
346cc63d8f4SGeorge Bișoc      * Update the primary sequence number and write
347cc63d8f4SGeorge Bișoc      * the base block to hive.
348cc63d8f4SGeorge Bișoc      */
349c2c66affSColin Finck     RegistryHive->BaseBlock->Type = HFILE_TYPE_PRIMARY;
350c2c66affSColin Finck     RegistryHive->BaseBlock->Sequence1++;
351cc63d8f4SGeorge Bișoc     RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
352c2c66affSColin Finck 
353c2c66affSColin Finck     /* Write hive block */
354c2c66affSColin Finck     FileOffset = 0;
355f3141fb2SGeorge Bișoc     Success = RegistryHive->FileWrite(RegistryHive, FileType,
356c2c66affSColin Finck                                       &FileOffset, RegistryHive->BaseBlock,
357c2c66affSColin Finck                                       sizeof(HBASE_BLOCK));
358c2c66affSColin Finck     if (!Success)
359c2c66affSColin Finck     {
360cc63d8f4SGeorge Bișoc         DPRINT1("Failed to write the base block header to primary hive (primary sequence)\n");
361c2c66affSColin Finck         return FALSE;
362c2c66affSColin Finck     }
363c2c66affSColin Finck 
364cc63d8f4SGeorge Bișoc     /* Write the whole primary hive, block by block */
365c2c66affSColin Finck     BlockIndex = 0;
366c2c66affSColin Finck     while (BlockIndex < RegistryHive->Storage[Stable].Length)
367c2c66affSColin Finck     {
368cc63d8f4SGeorge Bișoc         /*
36976f1da56SHermès Bélusca-Maïto          * If we have to synchronize the registry hive we
370cc63d8f4SGeorge Bișoc          * want to look for dirty blocks to reflect the new
371cc63d8f4SGeorge Bișoc          * updates done to the hive. Otherwise just write
372cc63d8f4SGeorge Bișoc          * all the blocks as if we were doing a regular
37376f1da56SHermès Bélusca-Maïto          * hive write.
374cc63d8f4SGeorge Bișoc          */
375c2c66affSColin Finck         if (OnlyDirty)
376c2c66affSColin Finck         {
377cc63d8f4SGeorge Bișoc             /* Check if the block is clean or we're past the last block */
378c2c66affSColin Finck             LastIndex = BlockIndex;
379c2c66affSColin Finck             BlockIndex = RtlFindSetBits(&RegistryHive->DirtyVector, 1, BlockIndex);
380cc63d8f4SGeorge Bișoc             if (BlockIndex == ~HV_CLEAN_BLOCK || BlockIndex < LastIndex)
381c2c66affSColin Finck             {
382c2c66affSColin Finck                 break;
383c2c66affSColin Finck             }
384c2c66affSColin Finck         }
385c2c66affSColin Finck 
386cc63d8f4SGeorge Bișoc         /* Get the block and offset position */
387cc63d8f4SGeorge Bișoc         Block = (PVOID)RegistryHive->Storage[Stable].BlockList[BlockIndex].BlockAddress;
388c2c66affSColin Finck         FileOffset = (BlockIndex + 1) * HBLOCK_SIZE;
389c2c66affSColin Finck 
390cc63d8f4SGeorge Bișoc         /* Now write this block to primary hive file */
391f3141fb2SGeorge Bișoc         Success = RegistryHive->FileWrite(RegistryHive, FileType,
392cc63d8f4SGeorge Bișoc                                           &FileOffset, Block, HBLOCK_SIZE);
393c2c66affSColin Finck         if (!Success)
394c2c66affSColin Finck         {
395cc63d8f4SGeorge Bișoc             DPRINT1("Failed to write hive block to primary hive file (block 0x%p, block index 0x%x)\n",
396cc63d8f4SGeorge Bișoc                     Block, BlockIndex);
397c2c66affSColin Finck             return FALSE;
398c2c66affSColin Finck         }
399c2c66affSColin Finck 
400cc63d8f4SGeorge Bișoc         /* Go to the next block */
401c2c66affSColin Finck         BlockIndex++;
402c2c66affSColin Finck     }
403c2c66affSColin Finck 
404cc63d8f4SGeorge Bișoc     /*
405cc63d8f4SGeorge Bișoc      * We wrote all the hive contents to the file, we
406cc63d8f4SGeorge Bișoc      * must flush the changes to disk now.
407cc63d8f4SGeorge Bișoc      */
408f3141fb2SGeorge Bișoc     Success = RegistryHive->FileFlush(RegistryHive, FileType, NULL, 0);
409c2c66affSColin Finck     if (!Success)
410c2c66affSColin Finck     {
411cc63d8f4SGeorge Bișoc         DPRINT1("Failed to flush the primary hive\n");
412cc63d8f4SGeorge Bișoc         return FALSE;
413c2c66affSColin Finck     }
414c2c66affSColin Finck 
415cc63d8f4SGeorge Bișoc     /*
416cc63d8f4SGeorge Bișoc      * Increment the secondary sequence number and
41776f1da56SHermès Bélusca-Maïto      * update the checksum. A successful hive write
41876f1da56SHermès Bélusca-Maïto      * transaction is when both of sequences are the
41976f1da56SHermès Bélusca-Maïto      * same, indicating the write operation didn't
420cc63d8f4SGeorge Bișoc      * fail.
421cc63d8f4SGeorge Bișoc      */
422c2c66affSColin Finck     RegistryHive->BaseBlock->Sequence2++;
423cc63d8f4SGeorge Bișoc     RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
424c2c66affSColin Finck 
425c2c66affSColin Finck     /* Write hive block */
426c2c66affSColin Finck     FileOffset = 0;
427f3141fb2SGeorge Bișoc     Success = RegistryHive->FileWrite(RegistryHive, FileType,
428c2c66affSColin Finck                                       &FileOffset, RegistryHive->BaseBlock,
429c2c66affSColin Finck                                       sizeof(HBASE_BLOCK));
430c2c66affSColin Finck     if (!Success)
431c2c66affSColin Finck     {
432cc63d8f4SGeorge Bișoc         DPRINT1("Failed to write the base block header to primary hive (secondary sequence)\n");
433c2c66affSColin Finck         return FALSE;
434c2c66affSColin Finck     }
435c2c66affSColin Finck 
436cc63d8f4SGeorge Bișoc     /* Flush the hive immediately */
437f3141fb2SGeorge Bișoc     Success = RegistryHive->FileFlush(RegistryHive, FileType, NULL, 0);
438c2c66affSColin Finck     if (!Success)
439c2c66affSColin Finck     {
440cc63d8f4SGeorge Bișoc         DPRINT1("Failed to flush the primary hive\n");
441c2c66affSColin Finck         return FALSE;
442c2c66affSColin Finck     }
443c2c66affSColin Finck 
444cc63d8f4SGeorge Bișoc     return TRUE;
445cc63d8f4SGeorge Bișoc }
446cc63d8f4SGeorge Bișoc 
447cc63d8f4SGeorge Bișoc /* PUBLIC FUNCTIONS ***********************************************************/
448cc63d8f4SGeorge Bișoc 
449cc63d8f4SGeorge Bișoc /**
450cc63d8f4SGeorge Bișoc  * @brief
451cc63d8f4SGeorge Bișoc  * Synchronizes a registry hive with latest updates
452cc63d8f4SGeorge Bișoc  * from dirty data present in volatile memory, aka RAM.
453cc63d8f4SGeorge Bișoc  * It writes both to hive log and corresponding primary
454cc63d8f4SGeorge Bișoc  * hive. Syncing is done on request by the system during
455cc63d8f4SGeorge Bișoc  * a flush occurrence.
456cc63d8f4SGeorge Bișoc  *
457cc63d8f4SGeorge Bișoc  * @param[in] RegistryHive
458cc63d8f4SGeorge Bișoc  * A pointer to a hive descriptor where syncing is
459cc63d8f4SGeorge Bișoc  * to be performed.
460cc63d8f4SGeorge Bișoc  *
461cc63d8f4SGeorge Bișoc  * @return
462cc63d8f4SGeorge Bișoc  * Returns TRUE if syncing has succeeded, FALSE otherwise.
463cc63d8f4SGeorge Bișoc  */
464cc63d8f4SGeorge Bișoc BOOLEAN
465cc63d8f4SGeorge Bișoc CMAPI
HvSyncHive(_In_ PHHIVE RegistryHive)466cc63d8f4SGeorge Bișoc HvSyncHive(
467cc63d8f4SGeorge Bișoc     _In_ PHHIVE RegistryHive)
468cc63d8f4SGeorge Bișoc {
469cc63d8f4SGeorge Bișoc #if !defined(CMLIB_HOST) && !defined(_BLDR_)
470cc63d8f4SGeorge Bișoc     BOOLEAN HardErrors;
471cc63d8f4SGeorge Bișoc #endif
472cc63d8f4SGeorge Bișoc 
47376f1da56SHermès Bélusca-Maïto     ASSERT(!RegistryHive->ReadOnly);
474cc63d8f4SGeorge Bișoc     ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
475cc63d8f4SGeorge Bișoc 
476feb67576SHermès Bélusca-Maïto     /* Avoid any write operations on volatile hives */
477feb67576SHermès Bélusca-Maïto     if (RegistryHive->HiveFlags & HIVE_VOLATILE)
478feb67576SHermès Bélusca-Maïto     {
479feb67576SHermès Bélusca-Maïto         DPRINT("Hive 0x%p is volatile\n", RegistryHive);
480feb67576SHermès Bélusca-Maïto         return TRUE;
481feb67576SHermès Bélusca-Maïto     }
482feb67576SHermès Bélusca-Maïto 
483cc63d8f4SGeorge Bișoc     /*
484cc63d8f4SGeorge Bișoc      * Check if there's any dirty data in the vector.
485cc63d8f4SGeorge Bișoc      * A space with clean blocks would be pointless for
486cc63d8f4SGeorge Bișoc      * a log because we want to write dirty data in and
487cc63d8f4SGeorge Bișoc      * sync up, not clean data. So just consider our
488cc63d8f4SGeorge Bișoc      * job as done as there's literally nothing to do.
489cc63d8f4SGeorge Bișoc      */
490cc63d8f4SGeorge Bișoc     if (RtlFindSetBits(&RegistryHive->DirtyVector, 1, 0) == ~HV_CLEAN_BLOCK)
491cc63d8f4SGeorge Bișoc     {
492cc63d8f4SGeorge Bișoc         DPRINT("The dirty vector has clean data, nothing to do\n");
493cc63d8f4SGeorge Bișoc         return TRUE;
494cc63d8f4SGeorge Bișoc     }
495cc63d8f4SGeorge Bișoc 
496cc63d8f4SGeorge Bișoc #if !defined(CMLIB_HOST) && !defined(_BLDR_)
497cc63d8f4SGeorge Bișoc     /* Disable hard errors before syncing the hive */
498cc63d8f4SGeorge Bișoc     HardErrors = IoSetThreadHardErrorMode(FALSE);
499cc63d8f4SGeorge Bișoc #endif
500cc63d8f4SGeorge Bișoc 
501cc63d8f4SGeorge Bișoc #if !defined(_BLDR_)
502cc63d8f4SGeorge Bișoc     /* Update hive header modification time */
503cc63d8f4SGeorge Bișoc     KeQuerySystemTime(&RegistryHive->BaseBlock->TimeStamp);
504cc63d8f4SGeorge Bișoc #endif
505cc63d8f4SGeorge Bișoc 
50676f1da56SHermès Bélusca-Maïto     /* Update the hive log file if present */
50776f1da56SHermès Bélusca-Maïto     if (RegistryHive->Log)
508cc63d8f4SGeorge Bișoc     {
509cc63d8f4SGeorge Bișoc         if (!HvpWriteLog(RegistryHive))
510cc63d8f4SGeorge Bișoc         {
511cc63d8f4SGeorge Bișoc             DPRINT1("Failed to write a log whilst syncing the hive\n");
512cc63d8f4SGeorge Bișoc #if !defined(CMLIB_HOST) && !defined(_BLDR_)
513cc63d8f4SGeorge Bișoc             IoSetThreadHardErrorMode(HardErrors);
514cc63d8f4SGeorge Bișoc #endif
515cc63d8f4SGeorge Bișoc             return FALSE;
516cc63d8f4SGeorge Bișoc         }
517cc63d8f4SGeorge Bișoc     }
518cc63d8f4SGeorge Bișoc 
519cc63d8f4SGeorge Bișoc     /* Update the primary hive file */
520f3141fb2SGeorge Bișoc     if (!HvpWriteHive(RegistryHive, TRUE, HFILE_TYPE_PRIMARY))
521c2c66affSColin Finck     {
522cc63d8f4SGeorge Bișoc         DPRINT1("Failed to write the primary hive\n");
523cc63d8f4SGeorge Bișoc #if !defined(CMLIB_HOST) && !defined(_BLDR_)
524cc63d8f4SGeorge Bișoc         IoSetThreadHardErrorMode(HardErrors);
525cc63d8f4SGeorge Bișoc #endif
526c2c66affSColin Finck         return FALSE;
527c2c66affSColin Finck     }
528c2c66affSColin Finck 
529f3141fb2SGeorge Bișoc     /* Update the alternate hive file if present */
53076f1da56SHermès Bélusca-Maïto     if (RegistryHive->Alternate)
531f3141fb2SGeorge Bișoc     {
532f3141fb2SGeorge Bișoc         if (!HvpWriteHive(RegistryHive, TRUE, HFILE_TYPE_ALTERNATE))
533f3141fb2SGeorge Bișoc         {
534f3141fb2SGeorge Bișoc             DPRINT1("Failed to write the alternate hive\n");
535f3141fb2SGeorge Bișoc #if !defined(CMLIB_HOST) && !defined(_BLDR_)
536f3141fb2SGeorge Bișoc             IoSetThreadHardErrorMode(HardErrors);
537f3141fb2SGeorge Bișoc #endif
538f3141fb2SGeorge Bișoc             return FALSE;
539f3141fb2SGeorge Bișoc         }
540f3141fb2SGeorge Bișoc     }
541f3141fb2SGeorge Bișoc 
542c2c66affSColin Finck     /* Clear dirty bitmap. */
543c2c66affSColin Finck     RtlClearAllBits(&RegistryHive->DirtyVector);
544c2c66affSColin Finck     RegistryHive->DirtyCount = 0;
545c2c66affSColin Finck 
546cc63d8f4SGeorge Bișoc #if !defined(CMLIB_HOST) && !defined(_BLDR_)
547cc63d8f4SGeorge Bișoc     IoSetThreadHardErrorMode(HardErrors);
548cc63d8f4SGeorge Bișoc #endif
549c2c66affSColin Finck     return TRUE;
550c2c66affSColin Finck }
551c2c66affSColin Finck 
552cc63d8f4SGeorge Bișoc /**
553cc63d8f4SGeorge Bișoc  * @unimplemented
554cc63d8f4SGeorge Bișoc  * @brief
555cc63d8f4SGeorge Bișoc  * Determines whether a registry hive needs
556cc63d8f4SGeorge Bișoc  * to be shrinked or not based on its overall
557cc63d8f4SGeorge Bișoc  * size of the hive space to avoid unnecessary
558cc63d8f4SGeorge Bișoc  * bloat.
559cc63d8f4SGeorge Bișoc  *
560cc63d8f4SGeorge Bișoc  * @param[in] RegistryHive
561cc63d8f4SGeorge Bișoc  * A pointer to a hive descriptor where hive
562cc63d8f4SGeorge Bișoc  * shrinking is to be determined.
563cc63d8f4SGeorge Bișoc  *
564cc63d8f4SGeorge Bișoc  * @return
565cc63d8f4SGeorge Bișoc  * Returns TRUE if hive shrinking needs to be
566cc63d8f4SGeorge Bișoc  * done, FALSE otherwise.
567cc63d8f4SGeorge Bișoc  */
568c2c66affSColin Finck BOOLEAN
569c2c66affSColin Finck CMAPI
HvHiveWillShrink(_In_ PHHIVE RegistryHive)570cc63d8f4SGeorge Bișoc HvHiveWillShrink(
571cc63d8f4SGeorge Bișoc     _In_ PHHIVE RegistryHive)
572c2c66affSColin Finck {
573c2c66affSColin Finck     /* No shrinking yet */
574db55933bSMark Jansen     UNIMPLEMENTED_ONCE;
575c2c66affSColin Finck     return FALSE;
576c2c66affSColin Finck }
577c2c66affSColin Finck 
578cc63d8f4SGeorge Bișoc /**
579cc63d8f4SGeorge Bișoc  * @brief
580cc63d8f4SGeorge Bișoc  * Writes data to a registry hive. Unlike
581cc63d8f4SGeorge Bișoc  * HvSyncHive, this function just writes
582cc63d8f4SGeorge Bișoc  * the wholy registry data to a primary hive,
583cc63d8f4SGeorge Bișoc  * ignoring if a certain data block is dirty
584cc63d8f4SGeorge Bișoc  * or not.
585cc63d8f4SGeorge Bișoc  *
586cc63d8f4SGeorge Bișoc  * @param[in] RegistryHive
587cc63d8f4SGeorge Bișoc  * A pointer to a hive descriptor where data
588cc63d8f4SGeorge Bișoc  * is be written into.
589cc63d8f4SGeorge Bișoc  *
590cc63d8f4SGeorge Bișoc  * @return
591cc63d8f4SGeorge Bișoc  * Returns TRUE if hive writing has succeeded,
592cc63d8f4SGeorge Bișoc  * FALSE otherwise.
593cc63d8f4SGeorge Bișoc  */
594cc63d8f4SGeorge Bișoc BOOLEAN
595cc63d8f4SGeorge Bișoc CMAPI
HvWriteHive(_In_ PHHIVE RegistryHive)596c2c66affSColin Finck HvWriteHive(
597cc63d8f4SGeorge Bișoc     _In_ PHHIVE RegistryHive)
598c2c66affSColin Finck {
59976f1da56SHermès Bélusca-Maïto     ASSERT(!RegistryHive->ReadOnly);
600cc63d8f4SGeorge Bișoc     ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
601c2c66affSColin Finck 
602cc63d8f4SGeorge Bișoc #if !defined(_BLDR_)
603c2c66affSColin Finck     /* Update hive header modification time */
604c2c66affSColin Finck     KeQuerySystemTime(&RegistryHive->BaseBlock->TimeStamp);
605cc63d8f4SGeorge Bișoc #endif
606c2c66affSColin Finck 
607c2c66affSColin Finck     /* Update hive file */
608f3141fb2SGeorge Bișoc     if (!HvpWriteHive(RegistryHive, FALSE, HFILE_TYPE_PRIMARY))
609c2c66affSColin Finck     {
610cc63d8f4SGeorge Bișoc         DPRINT1("Failed to write the hive\n");
611c2c66affSColin Finck         return FALSE;
612c2c66affSColin Finck     }
613c2c66affSColin Finck 
614c2c66affSColin Finck     return TRUE;
615c2c66affSColin Finck }
616cc63d8f4SGeorge Bișoc 
617f3141fb2SGeorge Bișoc /**
618f3141fb2SGeorge Bișoc  * @brief
619f3141fb2SGeorge Bișoc  * Writes data to an alternate registry hive.
620f3141fb2SGeorge Bișoc  * An alternate hive is usually backed up by a primary
621f3141fb2SGeorge Bișoc  * hive. This function is tipically used to force write
622f3141fb2SGeorge Bișoc  * data into the alternate hive if both hives no longer match.
623f3141fb2SGeorge Bișoc  *
624f3141fb2SGeorge Bișoc  * @param[in] RegistryHive
625f3141fb2SGeorge Bișoc  * A pointer to a hive descriptor where data
626f3141fb2SGeorge Bișoc  * is to be written into.
627f3141fb2SGeorge Bișoc  *
628f3141fb2SGeorge Bișoc  * @return
629f3141fb2SGeorge Bișoc  * Returns TRUE if hive writing has succeeded,
630f3141fb2SGeorge Bișoc  * FALSE otherwise.
631f3141fb2SGeorge Bișoc  */
632f3141fb2SGeorge Bișoc BOOLEAN
633f3141fb2SGeorge Bișoc CMAPI
HvWriteAlternateHive(_In_ PHHIVE RegistryHive)634f3141fb2SGeorge Bișoc HvWriteAlternateHive(
635f3141fb2SGeorge Bișoc     _In_ PHHIVE RegistryHive)
636f3141fb2SGeorge Bișoc {
63776f1da56SHermès Bélusca-Maïto     ASSERT(!RegistryHive->ReadOnly);
638f3141fb2SGeorge Bișoc     ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
63976f1da56SHermès Bélusca-Maïto     ASSERT(RegistryHive->Alternate);
640f3141fb2SGeorge Bișoc 
641f3141fb2SGeorge Bișoc #if !defined(_BLDR_)
642f3141fb2SGeorge Bișoc     /* Update hive header modification time */
643f3141fb2SGeorge Bișoc     KeQuerySystemTime(&RegistryHive->BaseBlock->TimeStamp);
644f3141fb2SGeorge Bișoc #endif
645f3141fb2SGeorge Bișoc 
646f3141fb2SGeorge Bișoc     /* Update hive file */
647f3141fb2SGeorge Bișoc     if (!HvpWriteHive(RegistryHive, FALSE, HFILE_TYPE_ALTERNATE))
648f3141fb2SGeorge Bișoc     {
649f3141fb2SGeorge Bișoc         DPRINT1("Failed to write the alternate hive\n");
650f3141fb2SGeorge Bișoc         return FALSE;
651f3141fb2SGeorge Bișoc     }
652f3141fb2SGeorge Bișoc 
653f3141fb2SGeorge Bișoc     return TRUE;
654f3141fb2SGeorge Bișoc }
655cc63d8f4SGeorge Bișoc 
656cc63d8f4SGeorge Bișoc /**
657cc63d8f4SGeorge Bișoc  * @brief
658cc63d8f4SGeorge Bișoc  * Synchronizes a hive with recovered
659cc63d8f4SGeorge Bișoc  * data during a healing/resuscitation
660cc63d8f4SGeorge Bișoc  * operation of the registry.
661cc63d8f4SGeorge Bișoc  *
662cc63d8f4SGeorge Bișoc  * @param[in] RegistryHive
663cc63d8f4SGeorge Bișoc  * A pointer to a hive descriptor where data
664cc63d8f4SGeorge Bișoc  * syncing is to be done.
665cc63d8f4SGeorge Bișoc  *
666cc63d8f4SGeorge Bișoc  * @return
667cc63d8f4SGeorge Bișoc  * Returns TRUE if hive syncing during recovery
668cc63d8f4SGeorge Bișoc  * succeeded, FALSE otherwise.
669cc63d8f4SGeorge Bișoc  */
670cc63d8f4SGeorge Bișoc BOOLEAN
671cc63d8f4SGeorge Bișoc CMAPI
HvSyncHiveFromRecover(_In_ PHHIVE RegistryHive)672cc63d8f4SGeorge Bișoc HvSyncHiveFromRecover(
673cc63d8f4SGeorge Bișoc     _In_ PHHIVE RegistryHive)
674cc63d8f4SGeorge Bișoc {
67576f1da56SHermès Bélusca-Maïto     ASSERT(!RegistryHive->ReadOnly);
676cc63d8f4SGeorge Bișoc     ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
677cc63d8f4SGeorge Bișoc 
678cc63d8f4SGeorge Bișoc     /* Call the private API call to do the deed for us */
679f3141fb2SGeorge Bișoc     return HvpWriteHive(RegistryHive, TRUE, HFILE_TYPE_PRIMARY);
680cc63d8f4SGeorge Bișoc }
681cc63d8f4SGeorge Bișoc 
682cc63d8f4SGeorge Bișoc /* EOF */
683