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