Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535/* * 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. */BOOLEANCMAPIHvpVerifyHiveHeader( _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. */VOIDCMAPIHvpFreeHiveBins( _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__inlinePHBASE_BLOCKHvpAllocBaseBlockAligned( _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. */staticVOIDHvpInitFileName( _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. */NTSTATUSCMAPIHvpCreateHive( _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. */NTSTATUSCMAPIHvpInitializeMemoryHive( _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. */NTSTATUSCMAPIHvpInitializeFlatHive( _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. */RESULTCMAPIHvpGetHiveHeader( _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. */ULONGCMAPIHvpQueryHiveSize( _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. */RESULTCMAPIHvpRecoverHeaderFromLog( _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. */RESULTCMAPIHvpRecoverDataFromLog( _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. */NTSTATUSCMAPIHvLoadHive( _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. */NTSTATUSCMAPIHvInitialize( _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. */VOIDCMAPIHvFree( _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 */