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