Sync with trunk r58033.
[reactos.git] / 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
18 BOOLEAN CMAPI
19 HvpVerifyHiveHeader(
20 PHBASE_BLOCK BaseBlock)
21 {
22 if (BaseBlock->Signature != HV_SIGNATURE ||
23 BaseBlock->Major != HSYS_MAJOR ||
24 BaseBlock->Minor < HSYS_MINOR ||
25 BaseBlock->Type != HFILE_TYPE_PRIMARY ||
26 BaseBlock->Format != HBASE_FORMAT_MEMORY ||
27 BaseBlock->Cluster != 1 ||
28 BaseBlock->Sequence1 != BaseBlock->Sequence2 ||
29 HvpHiveHeaderChecksum(BaseBlock) != BaseBlock->CheckSum)
30 {
31 DPRINT1("Verify Hive Header failed: \n");
32 DPRINT1(" Signature: 0x%x and not 0x%x, Major: 0x%x and not 0x%x\n",
33 BaseBlock->Signature, HV_SIGNATURE, BaseBlock->Major, HSYS_MAJOR);
34 DPRINT1(" Minor: 0x%x is not >= 0x%x, Type: 0x%x and not 0x%x\n",
35 BaseBlock->Minor, HSYS_MINOR, BaseBlock->Type, HFILE_TYPE_PRIMARY);
36 DPRINT1(" Format: 0x%x and not 0x%x, Cluster: 0x%x and not 1\n",
37 BaseBlock->Format, HBASE_FORMAT_MEMORY, BaseBlock->Cluster);
38 DPRINT1(" Sequence: 0x%x and not 0x%x, Checksum: 0x%x and not 0x%x\n",
39 BaseBlock->Sequence1, BaseBlock->Sequence2,
40 HvpHiveHeaderChecksum(BaseBlock), BaseBlock->CheckSum);
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 hive
51 * descriptor.
52 */
53
54 VOID CMAPI
55 HvpFreeHiveBins(
56 PHHIVE Hive)
57 {
58 ULONG i;
59 PHBIN Bin;
60 ULONG Storage;
61
62 for (Storage = Stable; Storage < HTYPE_COUNT; Storage++)
63 {
64 Bin = NULL;
65 for (i = 0; i < Hive->Storage[Storage].Length; i++)
66 {
67 if (Hive->Storage[Storage].BlockList[i].BinAddress == (ULONG_PTR)NULL)
68 continue;
69 if (Hive->Storage[Storage].BlockList[i].BinAddress != (ULONG_PTR)Bin)
70 {
71 Bin = (PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress;
72 Hive->Free((PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress, 0);
73 }
74 Hive->Storage[Storage].BlockList[i].BinAddress = (ULONG_PTR)NULL;
75 Hive->Storage[Storage].BlockList[i].BlockAddress = (ULONG_PTR)NULL;
76 }
77
78 if (Hive->Storage[Storage].Length)
79 Hive->Free(Hive->Storage[Storage].BlockList, 0);
80 }
81 }
82
83 /**
84 * @name HvpCreateHive
85 *
86 * Internal helper function to initalize hive descriptor structure for
87 * newly created hive.
88 *
89 * @see HvInitialize
90 */
91
92 NTSTATUS CMAPI
93 HvpCreateHive(
94 PHHIVE RegistryHive)
95 {
96 PHBASE_BLOCK BaseBlock;
97 ULONG Index;
98
99 BaseBlock = RegistryHive->Allocate(sizeof(HBASE_BLOCK), FALSE, TAG_CM);
100 if (BaseBlock == NULL)
101 return STATUS_NO_MEMORY;
102 RtlZeroMemory(BaseBlock, sizeof(HBASE_BLOCK));
103 BaseBlock->Signature = HV_SIGNATURE;
104 BaseBlock->Major = HSYS_MAJOR;
105 BaseBlock->Minor = HSYS_MINOR;
106 BaseBlock->Type = HFILE_TYPE_PRIMARY;
107 BaseBlock->Format = HBASE_FORMAT_MEMORY;
108 BaseBlock->Cluster = 1;
109 BaseBlock->RootCell = HCELL_NIL;
110 BaseBlock->Length = HV_BLOCK_SIZE;
111 BaseBlock->Sequence1 = 1;
112 BaseBlock->Sequence2 = 1;
113 /* FIXME: Fill in the file name */
114 BaseBlock->CheckSum = HvpHiveHeaderChecksum(BaseBlock);
115
116 RegistryHive->BaseBlock = BaseBlock;
117 for (Index = 0; Index < 24; Index++)
118 {
119 RegistryHive->Storage[Stable].FreeDisplay[Index] = HCELL_NIL;
120 RegistryHive->Storage[Volatile].FreeDisplay[Index] = HCELL_NIL;
121 }
122
123 return STATUS_SUCCESS;
124 }
125
126 /**
127 * @name HvpInitializeMemoryHive
128 *
129 * Internal helper function to initalize hive descriptor structure for
130 * a hive stored in memory. The data of the hive are copied and it is
131 * prepared for read/write access.
132 *
133 * @see HvInitialize
134 */
135
136 NTSTATUS CMAPI
137 HvpInitializeMemoryHive(
138 PHHIVE Hive,
139 PVOID ChunkBase)
140 {
141 SIZE_T BlockIndex;
142 PHBIN Bin, NewBin;
143 ULONG i;
144 ULONG BitmapSize;
145 PULONG BitmapBuffer;
146 SIZE_T ChunkSize;
147
148 //
149 // This hack is similar in magnitude to the US's National Debt
150 //
151 ChunkSize = ((PHBASE_BLOCK)ChunkBase)->Length;
152 ((PHBASE_BLOCK)ChunkBase)->Length = HV_BLOCK_SIZE;
153 DPRINT("ChunkSize: %lx\n", ChunkSize);
154
155 if (ChunkSize < sizeof(HBASE_BLOCK) ||
156 !HvpVerifyHiveHeader((PHBASE_BLOCK)ChunkBase))
157 {
158 DPRINT1("Registry is corrupt: ChunkSize %lu < sizeof(HBASE_BLOCK) %lu, "
159 "or HvpVerifyHiveHeader() failed\n", ChunkSize, (SIZE_T)sizeof(HBASE_BLOCK));
160 return STATUS_REGISTRY_CORRUPT;
161 }
162
163 Hive->BaseBlock = Hive->Allocate(sizeof(HBASE_BLOCK), FALSE, TAG_CM);
164 if (Hive->BaseBlock == NULL)
165 {
166 return STATUS_NO_MEMORY;
167 }
168 RtlCopyMemory(Hive->BaseBlock, ChunkBase, sizeof(HBASE_BLOCK));
169
170 /*
171 * Build a block list from the in-memory chunk and copy the data as
172 * we go.
173 */
174
175 Hive->Storage[Stable].Length = (ULONG)(ChunkSize / HV_BLOCK_SIZE) - 1;
176 Hive->Storage[Stable].BlockList =
177 Hive->Allocate(Hive->Storage[Stable].Length *
178 sizeof(HMAP_ENTRY), FALSE, TAG_CM);
179 if (Hive->Storage[Stable].BlockList == NULL)
180 {
181 DPRINT1("Allocating block list failed\n");
182 Hive->Free(Hive->BaseBlock, 0);
183 return STATUS_NO_MEMORY;
184 }
185
186 for (BlockIndex = 0; BlockIndex < Hive->Storage[Stable].Length; )
187 {
188 Bin = (PHBIN)((ULONG_PTR)ChunkBase + (BlockIndex + 1) * HV_BLOCK_SIZE);
189 if (Bin->Signature != HV_BIN_SIGNATURE ||
190 (Bin->Size % HV_BLOCK_SIZE) != 0)
191 {
192 Hive->Free(Hive->BaseBlock, 0);
193 Hive->Free(Hive->Storage[Stable].BlockList, 0);
194 return STATUS_REGISTRY_CORRUPT;
195 }
196
197 NewBin = Hive->Allocate(Bin->Size, TRUE, TAG_CM);
198 if (NewBin == NULL)
199 {
200 Hive->Free(Hive->BaseBlock, 0);
201 Hive->Free(Hive->Storage[Stable].BlockList, 0);
202 return STATUS_NO_MEMORY;
203 }
204
205 Hive->Storage[Stable].BlockList[BlockIndex].BinAddress = (ULONG_PTR)NewBin;
206 Hive->Storage[Stable].BlockList[BlockIndex].BlockAddress = (ULONG_PTR)NewBin;
207
208 RtlCopyMemory(NewBin, Bin, Bin->Size);
209
210 if (Bin->Size > HV_BLOCK_SIZE)
211 {
212 for (i = 1; i < Bin->Size / HV_BLOCK_SIZE; i++)
213 {
214 Hive->Storage[Stable].BlockList[BlockIndex + i].BinAddress = (ULONG_PTR)NewBin;
215 Hive->Storage[Stable].BlockList[BlockIndex + i].BlockAddress =
216 ((ULONG_PTR)NewBin + (i * HV_BLOCK_SIZE));
217 }
218 }
219
220 BlockIndex += Bin->Size / HV_BLOCK_SIZE;
221 }
222
223 if (HvpCreateHiveFreeCellList(Hive))
224 {
225 HvpFreeHiveBins(Hive);
226 Hive->Free(Hive->BaseBlock, 0);
227 return STATUS_NO_MEMORY;
228 }
229
230 BitmapSize = ROUND_UP(Hive->Storage[Stable].Length,
231 sizeof(ULONG) * 8) / 8;
232 BitmapBuffer = (PULONG)Hive->Allocate(BitmapSize, TRUE, TAG_CM);
233 if (BitmapBuffer == NULL)
234 {
235 HvpFreeHiveBins(Hive);
236 Hive->Free(Hive->BaseBlock, 0);
237 return STATUS_NO_MEMORY;
238 }
239
240 RtlInitializeBitMap(&Hive->DirtyVector, BitmapBuffer, BitmapSize * 8);
241 RtlClearAllBits(&Hive->DirtyVector);
242
243 return STATUS_SUCCESS;
244 }
245
246 /**
247 * @name HvpInitializeMemoryInplaceHive
248 *
249 * Internal helper function to initalize hive descriptor structure for
250 * a hive stored in memory. The in-memory data of the hive are directly
251 * used and it is read-only accessible.
252 *
253 * @see HvInitialize
254 */
255
256 NTSTATUS CMAPI
257 HvpInitializeMemoryInplaceHive(
258 PHHIVE Hive,
259 PVOID ChunkBase)
260 {
261 if (!HvpVerifyHiveHeader((PHBASE_BLOCK)ChunkBase))
262 {
263 return STATUS_REGISTRY_CORRUPT;
264 }
265
266 Hive->BaseBlock = (PHBASE_BLOCK)ChunkBase;
267 Hive->ReadOnly = TRUE;
268 Hive->Flat = TRUE;
269
270 return STATUS_SUCCESS;
271 }
272
273 typedef enum _RESULT
274 {
275 NotHive,
276 Fail,
277 NoMemory,
278 HiveSuccess,
279 RecoverHeader,
280 RecoverData,
281 SelfHeal
282 } RESULT;
283
284 RESULT CMAPI
285 HvpGetHiveHeader(IN PHHIVE Hive,
286 IN PHBASE_BLOCK *HiveBaseBlock,
287 IN PLARGE_INTEGER TimeStamp)
288 {
289 PHBASE_BLOCK BaseBlock;
290 ULONG Alignment;
291 ULONG Result;
292 ULONG Offset = 0;
293 ASSERT(sizeof(HBASE_BLOCK) >= (HV_BLOCK_SIZE * Hive->Cluster));
294
295 /* Assume failure and allocate the buffer */
296 *HiveBaseBlock = 0;
297 BaseBlock = Hive->Allocate(sizeof(HBASE_BLOCK), TRUE, TAG_CM);
298 if (!BaseBlock) return NoMemory;
299
300 /* Check for, and enforce, alignment */
301 Alignment = Hive->Cluster * HV_BLOCK_SIZE -1;
302 if ((ULONG_PTR)BaseBlock & Alignment)
303 {
304 /* Free the old header */
305 Hive->Free(BaseBlock, 0);
306 BaseBlock = Hive->Allocate(PAGE_SIZE, TRUE, TAG_CM);
307 if (!BaseBlock) return NoMemory;
308
309 //BaseBlock->Length = PAGE_SIZE; ??
310 }
311
312 /* Clear it */
313 RtlZeroMemory(BaseBlock, sizeof(HBASE_BLOCK));
314
315 /* Now read it from disk */
316 Result = Hive->FileRead(Hive,
317 HFILE_TYPE_PRIMARY,
318 &Offset,
319 BaseBlock,
320 Hive->Cluster * HV_BLOCK_SIZE);
321
322 /* Couldn't read: assume it's not a hive */
323 if (!Result) return NotHive;
324
325 /* Do validation */
326 if (!HvpVerifyHiveHeader(BaseBlock)) return NotHive;
327
328 /* Return information */
329 *HiveBaseBlock = BaseBlock;
330 *TimeStamp = BaseBlock->TimeStamp;
331 return HiveSuccess;
332 }
333
334 NTSTATUS CMAPI
335 HvLoadHive(IN PHHIVE Hive,
336 IN ULONG FileSize)
337 {
338 PHBASE_BLOCK BaseBlock = NULL;
339 ULONG Result;
340 LARGE_INTEGER TimeStamp;
341 ULONG Offset = 0;
342 PVOID HiveData;
343
344 /* Get the hive header */
345 Result = HvpGetHiveHeader(Hive, &BaseBlock, &TimeStamp);
346 switch (Result)
347 {
348 /* Out of memory */
349 case NoMemory:
350
351 /* Fail */
352 return STATUS_INSUFFICIENT_RESOURCES;
353
354 /* Not a hive */
355 case NotHive:
356
357 /* Fail */
358 return STATUS_NOT_REGISTRY_FILE;
359
360 /* Has recovery data */
361 case RecoverData:
362 case RecoverHeader:
363
364 /* Fail */
365 return STATUS_REGISTRY_CORRUPT;
366 }
367
368 /* Set default boot type */
369 BaseBlock->BootType = 0;
370
371 /* Setup hive data */
372 Hive->BaseBlock = BaseBlock;
373 Hive->Version = Hive->BaseBlock->Minor;
374
375 /* Allocate a buffer large enough to hold the hive */
376 HiveData = Hive->Allocate(FileSize, TRUE, TAG_CM);
377 if (!HiveData) return STATUS_INSUFFICIENT_RESOURCES;
378
379 /* Now read the whole hive */
380 Result = Hive->FileRead(Hive,
381 HFILE_TYPE_PRIMARY,
382 &Offset,
383 HiveData,
384 FileSize);
385 if (!Result) return STATUS_NOT_REGISTRY_FILE;
386
387 /* Apply "US National Debt" hack */
388 ((PHBASE_BLOCK)HiveData)->Length = FileSize;
389
390 /* Free our base block... it's usless in this implementation */
391 Hive->Free(BaseBlock, 0);
392
393 /* Initialize the hive directly from memory */
394 return HvpInitializeMemoryHive(Hive, HiveData);
395 }
396
397 /**
398 * @name HvInitialize
399 *
400 * Allocate a new hive descriptor structure and intialize it.
401 *
402 * @param RegistryHive
403 * Output variable to store pointer to the hive descriptor.
404 * @param Operation
405 * - HV_OPERATION_CREATE_HIVE
406 * Create a new hive for read/write access.
407 * - HV_OPERATION_MEMORY
408 * Load and copy in-memory hive for read/write access. The
409 * pointer to data passed to this routine can be freed after
410 * the function is executed.
411 * - HV_OPERATION_MEMORY_INPLACE
412 * Load an in-memory hive for read-only access. The pointer
413 * to data passed to this routine MUSTN'T be freed until
414 * HvFree is called.
415 * @param ChunkBase
416 * Pointer to hive data.
417 * @param ChunkSize
418 * Size of passed hive data.
419 *
420 * @return
421 * STATUS_NO_MEMORY - A memory allocation failed.
422 * STATUS_REGISTRY_CORRUPT - Registry corruption was detected.
423 * STATUS_SUCCESS
424 *
425 * @see HvFree
426 */
427
428 NTSTATUS CMAPI
429 HvInitialize(
430 PHHIVE RegistryHive,
431 ULONG Operation,
432 ULONG HiveType,
433 ULONG HiveFlags,
434 PVOID HiveData OPTIONAL,
435 PALLOCATE_ROUTINE Allocate,
436 PFREE_ROUTINE Free,
437 PFILE_SET_SIZE_ROUTINE FileSetSize,
438 PFILE_WRITE_ROUTINE FileWrite,
439 PFILE_READ_ROUTINE FileRead,
440 PFILE_FLUSH_ROUTINE FileFlush,
441 ULONG Cluster OPTIONAL,
442 PUNICODE_STRING FileName)
443 {
444 NTSTATUS Status;
445 PHHIVE Hive = RegistryHive;
446
447 UNREFERENCED_PARAMETER(HiveType);
448 UNREFERENCED_PARAMETER(FileName);
449
450 /*
451 * Create a new hive structure that will hold all the maintenance data.
452 */
453
454 RtlZeroMemory(Hive, sizeof(HHIVE));
455
456 Hive->Allocate = Allocate;
457 Hive->Free = Free;
458 Hive->FileRead = FileRead;
459 Hive->FileWrite = FileWrite;
460 Hive->FileSetSize = FileSetSize;
461 Hive->FileFlush = FileFlush;
462 Hive->StorageTypeCount = HTYPE_COUNT;
463 Hive->Cluster = 1;
464 Hive->Version = HSYS_MINOR;
465 Hive->HiveFlags = HiveFlags &~ HIVE_NOLAZYFLUSH;
466
467 switch (Operation)
468 {
469 case HINIT_CREATE:
470 Status = HvpCreateHive(Hive);
471 break;
472
473 case HINIT_MEMORY:
474 Status = HvpInitializeMemoryHive(Hive, HiveData);
475 break;
476
477 case HINIT_FLAT:
478 Status = HvpInitializeMemoryInplaceHive(Hive, HiveData);
479 break;
480
481 case HINIT_FILE:
482
483 /* Hack of doom: Cluster is actually the file size. */
484 Status = HvLoadHive(Hive, Cluster);
485 if ((Status != STATUS_SUCCESS) &&
486 (Status != STATUS_REGISTRY_RECOVERED))
487 {
488 /* Unrecoverable failure */
489 return Status;
490 }
491
492 /* Check for previous damage */
493 if (Status == STATUS_REGISTRY_RECOVERED) ASSERT(FALSE);
494 break;
495
496 default:
497 /* FIXME: A better return status value is needed */
498 Status = STATUS_NOT_IMPLEMENTED;
499 ASSERT(FALSE);
500 }
501
502 if (!NT_SUCCESS(Status))
503 return Status;
504
505 if (Operation != HINIT_CREATE) CmPrepareHive(Hive);
506
507 return Status;
508 }
509
510 /**
511 * @name HvFree
512 *
513 * Free all stroage and handles associated with hive descriptor.
514 */
515
516 VOID CMAPI
517 HvFree(
518 PHHIVE RegistryHive)
519 {
520 if (!RegistryHive->ReadOnly)
521 {
522 /* Release hive bitmap */
523 if (RegistryHive->DirtyVector.Buffer)
524 {
525 RegistryHive->Free(RegistryHive->DirtyVector.Buffer, 0);
526 }
527
528 HvpFreeHiveBins(RegistryHive);
529 }
530
531 RegistryHive->Free(RegistryHive, 0);
532 }
533
534 /* EOF */