7aab1797de0621c843dd020a37b96eaa44cb4703
[reactos.git] / sdk / lib / cmlib / hiveinit.c
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_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_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_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: %lx\n", ChunkSize);
235
236 if (ChunkSize < sizeof(HBASE_BLOCK) ||
237 !HvpVerifyHiveHeader(ChunkBase))
238 {
239 DPRINT1("Registry is corrupt: ChunkSize %lu < sizeof(HBASE_BLOCK) %lu, "
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_BIN_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 (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
546 Hive->Allocate = Allocate;
547 Hive->Free = Free;
548 Hive->FileSetSize = FileSetSize;
549 Hive->FileWrite = FileWrite;
550 Hive->FileRead = FileRead;
551 Hive->FileFlush = FileFlush;
552
553 Hive->RefreshCount = 0;
554 Hive->StorageTypeCount = HTYPE_COUNT;
555 Hive->Cluster = Cluster;
556 Hive->BaseBlockAlloc = sizeof(HBASE_BLOCK); // == HBLOCK_SIZE
557
558 Hive->Version = HSYS_MINOR;
559 Hive->Log = (FileType == HFILE_TYPE_LOG);
560 Hive->HiveFlags = HiveFlags & ~HIVE_NOLAZYFLUSH;
561
562 switch (OperationType)
563 {
564 case HINIT_CREATE:
565 Status = HvpCreateHive(Hive, FileName);
566 break;
567
568 case HINIT_MEMORY:
569 Status = HvpInitializeMemoryHive(Hive, HiveData, FileName);
570 break;
571
572 case HINIT_FLAT:
573 Status = HvpInitializeFlatHive(Hive, HiveData);
574 break;
575
576 case HINIT_FILE:
577 {
578 Status = HvLoadHive(Hive, FileName);
579 if ((Status != STATUS_SUCCESS) &&
580 (Status != STATUS_REGISTRY_RECOVERED))
581 {
582 /* Unrecoverable failure */
583 return Status;
584 }
585
586 /* Check for previous damage */
587 ASSERT(Status != STATUS_REGISTRY_RECOVERED);
588 break;
589 }
590
591 case HINIT_MEMORY_INPLACE:
592 // Status = HvpInitializeMemoryInplaceHive(Hive, HiveData);
593 // break;
594
595 case HINIT_MAPFILE:
596
597 default:
598 /* FIXME: A better return status value is needed */
599 Status = STATUS_NOT_IMPLEMENTED;
600 ASSERT(FALSE);
601 }
602
603 if (!NT_SUCCESS(Status)) return Status;
604
605 /* HACK: ROS: Init root key cell and prepare the hive */
606 // r31253
607 // if (OperationType == HINIT_CREATE) CmCreateRootNode(Hive, L"");
608 if (OperationType != HINIT_CREATE) CmPrepareHive(Hive);
609
610 return Status;
611 }
612
613 /**
614 * @name HvFree
615 *
616 * Free all stroage and handles associated with hive descriptor.
617 * But do not free the hive descriptor itself.
618 */
619 VOID CMAPI
620 HvFree(
621 PHHIVE RegistryHive)
622 {
623 if (!RegistryHive->ReadOnly)
624 {
625 /* Release hive bitmap */
626 if (RegistryHive->DirtyVector.Buffer)
627 {
628 RegistryHive->Free(RegistryHive->DirtyVector.Buffer, 0);
629 }
630
631 HvpFreeHiveBins(RegistryHive);
632
633 /* Free the BaseBlock */
634 if (RegistryHive->BaseBlock)
635 {
636 RegistryHive->Free(RegistryHive->BaseBlock, RegistryHive->BaseBlockAlloc);
637 RegistryHive->BaseBlock = NULL;
638 }
639 }
640 }
641
642 /* EOF */