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