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