reactos/sdk/lib/cmlib/hiveinit.c
Hermès Bélusca-Maïto ee5338ff13
[CMLIB] Fix GCC13 print formatting '%lu' warning (#7408)
CORE-19724

sdk/lib/cmlib/cmcheck.c: Print the HCELL_INDEX indices in hexadecimal.

------------------

sdk/lib/cmlib/cmcheck.c:321:60: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'ULONG' {aka 'unsigned int'} [-Wformat=]

and at lines 341, 357, 374, 554, 579, 733, 760, 801, 984, 1003, 1458,
1476, 1521, 1551, 1670

sdk/lib/cmlib/cmcheck.c:1251:65: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'HCELL_INDEX' {aka 'unsigned int'} [-Wformat=]

and at lines 1282, 1303, 1551

sdk/lib/cmlib/cmcheck.c:1326:79: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'LONG' {aka 'int'} [-Wformat=]

sdk/lib/cmlib/cmcheck.c:1684:76: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'CM_CHECK_REGISTRY_STATUS' {aka 'unsigned int'} [-Wformat=]

and at line 1711

------------------

sdk/lib/cmlib/cmheal.c:223:108: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'ULONG' {aka 'unsigned int'} [-Wformat=]

and at lines 240, 319, 335

sdk/lib/cmlib/cmheal.c:480:79: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'int' [-Wformat=]

------------------

sdk/lib/cmlib/hiveinit.c:976:62: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'ULONG' {aka 'unsigned int'} [-Wformat=]

and at lines 988, 1492
2024-10-08 17:48:03 +02:00

1535 lines
49 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Configuration Manager Library - Registry Hive Loading & Initialization
* COPYRIGHT: Copyright 2001 - 2005 Eric Kohl
* Copyright 2005 Filip Navara <navaraf@reactos.org>
* Copyright 2021 Max Korostil
* Copyright 2022 George Bișoc <george.bisoc@reactos.org>
*/
#include "cmlib.h"
#define NDEBUG
#include <debug.h>
/* ENUMERATIONS *************************************************************/
typedef enum _RESULT
{
NotHive,
Fail,
NoMemory,
HiveSuccess,
RecoverHeader,
RecoverData,
SelfHeal
} RESULT;
/* PRIVATE FUNCTIONS ********************************************************/
/**
* @brief
* Validates the base block header of a registry
* file (hive or log).
*
* @param[in] BaseBlock
* A pointer to a base block header to
* be validated.
*
* @param[in] FileType
* The file type of a registry file to check
* against the file type of the base block.
*
* @return
* Returns TRUE if the base block header is valid,
* FALSE otherwise.
*/
BOOLEAN
CMAPI
HvpVerifyHiveHeader(
_In_ PHBASE_BLOCK BaseBlock,
_In_ ULONG FileType)
{
if (BaseBlock->Signature != HV_HBLOCK_SIGNATURE ||
BaseBlock->Major != HSYS_MAJOR ||
BaseBlock->Minor < HSYS_MINOR ||
BaseBlock->Type != FileType ||
BaseBlock->Format != HBASE_FORMAT_MEMORY ||
BaseBlock->Cluster != 1 ||
BaseBlock->Sequence1 != BaseBlock->Sequence2 ||
HvpHiveHeaderChecksum(BaseBlock) != BaseBlock->CheckSum)
{
DPRINT1("Verify Hive Header failed:\n");
DPRINT1(" Signature: 0x%x, expected 0x%x; Major: 0x%x, expected 0x%x\n",
BaseBlock->Signature, HV_HBLOCK_SIGNATURE, BaseBlock->Major, HSYS_MAJOR);
DPRINT1(" Minor: 0x%x expected to be >= 0x%x; Type: 0x%x, expected 0x%x\n",
BaseBlock->Minor, HSYS_MINOR, BaseBlock->Type, FileType);
DPRINT1(" Format: 0x%x, expected 0x%x; Cluster: 0x%x, expected 1\n",
BaseBlock->Format, HBASE_FORMAT_MEMORY, BaseBlock->Cluster);
DPRINT1(" Sequence: 0x%x, expected 0x%x; Checksum: 0x%x, expected 0x%x\n",
BaseBlock->Sequence1, BaseBlock->Sequence2,
HvpHiveHeaderChecksum(BaseBlock), BaseBlock->CheckSum);
return FALSE;
}
return TRUE;
}
/**
* @brief
* Frees all the bins within storage space
* associated with a hive descriptor.
*
* @param[in] Hive
* A pointer to a hive descriptor where
* all the bins are to be freed.
*/
VOID
CMAPI
HvpFreeHiveBins(
_In_ PHHIVE Hive)
{
ULONG i;
PHBIN Bin;
ULONG Storage;
for (Storage = 0; Storage < Hive->StorageTypeCount; Storage++)
{
Bin = NULL;
for (i = 0; i < Hive->Storage[Storage].Length; i++)
{
if (Hive->Storage[Storage].BlockList[i].BinAddress == (ULONG_PTR)NULL)
continue;
if (Hive->Storage[Storage].BlockList[i].BinAddress != (ULONG_PTR)Bin)
{
Bin = (PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress;
Hive->Free((PHBIN)Hive->Storage[Storage].BlockList[i].BinAddress, 0);
}
Hive->Storage[Storage].BlockList[i].BinAddress = (ULONG_PTR)NULL;
Hive->Storage[Storage].BlockList[i].BlockAddress = (ULONG_PTR)NULL;
}
if (Hive->Storage[Storage].Length)
Hive->Free(Hive->Storage[Storage].BlockList, 0);
}
}
/**
* @brief
* Allocates a cluster-aligned hive base header block.
*
* @param[in] Hive
* A pointer to a hive descriptor where
* the header block allocator function is to
* be gathered from.
*
* @param[in] Paged
* If set to TRUE, the allocated base block will reside
* in paged pool, otherwise it will reside in non paged
* pool.
*
* @param[in] Tag
* A tag name to supply for the allocated memory block
* for identification. This is for debugging purposes.
*
* @return
* Returns an allocated base block header if the function
* succeeds, otherwise it returns NULL.
*/
static
__inline
PHBASE_BLOCK
HvpAllocBaseBlockAligned(
_In_ PHHIVE Hive,
_In_ BOOLEAN Paged,
_In_ ULONG Tag)
{
PHBASE_BLOCK BaseBlock;
ULONG Alignment;
ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
/* Allocate the buffer */
BaseBlock = Hive->Allocate(Hive->BaseBlockAlloc, Paged, Tag);
if (!BaseBlock) return NULL;
/* Check for, and enforce, alignment */
Alignment = Hive->Cluster * HSECTOR_SIZE -1;
if ((ULONG_PTR)BaseBlock & Alignment)
{
/* Free the old header and reallocate a new one, always paged */
Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
BaseBlock = Hive->Allocate(PAGE_SIZE, TRUE, Tag);
if (!BaseBlock) return NULL;
Hive->BaseBlockAlloc = PAGE_SIZE;
}
return BaseBlock;
}
/**
* @brief
* Initializes a NULL-terminated Unicode hive file name
* of a hive header by copying the last 31 characters of
* the hive file name. Mainly used for debugging purposes.
*
* @param[in,out] BaseBlock
* A pointer to a base block header where the hive
* file name is to be copied to.
*
* @param[in] FileName
* A pointer to a Unicode string structure containing
* the hive file name to be copied from. If this argument
* is NULL, the base block will not have any hive file name.
*/
static
VOID
HvpInitFileName(
_Inout_ PHBASE_BLOCK BaseBlock,
_In_opt_ PCUNICODE_STRING FileName)
{
ULONG_PTR Offset;
SIZE_T Length;
/* Always NULL-initialize */
RtlZeroMemory(BaseBlock->FileName, (HIVE_FILENAME_MAXLEN + 1) * sizeof(WCHAR));
/* Copy the 31 last characters of the hive file name if any */
if (!FileName) return;
if (FileName->Length / sizeof(WCHAR) <= HIVE_FILENAME_MAXLEN)
{
Offset = 0;
Length = FileName->Length;
}
else
{
Offset = FileName->Length / sizeof(WCHAR) - HIVE_FILENAME_MAXLEN;
Length = HIVE_FILENAME_MAXLEN * sizeof(WCHAR);
}
RtlCopyMemory(BaseBlock->FileName, FileName->Buffer + Offset, Length);
}
/**
* @brief
* Initializes a hive descriptor structure for a
* newly created hive in memory.
*
* @param[in,out] RegistryHive
* A pointer to a registry hive descriptor where
* the internal structures field are to be initialized
* for the said hive.
*
* @param[in] FileName
* A pointer to a Unicode string structure containing
* the hive file name to be copied from. If this argument
* is NULL, the base block will not have any hive file name.
*
* @return
* Returns STATUS_SUCCESS if the function has created the
* hive descriptor successfully. STATUS_NO_MEMORY is returned
* if the base header block could not be allocated.
*/
NTSTATUS
CMAPI
HvpCreateHive(
_Inout_ PHHIVE RegistryHive,
_In_opt_ PCUNICODE_STRING FileName)
{
PHBASE_BLOCK BaseBlock;
ULONG Index;
/* Allocate the base block */
BaseBlock = HvpAllocBaseBlockAligned(RegistryHive, FALSE, TAG_CM);
if (BaseBlock == NULL)
return STATUS_NO_MEMORY;
/* Clear it */
RtlZeroMemory(BaseBlock, RegistryHive->BaseBlockAlloc);
BaseBlock->Signature = HV_HBLOCK_SIGNATURE;
BaseBlock->Major = HSYS_MAJOR;
BaseBlock->Minor = HSYS_MINOR;
BaseBlock->Type = HFILE_TYPE_PRIMARY;
BaseBlock->Format = HBASE_FORMAT_MEMORY;
BaseBlock->Cluster = 1;
BaseBlock->RootCell = HCELL_NIL;
BaseBlock->Length = 0;
BaseBlock->Sequence1 = 1;
BaseBlock->Sequence2 = 1;
BaseBlock->TimeStamp.QuadPart = 0ULL;
/*
* No need to compute the checksum since
* the hive resides only in memory so far.
*/
BaseBlock->CheckSum = 0;
/* Set default boot type */
BaseBlock->BootType = HBOOT_TYPE_REGULAR;
/* Setup hive data */
RegistryHive->BaseBlock = BaseBlock;
RegistryHive->Version = BaseBlock->Minor; // == HSYS_MINOR
for (Index = 0; Index < 24; Index++)
{
RegistryHive->Storage[Stable].FreeDisplay[Index] = HCELL_NIL;
RegistryHive->Storage[Volatile].FreeDisplay[Index] = HCELL_NIL;
}
HvpInitFileName(BaseBlock, FileName);
return STATUS_SUCCESS;
}
/**
* @brief
* Initializes a hive descriptor from an already loaded
* registry hive stored in memory. The data of the hive is
* copied and it is prepared for read/write access.
*
* @param[in] Hive
* A pointer to a registry hive descriptor where
* the internal structures field are to be initialized
* from hive data that is already loaded in memory.
*
* @param[in] ChunkBase
* A pointer to a valid base block header containing
* registry header data for initialization.
*
* @param[in] FileName
* A pointer to a Unicode string structure containing
* the hive file name to be copied from. If this argument
* is NULL, the base block will not have any hive file name.
*
* @return
* Returns STATUS_SUCCESS if the function has initialized the
* hive descriptor successfully. STATUS_REGISTRY_CORRUPT is
* returned if the base block header contains invalid header
* data. STATUS_NO_MEMORY is returned if memory could not
* be allocated for registry stuff.
*/
NTSTATUS
CMAPI
HvpInitializeMemoryHive(
_In_ PHHIVE Hive,
_In_ PHBASE_BLOCK ChunkBase,
_In_opt_ PCUNICODE_STRING FileName)
{
SIZE_T BlockIndex;
PHBIN Bin, NewBin;
ULONG i;
ULONG BitmapSize;
PULONG BitmapBuffer;
SIZE_T ChunkSize;
ChunkSize = ChunkBase->Length;
DPRINT("ChunkSize: %zx\n", ChunkSize);
if (ChunkSize < sizeof(HBASE_BLOCK) ||
!HvpVerifyHiveHeader(ChunkBase, HFILE_TYPE_PRIMARY))
{
DPRINT1("Registry is corrupt: ChunkSize 0x%zx < sizeof(HBASE_BLOCK) 0x%zx, "
"or HvpVerifyHiveHeader() failed\n", ChunkSize, sizeof(HBASE_BLOCK));
return STATUS_REGISTRY_CORRUPT;
}
/* Allocate the base block */
Hive->BaseBlock = HvpAllocBaseBlockAligned(Hive, FALSE, TAG_CM);
if (Hive->BaseBlock == NULL)
return STATUS_NO_MEMORY;
RtlCopyMemory(Hive->BaseBlock, ChunkBase, sizeof(HBASE_BLOCK));
/* Setup hive data */
Hive->Version = ChunkBase->Minor;
/*
* Build a block list from the in-memory chunk and copy the data as
* we go.
*/
Hive->Storage[Stable].Length = (ULONG)(ChunkSize / HBLOCK_SIZE);
Hive->Storage[Stable].BlockList =
Hive->Allocate(Hive->Storage[Stable].Length *
sizeof(HMAP_ENTRY), FALSE, TAG_CM);
if (Hive->Storage[Stable].BlockList == NULL)
{
DPRINT1("Allocating block list failed\n");
Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
return STATUS_NO_MEMORY;
}
for (BlockIndex = 0; BlockIndex < Hive->Storage[Stable].Length; )
{
Bin = (PHBIN)((ULONG_PTR)ChunkBase + (BlockIndex + 1) * HBLOCK_SIZE);
if (Bin->Signature != HV_HBIN_SIGNATURE ||
(Bin->Size % HBLOCK_SIZE) != 0 ||
(Bin->FileOffset / HBLOCK_SIZE) != BlockIndex)
{
/*
* Bin is toast but luckily either the signature, size or offset
* is out of order. For the signature it is obvious what we are going
* to do, for the offset we are re-positioning the bin back to where it
* was and for the size we will set it up to a block size, since technically
* a hive bin is large as a block itself to accommodate cells.
*/
if (!CmIsSelfHealEnabled(FALSE))
{
DPRINT1("Invalid bin at BlockIndex %lu, Signature 0x%x, Size 0x%x. Self-heal not possible!\n",
(unsigned long)BlockIndex, (unsigned)Bin->Signature, (unsigned)Bin->Size);
Hive->Free(Hive->Storage[Stable].BlockList, 0);
Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
return STATUS_REGISTRY_CORRUPT;
}
/* Fix this bin */
Bin->Signature = HV_HBIN_SIGNATURE;
Bin->Size = HBLOCK_SIZE;
Bin->FileOffset = BlockIndex * HBLOCK_SIZE;
ChunkBase->BootType |= HBOOT_TYPE_SELF_HEAL;
DPRINT1("Bin at index %lu is corrupt and it has been repaired!\n", (unsigned long)BlockIndex);
}
NewBin = Hive->Allocate(Bin->Size, TRUE, TAG_CM);
if (NewBin == NULL)
{
Hive->Free(Hive->Storage[Stable].BlockList, 0);
Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
return STATUS_NO_MEMORY;
}
Hive->Storage[Stable].BlockList[BlockIndex].BinAddress = (ULONG_PTR)NewBin;
Hive->Storage[Stable].BlockList[BlockIndex].BlockAddress = (ULONG_PTR)NewBin;
RtlCopyMemory(NewBin, Bin, Bin->Size);
if (Bin->Size > HBLOCK_SIZE)
{
for (i = 1; i < Bin->Size / HBLOCK_SIZE; i++)
{
Hive->Storage[Stable].BlockList[BlockIndex + i].BinAddress = (ULONG_PTR)NewBin;
Hive->Storage[Stable].BlockList[BlockIndex + i].BlockAddress =
((ULONG_PTR)NewBin + (i * HBLOCK_SIZE));
}
}
BlockIndex += Bin->Size / HBLOCK_SIZE;
}
if (!NT_SUCCESS(HvpCreateHiveFreeCellList(Hive)))
{
HvpFreeHiveBins(Hive);
Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
return STATUS_NO_MEMORY;
}
BitmapSize = ROUND_UP(Hive->Storage[Stable].Length,
sizeof(ULONG) * 8) / 8;
BitmapBuffer = (PULONG)Hive->Allocate(BitmapSize, TRUE, TAG_CM);
if (BitmapBuffer == NULL)
{
HvpFreeHiveBins(Hive);
Hive->Free(Hive->BaseBlock, Hive->BaseBlockAlloc);
return STATUS_NO_MEMORY;
}
RtlInitializeBitMap(&Hive->DirtyVector, BitmapBuffer, BitmapSize * 8);
RtlClearAllBits(&Hive->DirtyVector);
/*
* Mark the entire hive as dirty. Indeed we understand if we charged up
* the alternate variant of the primary hive (e.g. SYSTEM.ALT) because
* FreeLdr could not load the main SYSTEM hive, due to corruptions, and
* repairing it with a LOG did not help at all.
*/
if (ChunkBase->BootRecover == HBOOT_BOOT_RECOVERED_BY_ALTERNATE_HIVE)
{
RtlSetAllBits(&Hive->DirtyVector);
Hive->DirtyCount = Hive->DirtyVector.SizeOfBitMap;
}
HvpInitFileName(Hive->BaseBlock, FileName);
return STATUS_SUCCESS;
}
/**
* @brief
* Initializes a hive descriptor for an already loaded hive
* that is stored in memory. This descriptor serves to denote
* such hive as being "flat", that is, the data and properties
* can be only read and not written into.
*
* @param[in] Hive
* A pointer to a registry hive descriptor where
* the internal structures fields are to be initialized
* from hive data that is already loaded in memory. Such
* hive descriptor will become read-only and flat.
*
* @param[in] ChunkBase
* A pointer to a valid base block header containing
* registry header data for initialization.
*
* @return
* Returns STATUS_SUCCESS if the function has initialized the
* flat hive descriptor. STATUS_REGISTRY_CORRUPT is returned if
* the base block header contains invalid header data.
*/
NTSTATUS
CMAPI
HvpInitializeFlatHive(
_In_ PHHIVE Hive,
_In_ PHBASE_BLOCK ChunkBase)
{
if (!HvpVerifyHiveHeader(ChunkBase, HFILE_TYPE_PRIMARY))
return STATUS_REGISTRY_CORRUPT;
/* Setup hive data */
Hive->BaseBlock = ChunkBase;
Hive->Version = ChunkBase->Minor;
Hive->Flat = TRUE;
Hive->ReadOnly = TRUE;
Hive->StorageTypeCount = 1;
/* Set default boot type */
ChunkBase->BootType = HBOOT_TYPE_REGULAR;
return STATUS_SUCCESS;
}
/**
* @brief
* Retrieves the base block hive header from the
* primary hive file stored in physical backing storage.
* This function may invoke a self-healing warning if
* hive header couldn't be obtained. See Return and Remarks
* sections for further information.
*
* @param[in] Hive
* A pointer to a registry hive descriptor that points
* to the primary hive being loaded. This descriptor is
* needed to obtain the hive header block from said hive.
*
* @param[in,out] HiveBaseBlock
* A pointer returned by the function that contains
* the hive header base block buffer obtained from
* the primary hive file pointed by the Hive argument.
* This parameter must not be NULL!
*
* @param[in,out] TimeStamp
* A pointer returned by the function that contains
* the time-stamp of the registry hive file at the
* moment of creation or modification. This parameter
* must not be NULL!
*
* @return
* This function returns a result indicator. That is,
* HiveSuccess is returned if the hive header was obtained
* successfully. NoMemory is returned if the hive base block
* could not be allocated. NotHive is returned if the hive file
* that's been read isn't actually a hive. RecoverHeader is
* returned if the header needs to be recovered. RecoverData
* is returned if the hive data needs to be returned.
*
* @remarks
* RecoverHeader and RecoverData are status indicators that
* invoke a self-healing procedure if the hive header could not
* be obtained in a normal way and as a matter of fact the whole
* registry initialization procedure is orchestrated. RecoverHeader
* implies that the base block header of a hive is corrupt and it
* needs to be recovered, whereas RecoverData implies the registry
* data is corrupt. The latter status indicator is less severe unlike
* the former because the system can cope with data loss.
*/
RESULT
CMAPI
HvpGetHiveHeader(
_In_ PHHIVE Hive,
_Inout_ PHBASE_BLOCK *HiveBaseBlock,
_Inout_ PLARGE_INTEGER TimeStamp)
{
PHBASE_BLOCK BaseBlock;
ULONG Result;
ULONG FileOffset;
PHBIN FirstBin;
ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
/* Assume failure and allocate the base block */
*HiveBaseBlock = NULL;
BaseBlock = HvpAllocBaseBlockAligned(Hive, TRUE, TAG_CM);
if (!BaseBlock)
{
DPRINT1("Failed to allocate an aligned base block buffer\n");
return NoMemory;
}
/* Clear it */
RtlZeroMemory(BaseBlock, sizeof(HBASE_BLOCK));
/* Now read it from disk */
FileOffset = 0;
Result = Hive->FileRead(Hive,
HFILE_TYPE_PRIMARY,
&FileOffset,
BaseBlock,
Hive->Cluster * HSECTOR_SIZE);
if (!Result)
{
/*
* Don't assume the hive is ultimately destroyed
* but instead try to read the first block of
* the first bin hive. So that we're sure of
* ourselves we can somewhat recover this hive.
*/
FileOffset = HBLOCK_SIZE;
Result = Hive->FileRead(Hive,
HFILE_TYPE_PRIMARY,
&FileOffset,
(PVOID)BaseBlock,
Hive->Cluster * HSECTOR_SIZE);
if (!Result)
{
DPRINT1("Failed to read the first block of the first bin hive (hive too corrupt)\n");
Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
return NotHive;
}
/*
* Deconstruct the casted buffer we got
* into a hive bin. Check if the offset
* position is in the right place (namely
* its offset must be 0 because it's the first
* bin) and it should have a sane signature.
*/
FirstBin = (PHBIN)BaseBlock;
if (FirstBin->Signature != HV_HBIN_SIGNATURE ||
FirstBin->FileOffset != 0)
{
DPRINT1("Failed to read the first block of the first bin hive (hive too corrupt)\n");
Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
return NotHive;
}
/*
* There's still hope for this hive so acknowledge the
* caller this hive needs a recoverable header.
*/
*TimeStamp = BaseBlock->TimeStamp;
DPRINT1("The hive is not fully corrupt, the base block needs to be RECOVERED\n");
return RecoverHeader;
}
/*
* This hive has a base block that's not maimed
* but is the header data valid?
*
* FIXME: We must check if primary and secondary
* sequences mismatch separately and fire up RecoverData
* in that case but due to a hack in HvLoadHive, let
* HvpVerifyHiveHeader check the sequences for now.
*/
if (!HvpVerifyHiveHeader(BaseBlock, HFILE_TYPE_PRIMARY))
{
DPRINT1("The hive base header block needs to be RECOVERED\n");
*TimeStamp = BaseBlock->TimeStamp;
Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
return RecoverHeader;
}
/* Return information */
*HiveBaseBlock = BaseBlock;
*TimeStamp = BaseBlock->TimeStamp;
return HiveSuccess;
}
/*
* FIXME: Disable compilation for AMD64 for now since it makes
* the FreeLdr binary size so large it makes booting impossible.
*/
#if !defined(_M_AMD64)
/**
* @brief
* Computes the hive space size by querying
* the file size of the associated hive file.
*
* @param[in] Hive
* A pointer to a hive descriptor where the
* hive length size is to be calculated.
*
* @return
* Returns the computed hive size.
*/
ULONG
CMAPI
HvpQueryHiveSize(
_In_ PHHIVE Hive)
{
#if !defined(CMLIB_HOST) && !defined(_BLDR_)
NTSTATUS Status;
FILE_STANDARD_INFORMATION FileStandard;
IO_STATUS_BLOCK IoStatusBlock;
#endif
ULONG HiveSize = 0;
/*
* Query the file size of the physical hive
* file. We need that information in order
* to ensure how big the hive actually is.
*/
#if !defined(CMLIB_HOST) && !defined(_BLDR_)
Status = ZwQueryInformationFile(((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY],
&IoStatusBlock,
&FileStandard,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwQueryInformationFile returned 0x%lx\n", Status);
return HiveSize;
}
/* Now compute the hive size */
HiveSize = FileStandard.EndOfFile.u.LowPart - HBLOCK_SIZE;
#endif
return HiveSize;
}
/**
* @brief
* Recovers the base block header by obtaining
* it from a log file associated with the hive.
*
* @param[in] Hive
* A pointer to a hive descriptor associated
* with the log file where the hive header is
* to be read from.
*
* @param[in] TimeStamp
* A pointer to a time-stamp used to check
* if the provided time matches with that
* of the hive.
*
* @param[in,out] BaseBlock
* A pointer returned by the caller that contains
* the base block header that was read from the log.
* This base block could be also made manually by hand.
* See Remarks for further information.
*
* @return
* Returns HiveSuccess if the header was obtained
* normally from the log. NoMemory is returned if
* the base block header could not be allocated.
* Fail is returned if self-healing mode is disabled
* and the log couldn't be read or a write attempt
* to the primary hive has failed. SelfHeal is returned
* to indicate that self-heal mode goes further.
*
* @remarks
* When SelfHeal is returned this indicates that
* even the log we have gotten at hand is corrupt
* but since we do have at least a log our only hope
* is to reconstruct the pieces of the base header
* by hand.
*/
RESULT
CMAPI
HvpRecoverHeaderFromLog(
_In_ PHHIVE Hive,
_In_ PLARGE_INTEGER TimeStamp,
_Inout_ PHBASE_BLOCK *BaseBlock)
{
BOOLEAN Success;
PHBASE_BLOCK LogHeader;
ULONG FileOffset;
ULONG HiveSize;
BOOLEAN HeaderResuscitated;
/*
* The cluster must not be greater than what the
* base block can permit.
*/
ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
/* Assume we haven't resuscitated the header */
HeaderResuscitated = FALSE;
/* Allocate an aligned buffer for the log header */
LogHeader = HvpAllocBaseBlockAligned(Hive, TRUE, TAG_CM);
if (!LogHeader)
{
DPRINT1("Failed to allocate memory for the log header\n");
return NoMemory;
}
/* Zero out our header buffer */
RtlZeroMemory(LogHeader, HSECTOR_SIZE);
/* Get the base header from the log */
FileOffset = 0;
Success = Hive->FileRead(Hive,
HFILE_TYPE_LOG,
&FileOffset,
LogHeader,
Hive->Cluster * HSECTOR_SIZE);
if (!Success ||
!HvpVerifyHiveHeader(LogHeader, HFILE_TYPE_LOG) ||
TimeStamp->HighPart != LogHeader->TimeStamp.HighPart ||
TimeStamp->LowPart != LogHeader->TimeStamp.LowPart)
{
/*
* We failed to read the base block header from
* the log, or the header itself or timestamp is
* invalid. Check if self healing is enabled.
*/
if (!CmIsSelfHealEnabled(FALSE))
{
DPRINT1("The log couldn't be read and self-healing mode is disabled\n");
Hive->Free(LogHeader, Hive->BaseBlockAlloc);
return Fail;
}
/*
* Determine the size of this hive so that
* we can estabilish the length of the base
* block we are trying to resuscitate.
*/
HiveSize = HvpQueryHiveSize(Hive);
if (HiveSize == 0)
{
DPRINT1("Failed to query the hive size\n");
Hive->Free(LogHeader, Hive->BaseBlockAlloc);
return Fail;
}
/*
* We can still resuscitate the base header if we
* could not grab one from the log by reconstructing
* the header internals by hand (this assumes the
* root cell is not NIL nor damaged). CmCheckRegistry
* does the ultimate judgement whether the root cell
* is fatally kaput or not after the hive has been
* initialized and loaded.
*
* For more information about base block header
* resuscitation, see https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md#notes-4.
*/
LogHeader->Signature = HV_HBLOCK_SIGNATURE;
LogHeader->Sequence1 = 1;
LogHeader->Sequence2 = 1;
LogHeader->Cluster = 1;
LogHeader->Length = HiveSize;
LogHeader->CheckSum = HvpHiveHeaderChecksum(LogHeader);
/*
* Acknowledge that we have resuscitated
* the header.
*/
HeaderResuscitated = TRUE;
DPRINT1("Header has been resuscitated, triggering self-heal mode\n");
}
/*
* Tag this log header as a primary hive before
* writing it to the hive.
*/
LogHeader->Type = HFILE_TYPE_PRIMARY;
/*
* If we have not made attempts of recovering
* the header due to log corruption then we
* have to compute the checksum. This is
* already done when the header has been resuscitated
* so don't try to do it twice.
*/
if (!HeaderResuscitated)
{
LogHeader->CheckSum = HvpHiveHeaderChecksum(LogHeader);
}
/* Write the header back to hive now */
Success = Hive->FileWrite(Hive,
HFILE_TYPE_PRIMARY,
&FileOffset,
LogHeader,
Hive->Cluster * HSECTOR_SIZE);
if (!Success)
{
DPRINT1("Couldn't write the base header to primary hive\n");
Hive->Free(LogHeader, Hive->BaseBlockAlloc);
return Fail;
}
*BaseBlock = LogHeader;
return HeaderResuscitated ? SelfHeal : HiveSuccess;
}
/**
* @brief
* Recovers the registry data by obtaining it
* from a log that is associated with the hive.
*
* @param[in] Hive
* A pointer to a hive descriptor associated
* with the log file where the hive data is to
* be read from.
*
* @param[in] BaseBlock
* A pointer to a base block header.
*
* @return
* Returns HiveSuccess if the data was obtained
* normally from the log. Fail is returned if
* self-healing is disabled and we couldn't be
* able to read the data from the log or the
* dirty vector signature is garbage or we
* failed to write the data block to the primary
* hive. SelfHeal is returned to indicate that
* the log is corrupt and the system will continue
* to be recovered at the expense of data loss.
*/
RESULT
CMAPI
HvpRecoverDataFromLog(
_In_ PHHIVE Hive,
_In_ PHBASE_BLOCK BaseBlock)
{
BOOLEAN Success;
ULONG FileOffset;
ULONG BlockIndex;
ULONG LogIndex;
ULONG StorageLength;
UCHAR DirtyVector[HSECTOR_SIZE];
UCHAR Buffer[HBLOCK_SIZE];
/* Read the dirty data from the log */
FileOffset = HV_LOG_HEADER_SIZE;
Success = Hive->FileRead(Hive,
HFILE_TYPE_LOG,
&FileOffset,
DirtyVector,
HSECTOR_SIZE);
if (!Success)
{
if (!CmIsSelfHealEnabled(FALSE))
{
DPRINT1("The log couldn't be read and self-healing mode is disabled\n");
return Fail;
}
/*
* There's nothing we can do on a situation
* where dirty data could not be read from
* the log. It does not make much sense to
* behead the system on such scenario so
* trigger a self-heal and go on. The worst
* thing that can happen? Data loss, that's it.
*/
DPRINT1("Triggering self-heal mode, DATA LOSS IS IMMINENT\n");
return SelfHeal;
}
/* Check the dirty vector */
if (*((PULONG)DirtyVector) != HV_LOG_DIRTY_SIGNATURE)
{
if (!CmIsSelfHealEnabled(FALSE))
{
DPRINT1("The log's dirty vector signature is not valid\n");
return Fail;
}
/*
* Trigger a self-heal like above. If the
* vector signature is garbage then logically
* whatever comes after the signature is also
* garbage.
*/
DPRINT1("Triggering self-heal mode, DATA LOSS IS IMMINENT\n");
return SelfHeal;
}
/* Now read each data individually and write it back to hive */
LogIndex = 0;
StorageLength = BaseBlock->Length / HBLOCK_SIZE;
for (BlockIndex = 0; BlockIndex < StorageLength; BlockIndex++)
{
/* Skip this block if it's not dirty and go to the next one */
if (DirtyVector[BlockIndex + sizeof(HV_LOG_DIRTY_SIGNATURE)] != HV_LOG_DIRTY_BLOCK)
{
continue;
}
FileOffset = HSECTOR_SIZE + HSECTOR_SIZE + LogIndex * HBLOCK_SIZE;
Success = Hive->FileRead(Hive,
HFILE_TYPE_LOG,
&FileOffset,
Buffer,
HBLOCK_SIZE);
if (!Success)
{
DPRINT1("Failed to read the dirty block (index %u)\n", BlockIndex);
return Fail;
}
FileOffset = HBLOCK_SIZE + BlockIndex * HBLOCK_SIZE;
Success = Hive->FileWrite(Hive,
HFILE_TYPE_PRIMARY,
&FileOffset,
Buffer,
HBLOCK_SIZE);
if (!Success)
{
DPRINT1("Failed to write dirty block to hive (index %u)\n", BlockIndex);
return Fail;
}
/* Increment the index in log as we continue further */
LogIndex++;
}
return HiveSuccess;
}
#endif
/**
* @brief
* Loads a registry hive from a physical hive file
* within the physical backing storage. Base block
* and registry data are read from the said physical
* hive file. This function can perform registry recovery
* if hive loading could not be done normally.
*
* @param[in] Hive
* A pointer to a hive descriptor where the said hive
* is to be loaded from the physical hive file.
*
* @param[in] FileName
* A pointer to a NULL-terminated Unicode string structure
* containing the hive file name to be copied from.
*
* @return
* STATUS_SUCCESS is returned if the hive has been loaded
* successfully. STATUS_INSUFFICIENT_RESOURCES is returned
* if there's not enough memory resources to satisfy registry
* operations and/or requests. STATUS_NOT_REGISTRY_FILE is returned
* if the hive is not actually a hive file. STATUS_REGISTRY_CORRUPT
* is returned if the hive has subdued previous damage and
* the hive could not be recovered because there's no
* log present or self healing is disabled. STATUS_REGISTRY_RECOVERED
* is returned if the hive has been recovered. An eventual flush
* of the registry is needed after the hive's been fully loaded.
*/
NTSTATUS
CMAPI
HvLoadHive(
_In_ PHHIVE Hive,
_In_opt_ PCUNICODE_STRING FileName)
{
NTSTATUS Status;
BOOLEAN Success;
PHBASE_BLOCK BaseBlock = NULL;
/* FIXME: See the comment above (near HvpQueryHiveSize) */
#if defined(_M_AMD64)
ULONG Result;
#else
ULONG Result, Result2;
#endif
LARGE_INTEGER TimeStamp;
ULONG Offset = 0;
PVOID HiveData;
ULONG FileSize;
BOOLEAN HiveSelfHeal = FALSE;
/* Get the hive header */
Result = HvpGetHiveHeader(Hive, &BaseBlock, &TimeStamp);
switch (Result)
{
/* Out of memory */
case NoMemory:
{
/* Fail */
DPRINT1("There's no enough memory to get the header\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Not a hive */
case NotHive:
{
/* Fail */
DPRINT1("The hive is not an actual registry hive file\n");
return STATUS_NOT_REGISTRY_FILE;
}
/* Hive data needs a repair */
case RecoverData:
{
/*
* FIXME: We must be handling this status
* case if the header isn't corrupt but
* the counter sequences do not match but
* due to a hack in HvLoadHive we have
* to do both a header + data recovery.
* RecoverHeader also implies RecoverData
* anyway. When HvLoadHive gets rid of
* that hack, data recovery must be done
* after we read the hive block by block.
*/
break;
}
/* Hive header needs a repair */
case RecoverHeader:
/* FIXME: See the comment above (near HvpQueryHiveSize) */
#if defined(_M_AMD64)
{
return STATUS_REGISTRY_CORRUPT;
}
#else
{
/* Check if this hive has a log at hand to begin with */
#if (NTDDI_VERSION < NTDDI_VISTA)
if (!Hive->Log)
{
DPRINT1("The hive has no log for header recovery\n");
return STATUS_REGISTRY_CORRUPT;
}
#endif
/* The header needs to be recovered so do it */
DPRINT1("Attempting to heal the header...\n");
Result2 = HvpRecoverHeaderFromLog(Hive, &TimeStamp, &BaseBlock);
if (Result2 == NoMemory)
{
DPRINT1("There's no enough memory to recover header from log\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Did we fail? */
if (Result2 == Fail)
{
DPRINT1("Failed to recover the hive header\n");
return STATUS_REGISTRY_CORRUPT;
}
/* Did we trigger the self-heal mode? */
if (Result2 == SelfHeal)
{
HiveSelfHeal = TRUE;
}
/* Now recover the data */
Result2 = HvpRecoverDataFromLog(Hive, BaseBlock);
if (Result2 == Fail)
{
DPRINT1("Failed to recover the hive data\n");
return STATUS_REGISTRY_CORRUPT;
}
/* Tag the boot as self heal if we haven't done it before */
if ((Result2 == SelfHeal) && (!HiveSelfHeal))
{
HiveSelfHeal = TRUE;
}
break;
}
#endif
}
/* Set the boot type */
BaseBlock->BootType = HiveSelfHeal ? HBOOT_TYPE_SELF_HEAL : HBOOT_TYPE_REGULAR;
/* Setup hive data */
Hive->BaseBlock = BaseBlock;
Hive->Version = BaseBlock->Minor;
/* Allocate a buffer large enough to hold the hive */
FileSize = HBLOCK_SIZE + BaseBlock->Length; // == sizeof(HBASE_BLOCK) + BaseBlock->Length;
HiveData = Hive->Allocate(FileSize, TRUE, TAG_CM);
if (!HiveData)
{
Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
DPRINT1("There's no enough memory to allocate hive data\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
/* HACK (see explanation below): Now read the whole hive */
Success = Hive->FileRead(Hive,
HFILE_TYPE_PRIMARY,
&Offset,
HiveData,
FileSize);
if (!Success)
{
DPRINT1("Failed to read the whole hive\n");
Hive->Free(HiveData, FileSize);
Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
return STATUS_NOT_REGISTRY_FILE;
}
/*
* HACK (FIXME): Free our base block... it's useless in
* this implementation.
*
* And it's useless because while the idea of reading the
* hive from physical file is correct, the implementation
* is hacky and incorrect. Instead of reading the whole hive,
* we should be instead reading the hive block by block,
* deconstruct the block buffer and enlist the bins and
* prepare the storage for the hive. What we currently do
* is we try to initialize the hive storage and bins enlistment
* by calling HvpInitializeMemoryHive below. This mixes
* HINIT_FILE and HINIT_MEMORY together which is disgusting
* because HINIT_FILE implementation shouldn't be calling
* HvpInitializeMemoryHive.
*/
Hive->Free(BaseBlock, Hive->BaseBlockAlloc);
Status = HvpInitializeMemoryHive(Hive, HiveData, FileName);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to initialize hive from memory\n");
Hive->Free(HiveData, FileSize);
return Status;
}
/*
* If we have done some sort of recovery against
* the hive we were going to load it from file,
* tell the caller we did recover it. The caller
* is responsible to flush the data later on.
*/
return (Result == RecoverHeader) ? STATUS_REGISTRY_RECOVERED : STATUS_SUCCESS;
}
/**
* @brief
* Initializes a registry hive. It allocates a hive
* descriptor and sets up the hive type depending
* on the type chosen by the caller.
*
* @param[in,out] RegistryHive
* A pointer to a hive descriptor to be initialized.
*
* @param[in] OperationType
* The operation type to choose for hive initialization.
* For further information about this, see Remarks.
*
* @param[in] HiveFlags
* A hive flag. Such flag is used to determine what kind
* of action must be taken into the hive or what aspects
* must be taken into account for such hive. For further
* information, see Remarks.
*
* @param[in] FileType
* Hive file type. For the newly initialized hive, you can
* choose from three different types for the hive:
*
* HFILE_TYPE_PRIMARY - Initializes a hive as primary hive
* of the system.
*
* HFILE_TYPE_LOG - The newly created hive is a hive log.
* Logs don't exist per se but they're accompanied with their
* associated primary hives. The Log field member of the hive
* descriptor is set to TRUE.
*
* HFILE_TYPE_EXTERNAL - The newly created hive is a portable
* hive, that can be used and copied for different machines,
* unlike primary hives.
*
* HFILE_TYPE_ALTERNATE - The newly created hive is an alternate hive.
* Technically speaking it is the same as a primary hive (the representation
* of on-disk image of the registry header is HFILE_TYPE_PRIMARY), with
* the purpose is to serve as a backup hive. The Alternate field of the
* hive descriptor is set to TRUE. Only the SYSTEM hive has a backup
* alternate hive.
*
* @param[in] HiveData
* An arbitrary pointer that points to the hive data. Usually this
* data is in form of a hive base block given by the caller of this
* function.
*
* @param[in] Allocate
* A pointer to a ALLOCATE_ROUTINE function that describes
* the main allocation routine for this hive. This parameter
* can be NULL.
*
* @param[in] Free
* A pointer to a FREE_ROUTINE function that describes the
* the main memory freeing routine for this hive. This parameter
* can be NULL.
*
* @param[in] FileSetSize
* A pointer to a FILE_SET_SIZE_ROUTINE function that describes
* the file set size routine for this hive. This parameter
* can be NULL.
*
* @param[in] FileWrite
* A pointer to a FILE_WRITE_ROUTINE function that describes
* the file writing routine for this hive. This parameter
* can be NULL.
*
* @param[in] FileRead
* A pointer to a FILE_READ_ROUTINE function that describes
* the file reading routine for this hive. This parameter
* can be NULL.
*
* @param[in] FileFlush
* A pointer to a FILE_FLUSH_ROUTINE function that describes
* the file flushing routine for this hive. This parameter
* can be NULL.
*
* @param[in] Cluster
* The registry hive cluster to be set. Usually this value
* is set to 1.
*
* @param[in] FileName
* A to a NULL-terminated Unicode string structure containing
* the hive file name. This parameter can be NULL.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully
* initialized the hive. STATUS_REGISTRY_RECOVERED is returned
* if the hive has subdued previous damage and it's been recovered.
* This function will perform a hive writing and flushing with
* healthy and recovered data in that case. STATUS_REGISTRY_IO_FAILED
* is returned if registry hive writing/flushing of recovered data
* has failed. STATUS_INVALID_PARAMETER is returned if an invalid
* operation type pointed by OperationType parameter has been
* submitted. A failure NTSTATUS code is returned otherwise.
*
* @remarks
* OperationType parameter influences how should the hive be
* initialized. These are the following supported operation
* types:
*
* HINIT_CREATE -- Creates a new fresh hive.
*
* HINIT_MEMORY -- Initializes a registry hive that already exists
* from memory. The hive data is copied from the
* loaded hive in memory and used for read/write
* access.
*
* HINIT_FLAT -- Initializes a flat registry hive, with data that can
* only be read and not written into. Cells are always
* allocated on a flat hive.
*
* HINIT_FILE -- Initializes a hive from a hive file from the physical
* backing storage of the system. In this situation the
* function will perform self-healing and resuscitation
* procedures if data read from the physical hive file
* is corrupt.
*
* HINIT_MEMORY_INPLACE -- This operation type is similar to HINIT_FLAT,
* with the difference is that the hive is initialized
* with hive data from memory. The hive can only be read
* and not written into.
*
* HINIT_MAPFILE -- Initializes a hive from a hive file from the physical
* backing storage of the system. Unlike HINIT_FILE, the
* initialized hive is not backed to paged pool in memory
* but rather through mapping views.
*
* Alongside the operation type, the hive flags also influence the aspect
* of the newly initialized hive. These are the following supported hive
* flags:
*
* HIVE_VOLATILE -- Tells the function that this hive will be volatile, that
* is, the data stored inside the hive space resides only
* in volatile memory of the system, aka the RAM, and the
* data will be erased upon shutdown of the system.
*
* HIVE_NOLAZYFLUSH -- Tells the function that no lazy flushing must be
* done to this hive.
*/
NTSTATUS
CMAPI
HvInitialize(
_Inout_ PHHIVE RegistryHive,
_In_ ULONG OperationType,
_In_ ULONG HiveFlags,
_In_ ULONG FileType,
_In_opt_ PVOID HiveData,
_In_opt_ PALLOCATE_ROUTINE Allocate,
_In_opt_ PFREE_ROUTINE Free,
_In_opt_ PFILE_SET_SIZE_ROUTINE FileSetSize,
_In_opt_ PFILE_WRITE_ROUTINE FileWrite,
_In_opt_ PFILE_READ_ROUTINE FileRead,
_In_opt_ PFILE_FLUSH_ROUTINE FileFlush,
_In_ ULONG Cluster,
_In_opt_ PCUNICODE_STRING FileName)
{
NTSTATUS Status;
PHHIVE Hive = RegistryHive;
/*
* Create a new hive structure that will hold all the maintenance data.
*/
RtlZeroMemory(Hive, sizeof(HHIVE));
Hive->Signature = HV_HHIVE_SIGNATURE;
Hive->Allocate = Allocate;
Hive->Free = Free;
Hive->FileSetSize = FileSetSize;
Hive->FileWrite = FileWrite;
Hive->FileRead = FileRead;
Hive->FileFlush = FileFlush;
Hive->RefreshCount = 0;
Hive->StorageTypeCount = HTYPE_COUNT;
Hive->Cluster = Cluster;
Hive->BaseBlockAlloc = sizeof(HBASE_BLOCK); // == HBLOCK_SIZE
Hive->Version = HSYS_MINOR;
#if (NTDDI_VERSION < NTDDI_VISTA)
Hive->Log = (FileType == HFILE_TYPE_LOG);
Hive->Alternate = (FileType == HFILE_TYPE_ALTERNATE);
#endif
Hive->HiveFlags = HiveFlags & ~HIVE_NOLAZYFLUSH;
// TODO: The CellRoutines point to different callbacks
// depending on the OperationType.
Hive->GetCellRoutine = HvpGetCellData;
Hive->ReleaseCellRoutine = NULL;
switch (OperationType)
{
case HINIT_CREATE:
{
/* Create a new fresh hive */
Status = HvpCreateHive(Hive, FileName);
break;
}
case HINIT_MEMORY:
{
/* Initialize a hive from memory */
Status = HvpInitializeMemoryHive(Hive, HiveData, FileName);
break;
}
case HINIT_FLAT:
{
/* Initialize a flat read-only hive */
Status = HvpInitializeFlatHive(Hive, HiveData);
break;
}
case HINIT_FILE:
{
/* Initialize a hive by loading it from physical file in backing storage */
Status = HvLoadHive(Hive, FileName);
if ((Status != STATUS_SUCCESS) &&
(Status != STATUS_REGISTRY_RECOVERED))
{
/* Unrecoverable failure */
DPRINT1("Registry hive couldn't be initialized, it's corrupt (hive 0x%p)\n", Hive);
return Status;
}
/* FIXME: See the comment above (near HvpQueryHiveSize) */
#if !defined(_M_AMD64)
/*
* Check if we have recovered this hive. We are responsible to
* flush the primary hive back to backing storage afterwards.
*/
if (Status == STATUS_REGISTRY_RECOVERED)
{
if (!HvSyncHiveFromRecover(Hive))
{
DPRINT1("Fail to write healthy data back to hive\n");
return STATUS_REGISTRY_IO_FAILED;
}
/*
* We are saved from hell, now clear out the
* dirty bits and dirty count.
*
* FIXME: We must as well clear out the log
* and reset its size to 0 but we are lacking
* in code that deals with log growing/shrinking
* management. When the time comes to implement
* this stuff we must set the LogSize and file size
* to 0 here.
*/
RtlClearAllBits(&Hive->DirtyVector);
Hive->DirtyCount = 0;
/*
* Masquerade the status code as success.
* STATUS_REGISTRY_RECOVERED is not a failure
* code but not STATUS_SUCCESS either so the caller
* thinks we failed at our job.
*/
Status = STATUS_SUCCESS;
}
#endif
break;
}
case HINIT_MEMORY_INPLACE:
{
// Status = HvpInitializeMemoryInplaceHive(Hive, HiveData);
// break;
DPRINT1("HINIT_MEMORY_INPLACE is UNIMPLEMENTED\n");
return STATUS_NOT_IMPLEMENTED;
}
case HINIT_MAPFILE:
{
DPRINT1("HINIT_MAPFILE is UNIMPLEMENTED\n");
return STATUS_NOT_IMPLEMENTED;
}
default:
{
DPRINT1("Invalid operation type (OperationType = %u)\n", OperationType);
return STATUS_INVALID_PARAMETER;
}
}
return Status;
}
/**
* @brief
* Frees all the bins within the storage, the dirty vector
* and the base block associated with the given registry
* hive descriptor.
*
* @param[in] RegistryHive
* A pointer to a hive descriptor where all of its data
* is to be freed.
*/
VOID
CMAPI
HvFree(
_In_ PHHIVE RegistryHive)
{
if (!RegistryHive->ReadOnly)
{
/* Release hive bitmap */
if (RegistryHive->DirtyVector.Buffer)
{
RegistryHive->Free(RegistryHive->DirtyVector.Buffer, 0);
}
HvpFreeHiveBins(RegistryHive);
/* Free the BaseBlock */
if (RegistryHive->BaseBlock)
{
RegistryHive->Free(RegistryHive->BaseBlock, RegistryHive->BaseBlockAlloc);
RegistryHive->BaseBlock = NULL;
}
}
}
/* EOF */