ReactOS 0.4.15-dev-7953-g1f49173
hivewrt.c
Go to the documentation of this file.
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 Syncing & Hive/Log/Alternate Writing
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/* DECLARATIONS *************************************************************/
16
17#if !defined(CMLIB_HOST) && !defined(_BLDR_)
21 _In_ BOOLEAN HardErrorEnabled);
22#endif
23
24/* GLOBALS ******************************************************************/
25
26/* PRIVATE FUNCTIONS ********************************************************/
27
37static
38VOID
40 _In_ PHHIVE RegistryHive)
41{
42 PHBASE_BLOCK BaseBlock;
43
44 /*
45 * Cache the base block and validate it.
46 * Especially...
47 *
48 * 1. It must must have a valid signature.
49 * 2. It must have a valid format.
50 * 3. It must be of an adequate major version,
51 * not anything else.
52 */
53 BaseBlock = RegistryHive->BaseBlock;
55 ASSERT(BaseBlock->Format == HBASE_FORMAT_MEMORY);
56 ASSERT(BaseBlock->Major == HSYS_MAJOR);
57}
58
82static
86 _In_ PHHIVE RegistryHive)
87{
90 ULONG BlockIndex;
91 ULONG LastIndex;
92 PVOID Block;
93 UINT32 BitmapSize, BufferSize;
94 PUCHAR HeaderBuffer, Ptr;
95
96 /*
97 * The hive log we are going to write data into
98 * has to be writable and with a sane storage.
99 */
100 ASSERT(!RegistryHive->ReadOnly);
101 ASSERT(RegistryHive->BaseBlock->Length ==
102 RegistryHive->Storage[Stable].Length * HBLOCK_SIZE);
103
104 /* Validate the base header before we go further */
105 HvpValidateBaseHeader(RegistryHive);
106
107 /*
108 * The sequences can diverge during a forced system shutdown
109 * occurrence, such as during a power failure, a hardware
110 * failure or during a system crash, and when one of the
111 * sequences have been modified during writing into the log
112 * or hive. In such cases the hive needs a repair.
113 */
114 if (RegistryHive->BaseBlock->Sequence1 !=
115 RegistryHive->BaseBlock->Sequence2)
116 {
117 DPRINT1("The sequences DO NOT MATCH (Sequence1 == 0x%x, Sequence2 == 0x%x)\n",
118 RegistryHive->BaseBlock->Sequence1, RegistryHive->BaseBlock->Sequence2);
119 return FALSE;
120 }
121
122 /*
123 * FIXME: We must set a new file size for this log
124 * here but ReactOS lacks the necessary code implementation
125 * that manages the growing and shrinking of a hive's log
126 * size. So for now don't set any new size for the log.
127 */
128
129 /*
130 * Now calculate the bitmap and buffer sizes to hold up our
131 * contents in a buffer.
132 */
133 BitmapSize = ROUND_UP(sizeof(ULONG) + RegistryHive->DirtyVector.SizeOfBitMap, HSECTOR_SIZE);
134 BufferSize = HV_LOG_HEADER_SIZE + BitmapSize;
135
136 /* Now allocate the base header block buffer */
137 HeaderBuffer = RegistryHive->Allocate(BufferSize, TRUE, TAG_CM);
138 if (!HeaderBuffer)
139 {
140 DPRINT1("Couldn't allocate buffer for base header block\n");
141 return FALSE;
142 }
143
144 /* Great, now zero out the buffer */
145 RtlZeroMemory(HeaderBuffer, BufferSize);
146
147 /*
148 * Update the base block of this hive and
149 * increment the primary sequence number
150 * as we are at the half of the work.
151 */
152 RegistryHive->BaseBlock->Type = HFILE_TYPE_LOG;
153 RegistryHive->BaseBlock->Sequence1++;
154 RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
155
156 /* Copy the base block header */
157 RtlCopyMemory(HeaderBuffer, RegistryHive->BaseBlock, HV_LOG_HEADER_SIZE);
158 Ptr = HeaderBuffer + HV_LOG_HEADER_SIZE;
159
160 /* Copy the dirty vector */
162 Ptr += sizeof(HV_LOG_DIRTY_SIGNATURE);
163
164 /*
165 * FIXME: In ReactOS a vector contains one bit per block
166 * whereas in Windows each bit within a vector is per
167 * sector. Furthermore, the dirty blocks within a respective
168 * hive has to be marked as such in an appropriate function
169 * for this purpose (probably HvMarkDirty or similar).
170 *
171 * For the moment being, mark the relevant dirty blocks
172 * here.
173 */
174 BlockIndex = 0;
175 while (BlockIndex < RegistryHive->Storage[Stable].Length)
176 {
177 /* Check if the block is clean or we're past the last block */
178 LastIndex = BlockIndex;
179 BlockIndex = RtlFindSetBits(&RegistryHive->DirtyVector, 1, BlockIndex);
180 if (BlockIndex == ~HV_CLEAN_BLOCK || BlockIndex < LastIndex)
181 {
182 break;
183 }
184
185 /*
186 * Mark this block as dirty and go to the next one.
187 *
188 * FIXME: We should rather use RtlSetBits but that crashes
189 * the system with a bugckeck. So for now mark blocks manually
190 * by hand.
191 */
192 Ptr[BlockIndex] = HV_LOG_DIRTY_BLOCK;
193 BlockIndex++;
194 }
195
196 /* Now write the hive header and block bitmap into the log */
197 FileOffset = 0;
198 Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_LOG,
199 &FileOffset, HeaderBuffer, BufferSize);
200 RegistryHive->Free(HeaderBuffer, 0);
201 if (!Success)
202 {
203 DPRINT1("Failed to write the hive header block to log (primary sequence)\n");
204 return FALSE;
205 }
206
207 /* Now write the actual dirty data to log */
209 BlockIndex = 0;
210 while (BlockIndex < RegistryHive->Storage[Stable].Length)
211 {
212 /* Check if the block is clean or we're past the last block */
213 LastIndex = BlockIndex;
214 BlockIndex = RtlFindSetBits(&RegistryHive->DirtyVector, 1, BlockIndex);
215 if (BlockIndex == ~HV_CLEAN_BLOCK || BlockIndex < LastIndex)
216 {
217 break;
218 }
219
220 /* Get the block */
221 Block = (PVOID)RegistryHive->Storage[Stable].BlockList[BlockIndex].BlockAddress;
222
223 /* Write it to log */
224 Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_LOG,
225 &FileOffset, Block, HBLOCK_SIZE);
226 if (!Success)
227 {
228 DPRINT1("Failed to write dirty block to log (block 0x%p, block index 0x%x)\n", Block, BlockIndex);
229 return FALSE;
230 }
231
232 /* Grow up the file offset as we go to the next block */
233 BlockIndex++;
235 }
236
237 /*
238 * We wrote the header and body of log with dirty,
239 * data do a flush immediately.
240 */
241 Success = RegistryHive->FileFlush(RegistryHive, HFILE_TYPE_LOG, NULL, 0);
242 if (!Success)
243 {
244 DPRINT1("Failed to flush the log\n");
245 return FALSE;
246 }
247
248 /*
249 * OK, we're now at 80% of the work done.
250 * Increment the secondary sequence and flush
251 * the log again. We can have a fully successful
252 * transacted write of a log if the sequences
253 * are synced up properly.
254 */
255 RegistryHive->BaseBlock->Sequence2++;
256 RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
257
258 /* Write new stuff into log first */
259 FileOffset = 0;
260 Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_LOG,
261 &FileOffset, RegistryHive->BaseBlock,
263 if (!Success)
264 {
265 DPRINT1("Failed to write the log file (secondary sequence)\n");
266 return FALSE;
267 }
268
269 /* Flush it finally */
270 Success = RegistryHive->FileFlush(RegistryHive, HFILE_TYPE_LOG, NULL, 0);
271 if (!Success)
272 {
273 DPRINT1("Failed to flush the log\n");
274 return FALSE;
275 }
276
277 return TRUE;
278}
279
308static
310CMAPI
312 _In_ PHHIVE RegistryHive,
313 _In_ BOOLEAN OnlyDirty,
315{
318 ULONG BlockIndex;
319 ULONG LastIndex;
320 PVOID Block;
321
322 ASSERT(!RegistryHive->ReadOnly);
323 ASSERT(RegistryHive->BaseBlock->Length ==
324 RegistryHive->Storage[Stable].Length * HBLOCK_SIZE);
325 ASSERT(RegistryHive->BaseBlock->RootCell != HCELL_NIL);
326
327 /* Validate the base header before we go further */
328 HvpValidateBaseHeader(RegistryHive);
329
330 /*
331 * The sequences can diverge during a forced system shutdown
332 * occurrence, such as during a power failure, a hardware
333 * failure or during a system crash, and when one of the
334 * sequences have been modified during writing into the log
335 * or hive. In such cases the hive needs a repair.
336 */
337 if (RegistryHive->BaseBlock->Sequence1 !=
338 RegistryHive->BaseBlock->Sequence2)
339 {
340 DPRINT1("The sequences DO NOT MATCH (Sequence1 == 0x%x, Sequence2 == 0x%x)\n",
341 RegistryHive->BaseBlock->Sequence1, RegistryHive->BaseBlock->Sequence2);
342 return FALSE;
343 }
344
345 /*
346 * Update the primary sequence number and write
347 * the base block to hive.
348 */
349 RegistryHive->BaseBlock->Type = HFILE_TYPE_PRIMARY;
350 RegistryHive->BaseBlock->Sequence1++;
351 RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
352
353 /* Write hive block */
354 FileOffset = 0;
355 Success = RegistryHive->FileWrite(RegistryHive, FileType,
356 &FileOffset, RegistryHive->BaseBlock,
357 sizeof(HBASE_BLOCK));
358 if (!Success)
359 {
360 DPRINT1("Failed to write the base block header to primary hive (primary sequence)\n");
361 return FALSE;
362 }
363
364 /* Write the whole primary hive, block by block */
365 BlockIndex = 0;
366 while (BlockIndex < RegistryHive->Storage[Stable].Length)
367 {
368 /*
369 * If we have to synchronize the registry hive we
370 * want to look for dirty blocks to reflect the new
371 * updates done to the hive. Otherwise just write
372 * all the blocks as if we were doing a regular
373 * hive write.
374 */
375 if (OnlyDirty)
376 {
377 /* Check if the block is clean or we're past the last block */
378 LastIndex = BlockIndex;
379 BlockIndex = RtlFindSetBits(&RegistryHive->DirtyVector, 1, BlockIndex);
380 if (BlockIndex == ~HV_CLEAN_BLOCK || BlockIndex < LastIndex)
381 {
382 break;
383 }
384 }
385
386 /* Get the block and offset position */
387 Block = (PVOID)RegistryHive->Storage[Stable].BlockList[BlockIndex].BlockAddress;
388 FileOffset = (BlockIndex + 1) * HBLOCK_SIZE;
389
390 /* Now write this block to primary hive file */
391 Success = RegistryHive->FileWrite(RegistryHive, FileType,
392 &FileOffset, Block, HBLOCK_SIZE);
393 if (!Success)
394 {
395 DPRINT1("Failed to write hive block to primary hive file (block 0x%p, block index 0x%x)\n",
396 Block, BlockIndex);
397 return FALSE;
398 }
399
400 /* Go to the next block */
401 BlockIndex++;
402 }
403
404 /*
405 * We wrote all the hive contents to the file, we
406 * must flush the changes to disk now.
407 */
408 Success = RegistryHive->FileFlush(RegistryHive, FileType, NULL, 0);
409 if (!Success)
410 {
411 DPRINT1("Failed to flush the primary hive\n");
412 return FALSE;
413 }
414
415 /*
416 * Increment the secondary sequence number and
417 * update the checksum. A successful hive write
418 * transaction is when both of sequences are the
419 * same, indicating the write operation didn't
420 * fail.
421 */
422 RegistryHive->BaseBlock->Sequence2++;
423 RegistryHive->BaseBlock->CheckSum = HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
424
425 /* Write hive block */
426 FileOffset = 0;
427 Success = RegistryHive->FileWrite(RegistryHive, FileType,
428 &FileOffset, RegistryHive->BaseBlock,
429 sizeof(HBASE_BLOCK));
430 if (!Success)
431 {
432 DPRINT1("Failed to write the base block header to primary hive (secondary sequence)\n");
433 return FALSE;
434 }
435
436 /* Flush the hive immediately */
437 Success = RegistryHive->FileFlush(RegistryHive, FileType, NULL, 0);
438 if (!Success)
439 {
440 DPRINT1("Failed to flush the primary hive\n");
441 return FALSE;
442 }
443
444 return TRUE;
445}
446
447/* PUBLIC FUNCTIONS ***********************************************************/
448
465CMAPI
467 _In_ PHHIVE RegistryHive)
468{
469#if !defined(CMLIB_HOST) && !defined(_BLDR_)
470 BOOLEAN HardErrors;
471#endif
472
473 ASSERT(!RegistryHive->ReadOnly);
474 ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
475
476 /* Avoid any write operations on volatile hives */
477 if (RegistryHive->HiveFlags & HIVE_VOLATILE)
478 {
479 DPRINT("Hive 0x%p is volatile\n", RegistryHive);
480 return TRUE;
481 }
482
483 /*
484 * Check if there's any dirty data in the vector.
485 * A space with clean blocks would be pointless for
486 * a log because we want to write dirty data in and
487 * sync up, not clean data. So just consider our
488 * job as done as there's literally nothing to do.
489 */
490 if (RtlFindSetBits(&RegistryHive->DirtyVector, 1, 0) == ~HV_CLEAN_BLOCK)
491 {
492 DPRINT("The dirty vector has clean data, nothing to do\n");
493 return TRUE;
494 }
495
496#if !defined(CMLIB_HOST) && !defined(_BLDR_)
497 /* Disable hard errors before syncing the hive */
498 HardErrors = IoSetThreadHardErrorMode(FALSE);
499#endif
500
501#if !defined(_BLDR_)
502 /* Update hive header modification time */
503 KeQuerySystemTime(&RegistryHive->BaseBlock->TimeStamp);
504#endif
505
506 /* Update the hive log file if present */
507 if (RegistryHive->Log)
508 {
509 if (!HvpWriteLog(RegistryHive))
510 {
511 DPRINT1("Failed to write a log whilst syncing the hive\n");
512#if !defined(CMLIB_HOST) && !defined(_BLDR_)
513 IoSetThreadHardErrorMode(HardErrors);
514#endif
515 return FALSE;
516 }
517 }
518
519 /* Update the primary hive file */
520 if (!HvpWriteHive(RegistryHive, TRUE, HFILE_TYPE_PRIMARY))
521 {
522 DPRINT1("Failed to write the primary hive\n");
523#if !defined(CMLIB_HOST) && !defined(_BLDR_)
524 IoSetThreadHardErrorMode(HardErrors);
525#endif
526 return FALSE;
527 }
528
529 /* Update the alternate hive file if present */
530 if (RegistryHive->Alternate)
531 {
532 if (!HvpWriteHive(RegistryHive, TRUE, HFILE_TYPE_ALTERNATE))
533 {
534 DPRINT1("Failed to write the alternate hive\n");
535#if !defined(CMLIB_HOST) && !defined(_BLDR_)
536 IoSetThreadHardErrorMode(HardErrors);
537#endif
538 return FALSE;
539 }
540 }
541
542 /* Clear dirty bitmap. */
543 RtlClearAllBits(&RegistryHive->DirtyVector);
544 RegistryHive->DirtyCount = 0;
545
546#if !defined(CMLIB_HOST) && !defined(_BLDR_)
547 IoSetThreadHardErrorMode(HardErrors);
548#endif
549 return TRUE;
550}
551
569CMAPI
571 _In_ PHHIVE RegistryHive)
572{
573 /* No shrinking yet */
575 return FALSE;
576}
577
595CMAPI
597 _In_ PHHIVE RegistryHive)
598{
599 ASSERT(!RegistryHive->ReadOnly);
600 ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
601
602#if !defined(_BLDR_)
603 /* Update hive header modification time */
604 KeQuerySystemTime(&RegistryHive->BaseBlock->TimeStamp);
605#endif
606
607 /* Update hive file */
608 if (!HvpWriteHive(RegistryHive, FALSE, HFILE_TYPE_PRIMARY))
609 {
610 DPRINT1("Failed to write the hive\n");
611 return FALSE;
612 }
613
614 return TRUE;
615}
616
633CMAPI
635 _In_ PHHIVE RegistryHive)
636{
637 ASSERT(!RegistryHive->ReadOnly);
638 ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
639 ASSERT(RegistryHive->Alternate);
640
641#if !defined(_BLDR_)
642 /* Update hive header modification time */
643 KeQuerySystemTime(&RegistryHive->BaseBlock->TimeStamp);
644#endif
645
646 /* Update hive file */
647 if (!HvpWriteHive(RegistryHive, FALSE, HFILE_TYPE_ALTERNATE))
648 {
649 DPRINT1("Failed to write the alternate hive\n");
650 return FALSE;
651 }
652
653 return TRUE;
654}
655
671CMAPI
673 _In_ PHHIVE RegistryHive)
674{
675 ASSERT(!RegistryHive->ReadOnly);
676 ASSERT(RegistryHive->Signature == HV_HHIVE_SIGNATURE);
677
678 /* Call the private API call to do the deed for us */
679 return HvpWriteHive(RegistryHive, TRUE, HFILE_TYPE_PRIMARY);
680}
681
682/* EOF */
unsigned char BOOLEAN
unsigned int UINT32
#define DPRINT1
Definition: precomp.h:8
_In_ PFCB _In_ LONGLONG FileOffset
Definition: cdprocs.h:160
#define CMAPI
Definition: cfgmgr32.h:41
ULONG CMAPI HvpHiveHeaderChecksum(PHBASE_BLOCK HiveHeader)
Definition: hivesum.c:17
#define TAG_CM
Definition: cmlib.h:212
#define BufferSize
Definition: mmc.h:75
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define KeQuerySystemTime(t)
Definition: env_spec_w32.h:570
@ Success
Definition: eventcreate.c:712
#define ROUND_UP(n, align)
Definition: eventvwr.h:34
_Must_inspect_result_ _In_ PFSRTL_PER_STREAM_CONTEXT Ptr
Definition: fsrtlfuncs.h:898
#define HV_HHIVE_SIGNATURE
Definition: hivedata.h:62
@ Stable
Definition: hivedata.h:127
#define HBLOCK_SIZE
Definition: hivedata.h:42
#define HV_LOG_HEADER_SIZE
Definition: hivedata.h:46
#define HFILE_TYPE_ALTERNATE
Definition: hivedata.h:36
#define HFILE_TYPE_LOG
Definition: hivedata.h:34
#define HV_HBLOCK_SIGNATURE
Definition: hivedata.h:63
#define HIVE_VOLATILE
Definition: hivedata.h:23
#define HFILE_TYPE_PRIMARY
Definition: hivedata.h:33
#define HV_CLEAN_BLOCK
Definition: hivedata.h:51
#define HCELL_NIL
Definition: hivedata.h:110
#define HSYS_MAJOR
Definition: hivedata.h:69
#define HV_LOG_DIRTY_BLOCK
Definition: hivedata.h:56
#define HBASE_FORMAT_MEMORY
Definition: hivedata.h:78
#define HV_LOG_DIRTY_SIGNATURE
Definition: hivedata.h:57
#define HSECTOR_SIZE
Definition: hivedata.h:43
BOOLEAN CMAPI HvSyncHive(_In_ PHHIVE RegistryHive)
Synchronizes a registry hive with latest updates from dirty data present in volatile memory,...
Definition: hivewrt.c:466
static BOOLEAN CMAPI HvpWriteHive(_In_ PHHIVE RegistryHive, _In_ BOOLEAN OnlyDirty, _In_ ULONG FileType)
Writes data (dirty or non) to a primary hive during syncing operation. Hive writing is also performed...
Definition: hivewrt.c:311
BOOLEAN CMAPI HvSyncHiveFromRecover(_In_ PHHIVE RegistryHive)
Synchronizes a hive with recovered data during a healing/resuscitation operation of the registry.
Definition: hivewrt.c:672
BOOLEAN CMAPI HvWriteHive(_In_ PHHIVE RegistryHive)
Writes data to a registry hive. Unlike HvSyncHive, this function just writes the wholy registry data ...
Definition: hivewrt.c:596
BOOLEAN CMAPI HvHiveWillShrink(_In_ PHHIVE RegistryHive)
Determines whether a registry hive needs to be shrinked or not based on its overall size of the hive ...
Definition: hivewrt.c:570
BOOLEAN NTAPI IoSetThreadHardErrorMode(_In_ BOOLEAN HardErrorEnabled)
static BOOLEAN CMAPI HvpWriteLog(_In_ PHHIVE RegistryHive)
Writes dirty data in a transacted way to a hive log file during hive syncing operation....
Definition: hivewrt.c:85
BOOLEAN CMAPI HvWriteAlternateHive(_In_ PHHIVE RegistryHive)
Writes data to an alternate registry hive. An alternate hive is usually backed up by a primary hive....
Definition: hivewrt.c:634
static VOID HvpValidateBaseHeader(_In_ PHHIVE RegistryHive)
Validates the base block header of a primary hive for consistency.
Definition: hivewrt.c:39
NTSYSAPI void WINAPI RtlClearAllBits(PRTL_BITMAP)
NTSYSAPI ULONG WINAPI RtlFindSetBits(PCRTL_BITMAP, ULONG, ULONG)
#define ASSERT(a)
Definition: mode.c:44
static IStorage Storage
Definition: ole2.c:3548
#define _In_
Definition: ms_sal.h:308
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
#define DPRINT
Definition: sndvol32.h:71
ULONG Major
Definition: hivedata.h:154
ULONG Signature
Definition: hivedata.h:144
ULONG Format
Definition: hivedata.h:164
uint32_t * PULONG
Definition: typedefs.h:59
#define UNIMPLEMENTED_ONCE
Definition: typedefs.h:30
#define NTAPI
Definition: typedefs.h:36
void * PVOID
Definition: typedefs.h:50
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
_In_ WDFDEVICE _In_ WDF_SPECIAL_FILE_TYPE FileType
Definition: wdfdevice.h:2741
_In_ WDFMEMORY _Out_opt_ size_t * BufferSize
Definition: wdfmemory.h:254