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
13 * @name HvpVerifyHiveHeader
15 * Internal function to verify that a hive header has valid format.
19 IN PHBASE_BLOCK BaseBlock
)
21 if (BaseBlock
->Signature
!= HV_SIGNATURE
||
22 BaseBlock
->Major
!= HSYS_MAJOR
||
23 BaseBlock
->Minor
< HSYS_MINOR
||
24 BaseBlock
->Type
!= HFILE_TYPE_PRIMARY
||
25 BaseBlock
->Format
!= HBASE_FORMAT_MEMORY
||
26 BaseBlock
->Cluster
!= 1 ||
27 BaseBlock
->Sequence1
!= BaseBlock
->Sequence2
||
28 HvpHiveHeaderChecksum(BaseBlock
) != BaseBlock
->CheckSum
)
30 DPRINT1("Verify Hive Header failed:\n");
31 DPRINT1(" Signature: 0x%x, expected 0x%x; Major: 0x%x, expected 0x%x\n",
32 BaseBlock
->Signature
, HV_SIGNATURE
, BaseBlock
->Major
, HSYS_MAJOR
);
33 DPRINT1(" Minor: 0x%x expected to be >= 0x%x; Type: 0x%x, expected 0x%x\n",
34 BaseBlock
->Minor
, HSYS_MINOR
, BaseBlock
->Type
, HFILE_TYPE_PRIMARY
);
35 DPRINT1(" Format: 0x%x, expected 0x%x; Cluster: 0x%x, expected 1\n",
36 BaseBlock
->Format
, HBASE_FORMAT_MEMORY
, BaseBlock
->Cluster
);
37 DPRINT1(" Sequence: 0x%x, expected 0x%x; Checksum: 0x%x, expected 0x%x\n",
38 BaseBlock
->Sequence1
, BaseBlock
->Sequence2
,
39 HvpHiveHeaderChecksum(BaseBlock
), BaseBlock
->CheckSum
);
48 * @name HvpFreeHiveBins
50 * Internal function to free all bin storage associated with a hive descriptor.
60 for (Storage
= 0; Storage
< Hive
->StorageTypeCount
; Storage
++)
63 for (i
= 0; i
< Hive
->Storage
[Storage
].Length
; i
++)
65 if (Hive
->Storage
[Storage
].BlockList
[i
].BinAddress
== (ULONG_PTR
)NULL
)
67 if (Hive
->Storage
[Storage
].BlockList
[i
].BinAddress
!= (ULONG_PTR
)Bin
)
69 Bin
= (PHBIN
)Hive
->Storage
[Storage
].BlockList
[i
].BinAddress
;
70 Hive
->Free((PHBIN
)Hive
->Storage
[Storage
].BlockList
[i
].BinAddress
, 0);
72 Hive
->Storage
[Storage
].BlockList
[i
].BinAddress
= (ULONG_PTR
)NULL
;
73 Hive
->Storage
[Storage
].BlockList
[i
].BlockAddress
= (ULONG_PTR
)NULL
;
76 if (Hive
->Storage
[Storage
].Length
)
77 Hive
->Free(Hive
->Storage
[Storage
].BlockList
, 0);
82 * @name HvpAllocBaseBlockAligned
84 * Internal helper function to allocate cluster-aligned hive base blocks.
86 static __inline PHBASE_BLOCK
87 HvpAllocBaseBlockAligned(
92 PHBASE_BLOCK BaseBlock
;
95 ASSERT(sizeof(HBASE_BLOCK
) >= (HSECTOR_SIZE
* Hive
->Cluster
));
97 /* Allocate the buffer */
98 BaseBlock
= Hive
->Allocate(Hive
->BaseBlockAlloc
, Paged
, Tag
);
99 if (!BaseBlock
) return NULL
;
101 /* Check for, and enforce, alignment */
102 Alignment
= Hive
->Cluster
* HSECTOR_SIZE
-1;
103 if ((ULONG_PTR
)BaseBlock
& Alignment
)
105 /* Free the old header and reallocate a new one, always paged */
106 Hive
->Free(BaseBlock
, Hive
->BaseBlockAlloc
);
107 BaseBlock
= Hive
->Allocate(PAGE_SIZE
, TRUE
, Tag
);
108 if (!BaseBlock
) return NULL
;
110 Hive
->BaseBlockAlloc
= PAGE_SIZE
;
117 * @name HvpInitFileName
119 * Internal function to initialize the UNICODE NULL-terminated hive file name
120 * member of a hive header by copying the last 31 characters of the file name.
121 * Mainly used for debugging purposes.
125 IN OUT PHBASE_BLOCK BaseBlock
,
126 IN PCUNICODE_STRING FileName OPTIONAL
)
131 /* Always NULL-initialize */
132 RtlZeroMemory(BaseBlock
->FileName
, (HIVE_FILENAME_MAXLEN
+ 1) * sizeof(WCHAR
));
134 /* Copy the 31 last characters of the hive file name if any */
135 if (!FileName
) return;
137 if (FileName
->Length
/ sizeof(WCHAR
) <= HIVE_FILENAME_MAXLEN
)
140 Length
= FileName
->Length
;
144 Offset
= FileName
->Length
/ sizeof(WCHAR
) - HIVE_FILENAME_MAXLEN
;
145 Length
= HIVE_FILENAME_MAXLEN
* sizeof(WCHAR
);
148 RtlCopyMemory(BaseBlock
->FileName
, FileName
->Buffer
+ Offset
, Length
);
152 * @name HvpCreateHive
154 * Internal helper function to initialize a hive descriptor structure
155 * for a newly created hive in memory.
161 IN OUT PHHIVE RegistryHive
,
162 IN PCUNICODE_STRING FileName OPTIONAL
)
164 PHBASE_BLOCK BaseBlock
;
167 /* Allocate the base block */
168 BaseBlock
= HvpAllocBaseBlockAligned(RegistryHive
, FALSE
, TAG_CM
);
169 if (BaseBlock
== NULL
)
170 return STATUS_NO_MEMORY
;
173 RtlZeroMemory(BaseBlock
, RegistryHive
->BaseBlockAlloc
);
175 BaseBlock
->Signature
= HV_SIGNATURE
;
176 BaseBlock
->Major
= HSYS_MAJOR
;
177 BaseBlock
->Minor
= HSYS_MINOR
;
178 BaseBlock
->Type
= HFILE_TYPE_PRIMARY
;
179 BaseBlock
->Format
= HBASE_FORMAT_MEMORY
;
180 BaseBlock
->Cluster
= 1;
181 BaseBlock
->RootCell
= HCELL_NIL
;
182 BaseBlock
->Length
= 0;
183 BaseBlock
->Sequence1
= 1;
184 BaseBlock
->Sequence2
= 1;
185 BaseBlock
->TimeStamp
.QuadPart
= 0ULL;
188 * No need to compute the checksum since
189 * the hive resides only in memory so far.
191 BaseBlock
->CheckSum
= 0;
193 /* Set default boot type */
194 BaseBlock
->BootType
= 0;
196 /* Setup hive data */
197 RegistryHive
->BaseBlock
= BaseBlock
;
198 RegistryHive
->Version
= BaseBlock
->Minor
; // == HSYS_MINOR
200 for (Index
= 0; Index
< 24; Index
++)
202 RegistryHive
->Storage
[Stable
].FreeDisplay
[Index
] = HCELL_NIL
;
203 RegistryHive
->Storage
[Volatile
].FreeDisplay
[Index
] = HCELL_NIL
;
206 HvpInitFileName(BaseBlock
, FileName
);
208 return STATUS_SUCCESS
;
212 * @name HvpInitializeMemoryHive
214 * Internal helper function to initialize hive descriptor structure for
215 * an existing hive stored in memory. The data of the hive is copied
216 * and it is prepared for read/write access.
221 HvpInitializeMemoryHive(
223 PHBASE_BLOCK ChunkBase
,
224 IN PCUNICODE_STRING FileName OPTIONAL
)
233 ChunkSize
= ChunkBase
->Length
;
234 DPRINT("ChunkSize: %zx\n", ChunkSize
);
236 if (ChunkSize
< sizeof(HBASE_BLOCK
) ||
237 !HvpVerifyHiveHeader(ChunkBase
))
239 DPRINT1("Registry is corrupt: ChunkSize 0x%zx < sizeof(HBASE_BLOCK) 0x%zx, "
240 "or HvpVerifyHiveHeader() failed\n", ChunkSize
, sizeof(HBASE_BLOCK
));
241 return STATUS_REGISTRY_CORRUPT
;
244 /* Allocate the base block */
245 Hive
->BaseBlock
= HvpAllocBaseBlockAligned(Hive
, FALSE
, TAG_CM
);
246 if (Hive
->BaseBlock
== NULL
)
247 return STATUS_NO_MEMORY
;
249 RtlCopyMemory(Hive
->BaseBlock
, ChunkBase
, sizeof(HBASE_BLOCK
));
251 /* Setup hive data */
252 Hive
->Version
= ChunkBase
->Minor
;
255 * Build a block list from the in-memory chunk and copy the data as
259 Hive
->Storage
[Stable
].Length
= (ULONG
)(ChunkSize
/ HBLOCK_SIZE
);
260 Hive
->Storage
[Stable
].BlockList
=
261 Hive
->Allocate(Hive
->Storage
[Stable
].Length
*
262 sizeof(HMAP_ENTRY
), FALSE
, TAG_CM
);
263 if (Hive
->Storage
[Stable
].BlockList
== NULL
)
265 DPRINT1("Allocating block list failed\n");
266 Hive
->Free(Hive
->BaseBlock
, Hive
->BaseBlockAlloc
);
267 return STATUS_NO_MEMORY
;
270 for (BlockIndex
= 0; BlockIndex
< Hive
->Storage
[Stable
].Length
; )
272 Bin
= (PHBIN
)((ULONG_PTR
)ChunkBase
+ (BlockIndex
+ 1) * HBLOCK_SIZE
);
273 if (Bin
->Signature
!= HV_BIN_SIGNATURE
||
274 (Bin
->Size
% HBLOCK_SIZE
) != 0)
276 DPRINT1("Invalid bin at BlockIndex %lu, Signature 0x%x, Size 0x%x\n",
277 (unsigned long)BlockIndex
, (unsigned)Bin
->Signature
, (unsigned)Bin
->Size
);
278 Hive
->Free(Hive
->Storage
[Stable
].BlockList
, 0);
279 Hive
->Free(Hive
->BaseBlock
, Hive
->BaseBlockAlloc
);
280 return STATUS_REGISTRY_CORRUPT
;
283 NewBin
= Hive
->Allocate(Bin
->Size
, TRUE
, TAG_CM
);
286 Hive
->Free(Hive
->Storage
[Stable
].BlockList
, 0);
287 Hive
->Free(Hive
->BaseBlock
, Hive
->BaseBlockAlloc
);
288 return STATUS_NO_MEMORY
;
291 Hive
->Storage
[Stable
].BlockList
[BlockIndex
].BinAddress
= (ULONG_PTR
)NewBin
;
292 Hive
->Storage
[Stable
].BlockList
[BlockIndex
].BlockAddress
= (ULONG_PTR
)NewBin
;
294 RtlCopyMemory(NewBin
, Bin
, Bin
->Size
);
296 if (Bin
->Size
> HBLOCK_SIZE
)
298 for (i
= 1; i
< Bin
->Size
/ HBLOCK_SIZE
; i
++)
300 Hive
->Storage
[Stable
].BlockList
[BlockIndex
+ i
].BinAddress
= (ULONG_PTR
)NewBin
;
301 Hive
->Storage
[Stable
].BlockList
[BlockIndex
+ i
].BlockAddress
=
302 ((ULONG_PTR
)NewBin
+ (i
* HBLOCK_SIZE
));
306 BlockIndex
+= Bin
->Size
/ HBLOCK_SIZE
;
309 if (HvpCreateHiveFreeCellList(Hive
))
311 HvpFreeHiveBins(Hive
);
312 Hive
->Free(Hive
->BaseBlock
, Hive
->BaseBlockAlloc
);
313 return STATUS_NO_MEMORY
;
316 BitmapSize
= ROUND_UP(Hive
->Storage
[Stable
].Length
,
317 sizeof(ULONG
) * 8) / 8;
318 BitmapBuffer
= (PULONG
)Hive
->Allocate(BitmapSize
, TRUE
, TAG_CM
);
319 if (BitmapBuffer
== NULL
)
321 HvpFreeHiveBins(Hive
);
322 Hive
->Free(Hive
->BaseBlock
, Hive
->BaseBlockAlloc
);
323 return STATUS_NO_MEMORY
;
326 RtlInitializeBitMap(&Hive
->DirtyVector
, BitmapBuffer
, BitmapSize
* 8);
327 RtlClearAllBits(&Hive
->DirtyVector
);
329 HvpInitFileName(Hive
->BaseBlock
, FileName
);
331 return STATUS_SUCCESS
;
335 * @name HvpInitializeFlatHive
337 * Internal helper function to initialize hive descriptor structure for
338 * a hive stored in memory. The in-memory data of the hive are directly
339 * used and it is read-only accessible.
344 HvpInitializeFlatHive(
346 PHBASE_BLOCK ChunkBase
)
348 if (!HvpVerifyHiveHeader(ChunkBase
))
349 return STATUS_REGISTRY_CORRUPT
;
351 /* Setup hive data */
352 Hive
->BaseBlock
= ChunkBase
;
353 Hive
->Version
= ChunkBase
->Minor
;
355 Hive
->ReadOnly
= TRUE
;
357 Hive
->StorageTypeCount
= 1;
359 /* Set default boot type */
360 ChunkBase
->BootType
= 0;
362 return STATUS_SUCCESS
;
377 HvpGetHiveHeader(IN PHHIVE Hive
,
378 IN PHBASE_BLOCK
*HiveBaseBlock
,
379 IN PLARGE_INTEGER TimeStamp
)
381 PHBASE_BLOCK BaseBlock
;
385 ASSERT(sizeof(HBASE_BLOCK
) >= (HSECTOR_SIZE
* Hive
->Cluster
));
387 /* Assume failure and allocate the base block */
388 *HiveBaseBlock
= NULL
;
389 BaseBlock
= HvpAllocBaseBlockAligned(Hive
, TRUE
, TAG_CM
);
390 if (!BaseBlock
) return NoMemory
;
393 RtlZeroMemory(BaseBlock
, sizeof(HBASE_BLOCK
));
395 /* Now read it from disk */
396 Result
= Hive
->FileRead(Hive
,
400 Hive
->Cluster
* HSECTOR_SIZE
);
402 /* Couldn't read: assume it's not a hive */
403 if (!Result
) return NotHive
;
406 if (!HvpVerifyHiveHeader(BaseBlock
)) return NotHive
;
408 /* Return information */
409 *HiveBaseBlock
= BaseBlock
;
410 *TimeStamp
= BaseBlock
->TimeStamp
;
415 HvLoadHive(IN PHHIVE Hive
,
416 IN PCUNICODE_STRING FileName OPTIONAL
)
419 PHBASE_BLOCK BaseBlock
= NULL
;
421 LARGE_INTEGER TimeStamp
;
426 /* Get the hive header */
427 Result
= HvpGetHiveHeader(Hive
, &BaseBlock
, &TimeStamp
);
434 return STATUS_INSUFFICIENT_RESOURCES
;
440 return STATUS_NOT_REGISTRY_FILE
;
442 /* Has recovery data */
447 return STATUS_REGISTRY_CORRUPT
;
450 /* Set default boot type */
451 BaseBlock
->BootType
= 0;
453 /* Setup hive data */
454 Hive
->BaseBlock
= BaseBlock
;
455 Hive
->Version
= BaseBlock
->Minor
;
457 /* Allocate a buffer large enough to hold the hive */
458 FileSize
= HBLOCK_SIZE
+ BaseBlock
->Length
; // == sizeof(HBASE_BLOCK) + BaseBlock->Length;
459 HiveData
= Hive
->Allocate(FileSize
, TRUE
, TAG_CM
);
462 Hive
->Free(BaseBlock
, Hive
->BaseBlockAlloc
);
463 return STATUS_INSUFFICIENT_RESOURCES
;
466 /* Now read the whole hive */
467 Result
= Hive
->FileRead(Hive
,
474 Hive
->Free(HiveData
, FileSize
);
475 Hive
->Free(BaseBlock
, Hive
->BaseBlockAlloc
);
476 return STATUS_NOT_REGISTRY_FILE
;
480 /* Free our base block... it's usless in this implementation */
481 Hive
->Free(BaseBlock
, Hive
->BaseBlockAlloc
);
483 /* Initialize the hive directly from memory */
484 Status
= HvpInitializeMemoryHive(Hive
, HiveData
, FileName
);
485 if (!NT_SUCCESS(Status
))
486 Hive
->Free(HiveData
, FileSize
);
494 * Allocate a new hive descriptor structure and intialize it.
496 * @param RegistryHive
497 * Output variable to store pointer to the hive descriptor.
498 * @param OperationType
499 * - HV_OPERATION_CREATE_HIVE
500 * Create a new hive for read/write access.
501 * - HV_OPERATION_MEMORY
502 * Load and copy in-memory hive for read/write access. The
503 * pointer to data passed to this routine can be freed after
504 * the function is executed.
505 * - HV_OPERATION_MEMORY_INPLACE
506 * Load an in-memory hive for read-only access. The pointer
507 * to data passed to this routine MUSTN'T be freed until
510 * Pointer to hive data.
512 * Size of passed hive data.
515 * STATUS_NO_MEMORY - A memory allocation failed.
516 * STATUS_REGISTRY_CORRUPT - Registry corruption was detected.
527 PVOID HiveData OPTIONAL
,
528 PALLOCATE_ROUTINE Allocate
,
530 PFILE_SET_SIZE_ROUTINE FileSetSize
,
531 PFILE_WRITE_ROUTINE FileWrite
,
532 PFILE_READ_ROUTINE FileRead
,
533 PFILE_FLUSH_ROUTINE FileFlush
,
534 ULONG Cluster OPTIONAL
,
535 PCUNICODE_STRING FileName OPTIONAL
)
538 PHHIVE Hive
= RegistryHive
;
541 * Create a new hive structure that will hold all the maintenance data.
544 RtlZeroMemory(Hive
, sizeof(HHIVE
));
546 Hive
->Allocate
= Allocate
;
548 Hive
->FileSetSize
= FileSetSize
;
549 Hive
->FileWrite
= FileWrite
;
550 Hive
->FileRead
= FileRead
;
551 Hive
->FileFlush
= FileFlush
;
553 Hive
->RefreshCount
= 0;
554 Hive
->StorageTypeCount
= HTYPE_COUNT
;
555 Hive
->Cluster
= Cluster
;
556 Hive
->BaseBlockAlloc
= sizeof(HBASE_BLOCK
); // == HBLOCK_SIZE
558 Hive
->Version
= HSYS_MINOR
;
559 #if (NTDDI_VERSION < NTDDI_VISTA)
560 Hive
->Log
= (FileType
== HFILE_TYPE_LOG
);
562 Hive
->HiveFlags
= HiveFlags
& ~HIVE_NOLAZYFLUSH
;
564 switch (OperationType
)
567 Status
= HvpCreateHive(Hive
, FileName
);
571 Status
= HvpInitializeMemoryHive(Hive
, HiveData
, FileName
);
575 Status
= HvpInitializeFlatHive(Hive
, HiveData
);
580 Status
= HvLoadHive(Hive
, FileName
);
581 if ((Status
!= STATUS_SUCCESS
) &&
582 (Status
!= STATUS_REGISTRY_RECOVERED
))
584 /* Unrecoverable failure */
588 /* Check for previous damage */
589 ASSERT(Status
!= STATUS_REGISTRY_RECOVERED
);
593 case HINIT_MEMORY_INPLACE
:
594 // Status = HvpInitializeMemoryInplaceHive(Hive, HiveData);
600 /* FIXME: A better return status value is needed */
601 Status
= STATUS_NOT_IMPLEMENTED
;
605 if (!NT_SUCCESS(Status
)) return Status
;
607 /* HACK: ROS: Init root key cell and prepare the hive */
609 // if (OperationType == HINIT_CREATE) CmCreateRootNode(Hive, L"");
610 if (OperationType
!= HINIT_CREATE
) CmPrepareHive(Hive
);
618 * Free all stroage and handles associated with hive descriptor.
619 * But do not free the hive descriptor itself.
625 if (!RegistryHive
->ReadOnly
)
627 /* Release hive bitmap */
628 if (RegistryHive
->DirtyVector
.Buffer
)
630 RegistryHive
->Free(RegistryHive
->DirtyVector
.Buffer
, 0);
633 HvpFreeHiveBins(RegistryHive
);
635 /* Free the BaseBlock */
636 if (RegistryHive
->BaseBlock
)
638 RegistryHive
->Free(RegistryHive
->BaseBlock
, RegistryHive
->BaseBlockAlloc
);
639 RegistryHive
->BaseBlock
= NULL
;