Windows2000/private/ntos/config/hivesync.c
2020-09-30 17:12:32 +02:00

2061 lines
50 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
hivesync.c
Abstract:
This module implements procedures to write dirty parts of a hive's
stable store to backing media.
Author:
Bryan M. Willman (bryanwi) 28-Mar-92
Environment:
Revision History:
--*/
#include "cmp.h"
extern BOOLEAN HvShutdownComplete; // Set to true after shutdown
// to disable any further I/O
#if DBG
#define DumpDirtyVector(Hive) \
{ \
PRTL_BITMAP BitMap; \
ULONG BitMapSize; \
PUCHAR BitBuffer; \
ULONG i; \
UCHAR Byte; \
\
BitMap = &(Hive->DirtyVector); \
BitMapSize = (BitMap->SizeOfBitMap) / 8; \
BitBuffer = (PUCHAR)(BitMap->Buffer); \
for (i = 0; i < BitMapSize; i++) { \
if ((i % 8) == 0) { \
KdPrint(("\n\t")); \
} \
Byte = BitBuffer[i]; \
KdPrint(("%02x ", Byte)); \
} \
KdPrint(("\n")); \
}
#else
#define DumpDirtyVector(Hive)
#endif
// Private prototypes
BOOLEAN
HvpWriteLog(
PHHIVE Hive
);
BOOLEAN
HvpFindNextDirtyBlock(
PHHIVE Hive,
PRTL_BITMAP BitMap,
PULONG Current,
PUCHAR *Address,
PULONG Length,
PULONG Offset
);
VOID
HvpDiscardBins(
PHHIVE Hive
);
VOID
HvpTruncateBins(
PHHIVE Hive
);
VOID
HvRefreshHive(
PHHIVE Hive
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,HvMarkCellDirty)
#pragma alloc_text(PAGE,HvIsBinDirty)
#pragma alloc_text(PAGE,HvMarkDirty)
#pragma alloc_text(PAGE,HvMarkClean)
#pragma alloc_text(PAGE,HvpGrowLog1)
#pragma alloc_text(PAGE,HvpGrowLog2)
#pragma alloc_text(PAGE,HvSyncHive)
#pragma alloc_text(PAGE,HvpDoWriteHive)
#pragma alloc_text(PAGE,HvpWriteLog)
#pragma alloc_text(PAGE,HvpFindNextDirtyBlock)
#pragma alloc_text(PAGE,HvWriteHive)
#pragma alloc_text(PAGE,HvRefreshHive)
#pragma alloc_text(PAGE,HvpDiscardBins)
#pragma alloc_text(PAGE,HvpTruncateBins)
#ifdef _WRITE_PROTECTED_REGISTRY_POOL
#pragma alloc_text(PAGE,HvpChangeBinAllocation)
#pragma alloc_text(PAGE,HvpMarkBinReadWrite)
#endif
#endif
// Code for tracking modifications and ensuring adequate log space
BOOLEAN
HvMarkCellDirty(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Marks the data for the specified cell dirty.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - hcell_index of cell that is being edited
Return Value:
TRUE - it worked
FALSE - could not allocate log space, failure!
--*/
{
ULONG Type;
ULONG Size;
PHCELL pCell;
PHMAP_ENTRY Me;
HCELL_INDEX Base;
PHBIN Bin;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvMarkCellDirty:\n\t"));
KdPrint(("Hive:%08lx Cell:%08lx\n", Hive, Cell));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
Type = HvGetCellType(Cell);
if ( (Hive->HiveFlags & HIVE_VOLATILE) ||
(Type == Volatile) )
{
return TRUE;
}
pCell = HvpGetHCell(Hive,Cell);
#if DBG
Me = HvpGetCellMap(Hive, Cell);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,Cell);
Bin = (PHBIN)(Me->BinAddress & HMAP_BASE);
ASSERT(Bin->Signature == HBIN_SIGNATURE);
#endif
// If it's an old format hive, mark the entire
// bin dirty, because the Last backpointers are
// such a pain to deal with in the partial
// alloc and free-coalescing cases.
if (USE_OLD_CELL(Hive)) {
Me = HvpGetCellMap(Hive, Cell);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,Cell);
Bin = (PHBIN)(Me->BinAddress & HMAP_BASE);
Base = Bin->FileOffset;
Size = Bin->Size;
return HvMarkDirty(Hive, Base, Size);
} else {
if (pCell->Size < 0) {
Size = -pCell->Size;
} else {
Size = pCell->Size;
}
ASSERT(Size < Bin->Size);
return HvMarkDirty(Hive, Cell-FIELD_OFFSET(HCELL,u.NewCell), Size);
}
}
BOOLEAN
HvIsBinDirty(
IN PHHIVE Hive,
IN HCELL_INDEX Cell
)
/*++
Routine Description:
Given a hive and a cell, checks whether the bin containing
that cell has any dirty clusters or not.
Arguments:
Hive - Supplies a pointer to the hive control structure
Cell - Supplies the HCELL_INDEX of the Cell.
Return Value:
TRUE - Bin contains dirty data.
FALSE - Bin does not contain dirty data.
--*/
{
ULONG Type;
PHCELL pcell;
PRTL_BITMAP Bitmap;
ULONG First;
ULONG Last;
ULONG i;
PHMAP_ENTRY Map;
PHBIN Bin;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvIsBinDirty:\n\t"));
KdPrint(("Hive:%08lx Cell:%08lx\n", Hive, Cell));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
Type = HvGetCellType(Cell);
if ( (Hive->HiveFlags & HIVE_VOLATILE) ||
(Type == Volatile) )
{
return FALSE;
}
Bitmap = &(Hive->DirtyVector);
Map = HvpGetCellMap(Hive, Cell);
VALIDATE_CELL_MAP(__LINE__,Map,Hive,Cell);
Bin = (PHBIN)(Map->BinAddress & HMAP_BASE);
First = Bin->FileOffset / HSECTOR_SIZE;
Last = (Bin->FileOffset + Bin->Size - 1) / HSECTOR_SIZE;
for (i=First; i<=Last; i++) {
if (RtlCheckBit(Bitmap, i)==1) {
return(TRUE);
}
}
return(FALSE);
}
BOOLEAN
HvMarkDirty(
PHHIVE Hive,
HCELL_INDEX Start,
ULONG Length
)
/*++
Routine Description:
Marks the relevent parts of a hive dirty, so that they will
be flushed to backing store.
If Hive->Cluster is not 1, then adjacent all logical sectors
in the given cluster will be forced dirty (and log space
allocated for them.) This must be done here rather than in
HvSyncHive so that we can know how much to grow the log.
This is a noop for Volatile address range.
NOTE: Range will not be marked dirty if operation fails.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Start - supplies a hive virtual address (i.e., an HCELL_INDEX or
like form address) of the start of the area to mark dirty.
Length - inclusive length in bytes of area to mark dirty.
Return Value:
TRUE - it worked
FALSE - could not allocate log space, failure!
--*/
{
ULONG Type;
PRTL_BITMAP BitMap;
ULONG First;
ULONG Last;
ULONG i;
ULONG Cluster;
ULONG OriginalDirtyCount;
ULONG DirtySectors;
BOOLEAN Result = TRUE;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvMarkDirty:\n\t"));
KdPrint(("Hive:%08lx Start:%08lx Length:%08lx\n", Hive, Start, Length));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
Type = HvGetCellType(Start);
if ( (Hive->HiveFlags & HIVE_VOLATILE) ||
(Type == Volatile) )
{
return TRUE;
}
BitMap = &(Hive->DirtyVector);
OriginalDirtyCount = Hive->DirtyCount;
First = Start / HSECTOR_SIZE;
Last = (Start + Length - 1) / HSECTOR_SIZE;
Cluster = Hive->Cluster;
if (Cluster > 1) {
// Force Start down to base of cluster
// Force End up to top of cluster
First = First & ~(Cluster - 1);
Last = ROUND_UP(Last+1, Cluster) - 1;
}
if (Last >= BitMap->SizeOfBitMap) {
Last = BitMap->SizeOfBitMap-1;
}
// Try and grow the log enough to accomodate all the dirty sectors.
DirtySectors = 0;
for (i = First; i <= Last; i++) {
if (RtlCheckBit(BitMap, i)==0) {
++DirtySectors;
}
}
if (DirtySectors != 0) {
if (HvpGrowLog1(Hive, DirtySectors) == FALSE) {
return(FALSE);
}
if ((OriginalDirtyCount == 0) && (First != 0)) {
Result = HvMarkDirty(Hive, 0, sizeof(HBIN)); // force header of 1st bin dirty
if (Result==FALSE) {
return(FALSE);
}
}
// Log has been successfully grown, go ahead
// and set the dirty bits.
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
Hive->DirtyCount += DirtySectors;
RtlSetBits(BitMap, First, Last-First+1);
}
// mark this bin as writable
HvpMarkBinReadWrite(Hive,Start);
if (!(Hive->HiveFlags & HIVE_NOLAZYFLUSH)) {
CmpLazyFlush();
}
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
return(TRUE);
}
BOOLEAN
HvMarkClean(
PHHIVE Hive,
HCELL_INDEX Start,
ULONG Length
)
/*++
Routine Description:
Clears the dirty bits for a given portion of a hive. This is
the inverse of HvMarkDirty, although it does not give up any
file space in the primary or log that HvMarkDirty may have reserved.
This is a noop for Volatile address range.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Start - supplies a hive virtual address (i.e., an HCELL_INDEX or
like form address) of the start of the area to mark dirty.
Length - inclusive length in bytes of area to mark dirty.
Return Value:
TRUE - it worked
--*/
{
ULONG Type;
PRTL_BITMAP BitMap;
ULONG First;
ULONG Last;
ULONG i;
ULONG Cluster;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvMarkClean:\n\t"));
KdPrint(("Hive:%08lx Start:%08lx Length:%08lx\n", Hive, Start, Length));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
Type = HvGetCellType(Start);
if ( (Hive->HiveFlags & HIVE_VOLATILE) ||
(Type == Volatile) )
{
return TRUE;
}
BitMap = &(Hive->DirtyVector);
First = Start / HSECTOR_SIZE;
Last = (Start + Length - 1) / HSECTOR_SIZE;
Cluster = Hive->Cluster;
if (Cluster > 1) {
// Force Start down to base of cluster
// Force End up to top of cluster
First = First & ~(Cluster - 1);
Last = ROUND_UP(Last+1, Cluster) - 1;
}
if (Last >= BitMap->SizeOfBitMap) {
Last = BitMap->SizeOfBitMap-1;
}
// Subtract out the dirty count and
// and clear the dirty bits.
for (i=First; i<=Last; i++) {
if (RtlCheckBit(BitMap,i)==1) {
--Hive->DirtyCount;
RtlClearBits(BitMap, i, 1);
}
}
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
return(TRUE);
}
BOOLEAN
HvpGrowLog1(
PHHIVE Hive,
ULONG Count
)
/*++
Routine Description:
Adjust the log for growth in the number of sectors of dirty
data that are desired.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Count - number of additional logical sectors of log space needed
Return Value:
TRUE - it worked
FALSE - could not allocate log space, failure!
--*/
{
ULONG ClusterSize;
ULONG RequiredSize;
ULONG tmp;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvpGrowLog1:\n\t"));
KdPrint(("Hive:%08lx Count:%08lx\n", Hive, Count));
}
ASSERT(Hive->ReadOnly == FALSE);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
// If logging is off, tell caller world is OK.
if (Hive->Log == FALSE) {
return TRUE;
}
ClusterSize = Hive->Cluster * HSECTOR_SIZE;
tmp = Hive->DirtyVector.SizeOfBitMap / 8; // bytes
tmp += sizeof(ULONG); // signature
RequiredSize =
ClusterSize + // 1 cluster for header
ROUND_UP(tmp, ClusterSize) +
((Hive->DirtyCount + Count) * HSECTOR_SIZE);
RequiredSize = ROUND_UP(RequiredSize, HLOG_GROW);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
if ( ! (Hive->FileSetSize)(Hive, HFILE_TYPE_LOG, RequiredSize)) {
return FALSE;
}
Hive->LogSize = RequiredSize;
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
return TRUE;
}
BOOLEAN
HvpGrowLog2(
PHHIVE Hive,
ULONG Size
)
/*++
Routine Description:
Adjust the log for growth in the size of the hive, in particular,
account for the increased space needed for a bigger dirty vector.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Size - proposed growth in size in bytes.
Return Value:
TRUE - it worked
FALSE - could not allocate log space, failure!
--*/
{
ULONG ClusterSize;
ULONG RequiredSize;
ULONG DirtyBytes;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvpGrowLog2:\n\t"));
KdPrint(("Hive:%08lx Size:%08lx\n", Hive, Size));
}
ASSERT(Hive->ReadOnly == FALSE);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
// If logging is off, tell caller world is OK.
if (Hive->Log == FALSE) {
return TRUE;
}
ASSERT( (Size % HSECTOR_SIZE) == 0 );
ClusterSize = Hive->Cluster * HSECTOR_SIZE;
ASSERT( (((Hive->Storage[Stable].Length + Size) / HSECTOR_SIZE) % 8) == 0);
DirtyBytes = (Hive->DirtyVector.SizeOfBitMap / 8) +
((Size / HSECTOR_SIZE) / 8) +
sizeof(ULONG); // signature
DirtyBytes = ROUND_UP(DirtyBytes, ClusterSize);
RequiredSize =
ClusterSize + // 1 cluster for header
(Hive->DirtyCount * HSECTOR_SIZE) +
DirtyBytes;
RequiredSize = ROUND_UP(RequiredSize, HLOG_GROW);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
if ( ! (Hive->FileSetSize)(Hive, HFILE_TYPE_LOG, RequiredSize)) {
return FALSE;
}
Hive->LogSize = RequiredSize;
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
return TRUE;
}
// Code for syncing a hive to backing store
BOOLEAN
HvSyncHive(
PHHIVE Hive
)
/*++
Routine Description:
Force backing store to match the memory image of the Stable
part of the hive's space.
Logs, primary, and alternate data can be written. Primary is
always written. Normally either a log or an alternate, but
not both, will also be written.
It is possible to write only the primary.
All dirty bits will be set clear.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Return Value:
TRUE - it worked
FALSE - some failure.
--*/
{
BOOLEAN oldFlag;
CMLOG(CML_WORKER, CMS_IO) {
KdPrint(("HvSyncHive:\n\t"));
KdPrint(("Hive:%08lx\n", Hive));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
// Punt if post shutdown
if (HvShutdownComplete) {
CMLOG(CML_BUGCHECK, CMS_IO) {
KdPrint(("HvSyncHive: Attempt to sync AFTER SHUTDOWN\n"));
}
return FALSE;
}
// If nothing dirty, do nothing
if (Hive->DirtyCount == 0) {
return TRUE;
}
HvpTruncateBins(Hive);
// If hive is volatile, do nothing
if (Hive->HiveFlags & HIVE_VOLATILE) {
return TRUE;
}
CMLOG(CML_FLOW, CMS_IO) {
KdPrint(("\tDirtyCount:%08lx\n", Hive->DirtyCount));
KdPrint(("\tDirtyVector:"));
DumpDirtyVector(Hive);
}
// disable hard error popups, to avoid self deadlock on bogus devices
oldFlag = IoSetThreadHardErrorMode(FALSE);
// Write a log.
if (Hive->Log == TRUE) {
if (HvpWriteLog(Hive) == FALSE) {
IoSetThreadHardErrorMode(oldFlag);
return FALSE;
}
}
// Write the primary
if (HvpDoWriteHive(Hive, HFILE_TYPE_PRIMARY) == FALSE) {
IoSetThreadHardErrorMode(oldFlag);
return FALSE;
}
// Write an alternate
if (Hive->Alternate == TRUE) {
if (HvpDoWriteHive(Hive, HFILE_TYPE_ALTERNATE) == FALSE) {
IoSetThreadHardErrorMode(oldFlag);
return FALSE;
}
}
// restore hard error popups mode
IoSetThreadHardErrorMode(oldFlag);
// Hive was successfully written out, discard any bins marked as
// discardable.
HvpDiscardBins(Hive);
// Clear the dirty map
RtlClearAllBits(&(Hive->DirtyVector));
Hive->DirtyCount = 0;
return TRUE;
}
BOOLEAN
HvpDoWriteHive(
PHHIVE Hive,
ULONG FileType
)
/*++
Routine Description:
Write dirty parts of the hive out to either its primary or alternate
file. Write the header, flush, write all data, flush, update header,
flush. Assume either logging or primary/alternate pairs used.
NOTE: TimeStamp is not set, assumption is that HvpWriteLog set
that. It is only used for checking if Logs correspond anyway.
Arguments:
Hive - pointer to Hive for which dirty data is to be written.
FileType - indicated whether primary or alternate file should be written.
Return Value:
TRUE - it worked
FALSE - it failed
--*/
{
PHBASE_BLOCK BaseBlock;
ULONG Offset;
PUCHAR Address;
ULONG Length;
BOOLEAN rc;
ULONG Current;
PRTL_BITMAP BitMap;
PHMAP_ENTRY Me;
PHBIN Bin;
BOOLEAN ShrinkHive;
PCMP_OFFSET_ARRAY offsetArray;
CMP_OFFSET_ARRAY offsetElement;
ULONG Count;
ULONG SetBitCount;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvpDoWriteHive:\n\t"));
KdPrint(("Hive:%08lx FileType:%08lx\n", Hive, FileType));
}
// flush first, so that the filesystem structures get written to
// disk if we have grown the file.
if (!(Hive->FileFlush)(Hive, FileType)) {
return(FALSE);
}
BaseBlock = Hive->BaseBlock;
if (BaseBlock->Length > Hive->Storage[Stable].Length) {
ShrinkHive = TRUE;
} else {
ShrinkHive = FALSE;
}
// --- Write out header first time, flush ---
ASSERT(BaseBlock->Signature == HBASE_BLOCK_SIGNATURE);
ASSERT(BaseBlock->Major == HSYS_MAJOR);
ASSERT(BaseBlock->Format == HBASE_FORMAT_MEMORY);
ASSERT(Hive->ReadOnly == FALSE);
if (BaseBlock->Sequence1 != BaseBlock->Sequence2) {
// Some previous log attempt failed, or this hive needs to
// be recovered, so punt.
return FALSE;
}
BaseBlock->Length = Hive->Storage[Stable].Length;
BaseBlock->Sequence1++;
BaseBlock->Type = HFILE_TYPE_PRIMARY;
BaseBlock->Cluster = Hive->Cluster;
BaseBlock->CheckSum = HvpHeaderCheckSum(BaseBlock);
Offset = 0;
offsetElement.FileOffset = Offset;
offsetElement.DataBuffer = (PVOID) BaseBlock;
offsetElement.DataLength = HSECTOR_SIZE * Hive->Cluster;
rc = (Hive->FileWrite)(
Hive,
FileType,
&offsetElement,
1,
&Offset
);
if (rc == FALSE) {
return FALSE;
}
if ( ! (Hive->FileFlush)(Hive, FileType)) {
return FALSE;
}
Offset = ROUND_UP(Offset, HBLOCK_SIZE);
// --- Write out dirty data (only if there is any) ---
if (Hive->DirtyVector.Buffer != NULL) {
// First sector of first bin will always be dirty, write it out
// with the TimeStamp value overlaid on its Link field.
BitMap = &(Hive->DirtyVector);
ASSERT(RtlCheckBit(BitMap, 0) == 1);
ASSERT(RtlCheckBit(BitMap, (Hive->Cluster - 1)) == 1);
ASSERT(sizeof(LIST_ENTRY) >= sizeof(LARGE_INTEGER));
Me = HvpGetCellMap(Hive, 0);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,0);
Address = (PUCHAR)Me->BlockAddress;
Length = Hive->Cluster * HSECTOR_SIZE;
Bin = (PHBIN)Address;
Bin->TimeStamp = BaseBlock->TimeStamp;
offsetElement.FileOffset = Offset;
offsetElement.DataBuffer = (PVOID) Address;
offsetElement.DataLength = Length;
rc = (Hive->FileWrite)(
Hive,
FileType,
&offsetElement,
1,
&Offset
);
ASSERT((Offset % (Hive->Cluster * HSECTOR_SIZE)) == 0);
if (rc == FALSE) {
return FALSE;
}
// Write out the rest of the dirty data
Current = Hive->Cluster; // don't rewrite 1st bin or header
SetBitCount = RtlNumberOfSetBits(BitMap);
offsetArray = (PCMP_OFFSET_ARRAY)ExAllocatePool(PagedPool, sizeof(CMP_OFFSET_ARRAY) * SetBitCount);
if (offsetArray == NULL) {
return FALSE;
}
Count = 0;
while (HvpFindNextDirtyBlock(
Hive,
BitMap,
&Current,
&Address,
&Length,
&Offset
) == TRUE)
{
// Gather data into array.
ASSERT(Count < SetBitCount);
offsetArray[Count].FileOffset = Offset;
offsetArray[Count].DataBuffer = Address;
offsetArray[Count].DataLength = Length;
Offset += Length;
Count++;
ASSERT((Offset % (Hive->Cluster * HSECTOR_SIZE)) == 0);
}
rc = (Hive->FileWrite)(
Hive,
FileType,
offsetArray,
Count,
&Offset // Just an OUT parameter which returns the point
// in the file after the last write.
);
ExFreePool(offsetArray);
if (rc == FALSE) {
return FALSE;
}
}
if ( ! (Hive->FileFlush)(Hive, FileType)) {
return FALSE;
}
// --- Write header again to report completion ---
BaseBlock->Sequence2++;
BaseBlock->CheckSum = HvpHeaderCheckSum(BaseBlock);
Offset = 0;
offsetElement.FileOffset = Offset;
offsetElement.DataBuffer = (PVOID) BaseBlock;
offsetElement.DataLength = HSECTOR_SIZE * Hive->Cluster;
rc = (Hive->FileWrite)(
Hive,
FileType,
&offsetElement,
1,
&Offset
);
if (rc == FALSE) {
return FALSE;
}
if (ShrinkHive) {
// Hive has shrunk, give up the excess space.
CmpDoFileSetSize(Hive, FileType, Hive->Storage[Stable].Length + HBLOCK_SIZE);
}
if ( ! (Hive->FileFlush)(Hive, FileType)) {
return FALSE;
}
if ((Hive->Log) &&
(Hive->LogSize > HLOG_MINSIZE(Hive))) {
// Shrink log back down, reserve at least two clusters
// worth of space so that if all the disk space is
// consumed, there will still be enough space prereserved
// to allow a minimum of registry operations so the user
// can log on.
CmpDoFileSetSize(Hive, HFILE_TYPE_LOG, HLOG_MINSIZE(Hive));
Hive->LogSize = HLOG_MINSIZE(Hive);
}
return TRUE;
}
BOOLEAN
HvpWriteLog(
PHHIVE Hive
)
/*++
Routine Description:
Write a header, the DirtyVector, and all the dirty data into
the log file. Do flushes at the right places. Update the header.
Arguments:
Hive - pointer to Hive for which dirty data is to be logged.
Return Value:
TRUE - it worked
FALSE - it failed
--*/
{
PHBASE_BLOCK BaseBlock;
ULONG Offset;
PUCHAR Address;
ULONG Length;
BOOLEAN rc;
ULONG Current;
ULONG junk;
ULONG ClusterSize;
ULONG HeaderLength;
PRTL_BITMAP BitMap;
ULONG DirtyVectorSignature = HLOG_DV_SIGNATURE;
LARGE_INTEGER systemtime;
PCMP_OFFSET_ARRAY offsetArray;
CMP_OFFSET_ARRAY offsetElement;
ULONG Count;
ULONG SetBitCount;
CMLOG(CML_MINOR, CMS_IO) {
KdPrint(("HvpWriteLog:\n\t"));
KdPrint(("Hive:%08lx\n", Hive));
}
BitMap = &Hive->DirtyVector;
// --- Write out header first time, flush ---
BaseBlock = Hive->BaseBlock;
ASSERT(BaseBlock->Signature == HBASE_BLOCK_SIGNATURE);
ASSERT(BaseBlock->Major == HSYS_MAJOR);
ASSERT(BaseBlock->Format == HBASE_FORMAT_MEMORY);
ASSERT(Hive->ReadOnly == FALSE);
if (BaseBlock->Sequence1 != BaseBlock->Sequence2) {
// Some previous log attempt failed, or this hive needs to
// be recovered, so punt.
return FALSE;
}
BaseBlock->Sequence1++;
KeQuerySystemTime(&systemtime);
BaseBlock->TimeStamp = systemtime;
BaseBlock->Type = HFILE_TYPE_LOG;
ClusterSize = Hive->Cluster * HSECTOR_SIZE;
HeaderLength = ROUND_UP(HLOG_HEADER_SIZE, ClusterSize);
BaseBlock->Cluster = Hive->Cluster;
BaseBlock->CheckSum = HvpHeaderCheckSum(BaseBlock);
Offset = 0;
offsetElement.FileOffset = Offset;
offsetElement.DataBuffer = (PVOID) BaseBlock;
offsetElement.DataLength = HSECTOR_SIZE * Hive->Cluster;
rc = (Hive->FileWrite)(
Hive,
HFILE_TYPE_LOG,
&offsetElement,
1,
&Offset
);
if (rc == FALSE) {
return FALSE;
}
Offset = ROUND_UP(Offset, HeaderLength);
if ( ! (Hive->FileFlush)(Hive, HFILE_TYPE_LOG)) {
return FALSE;
}
// --- Write out dirty vector ---
ASSERT(sizeof(ULONG) == sizeof(DirtyVectorSignature)); // See GrowLog1 above
offsetElement.FileOffset = Offset;
offsetElement.DataBuffer = (PVOID) &DirtyVectorSignature;
offsetElement.DataLength = sizeof(DirtyVectorSignature);
rc = (Hive->FileWrite)(
Hive,
HFILE_TYPE_LOG,
&offsetElement,
1,
&Offset
);
if (rc == FALSE) {
return FALSE;
}
Length = Hive->DirtyVector.SizeOfBitMap / 8;
Address = (PUCHAR)(Hive->DirtyVector.Buffer);
offsetElement.FileOffset = Offset;
offsetElement.DataBuffer = (PVOID) Address;
offsetElement.DataLength = Length;
rc = (Hive->FileWrite)(
Hive,
HFILE_TYPE_LOG,
&offsetElement,
1,
&Offset
);
if (rc == FALSE) {
return FALSE;
}
Offset = ROUND_UP(Offset, ClusterSize);
// --- Write out body of log ---
SetBitCount = RtlNumberOfSetBits(BitMap);
offsetArray = (PCMP_OFFSET_ARRAY)ExAllocatePool(PagedPool, sizeof(CMP_OFFSET_ARRAY) * SetBitCount);
if (offsetArray == NULL) {
return FALSE;
}
Count = 0;
Current = 0;
while (HvpFindNextDirtyBlock(
Hive,
BitMap,
&Current,
&Address,
&Length,
&junk
) == TRUE)
{
// Gather data into array.
ASSERT(Count < SetBitCount);
offsetArray[Count].FileOffset = Offset;
offsetArray[Count].DataBuffer = Address;
offsetArray[Count].DataLength = Length;
Offset += Length;
Count++;
ASSERT((Offset % ClusterSize) == 0);
}
rc = (Hive->FileWrite)(
Hive,
HFILE_TYPE_LOG,
offsetArray,
Count,
&Offset // Just an OUT parameter which returns the point
// in the file after the last write.
);
ExFreePool(offsetArray);
if (rc == FALSE) {
return FALSE;
}
if ( ! (Hive->FileFlush)(Hive, HFILE_TYPE_LOG)) {
return FALSE;
}
// --- Write header again to report completion ---
BaseBlock->Sequence2++;
BaseBlock->CheckSum = HvpHeaderCheckSum(BaseBlock);
Offset = 0;
offsetElement.FileOffset = Offset;
offsetElement.DataBuffer = (PVOID) BaseBlock;
offsetElement.DataLength = HSECTOR_SIZE * Hive->Cluster;
rc = (Hive->FileWrite)(
Hive,
HFILE_TYPE_LOG,
&offsetElement,
1,
&Offset
);
if (rc == FALSE) {
return FALSE;
}
if ( ! (Hive->FileFlush)(Hive, HFILE_TYPE_LOG)) {
return FALSE;
}
return TRUE;
}
BOOLEAN
HvpFindNextDirtyBlock(
PHHIVE Hive,
PRTL_BITMAP BitMap,
PULONG Current,
PUCHAR *Address,
PULONG Length,
PULONG Offset
)
/*++
Routine Description:
This routine finds and reports the largest run of dirty logical
sectors in the hive, which are contiguous in memory and on disk.
Arguments:
Hive - pointer to Hive of interest.
BitMap - supplies a pointer to a bitmap structure, which
describes what is dirty.
Current - supplies a pointer to a varible that tracks position
in the bitmap. It is a bitnumber. It is updated by
this call.
Address - supplies a pointer to a variable to receive a pointer
to the area in memory to be written out.
Length - supplies a pointer to a variable to receive the length
of the region to read/write
Offset - supplies a pointer to a variable to receive the offset
in the backing file to which the data should be written.
(not valid for log files)
Return Value:
TRUE - more to write, ret values good
FALSE - all data has been written
--*/
{
ULONG i;
ULONG EndOfBitMap;
ULONG Start;
ULONG End;
HCELL_INDEX FileBaseAddress;
HCELL_INDEX FileEndAddress;
PHMAP_ENTRY Me;
PUCHAR Block;
PUCHAR StartBlock;
PUCHAR NextBlock;
ULONG RunSpan;
ULONG RunLength;
ULONG FileLength;
PFREE_HBIN FreeBin;
CMLOG(CML_FLOW, CMS_IO) {
KdPrint(("HvpFindNextDirtyBlock:\n\t"));
KdPrint(("Hive:%08lx Current:%08lx\n", Hive, *Current));
}
EndOfBitMap = BitMap->SizeOfBitMap;
if (*Current >= EndOfBitMap) {
return FALSE;
}
// Find next run of set bits
for (i = *Current; i < EndOfBitMap; i++) {
if (RtlCheckBit(BitMap, i) == 1) {
break;
}
}
Start = i;
for ( ; i < EndOfBitMap; i++) {
if (RtlCheckBit(BitMap, i) == 0) {
break;
}
}
End = i;
// Compute hive virtual addresses, beginning file address, memory address
FileBaseAddress = Start * HSECTOR_SIZE;
FileEndAddress = End * HSECTOR_SIZE;
FileLength = FileEndAddress - FileBaseAddress;
if (FileLength == 0) {
*Address = NULL;
*Current = 0xffffffff;
*Length = 0;
return FALSE;
}
Me = HvpGetCellMap(Hive, FileBaseAddress);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,FileBaseAddress);
if (Me->BinAddress & HMAP_DISCARDABLE) {
FreeBin = (PFREE_HBIN)Me->BlockAddress;
StartBlock = (PUCHAR)((Me->BinAddress & HMAP_BASE) + FileBaseAddress - FreeBin->FileOffset );
} else {
StartBlock = (PUCHAR)Me->BlockAddress;
}
Block = StartBlock;
ASSERT(((PHBIN)(Me->BinAddress & HMAP_BASE))->Signature == HBIN_SIGNATURE);
*Address = Block + (FileBaseAddress & HCELL_OFFSET_MASK);
*Offset = FileBaseAddress + HBLOCK_SIZE;
// Build up length. First, account for sectors in first block.
RunSpan = HSECTOR_COUNT - (Start % HSECTOR_COUNT);
if ((End - Start) <= RunSpan) {
// Entire length is in first block, return it
*Length = FileLength;
*Current = End;
return TRUE;
} else {
RunLength = RunSpan * HSECTOR_SIZE;
FileBaseAddress = ROUND_UP(FileBaseAddress+1, HBLOCK_SIZE);
}
// Scan forward through blocks, filling up length as we go.
// NOTE: This loop grows forward 1 block at time. If we were
// really clever we'd fill forward a bin at time, since
// bins are always contiguous. But most bins will be
// one block long anyway, so we won't bother for now.
while (RunLength < FileLength) {
Me = HvpGetCellMap(Hive, FileBaseAddress);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,FileBaseAddress);
ASSERT(((PHBIN)(Me->BinAddress & HMAP_BASE))->Signature == HBIN_SIGNATURE);
if (Me->BinAddress & HMAP_DISCARDABLE) {
FreeBin = (PFREE_HBIN)Me->BlockAddress;
NextBlock = (PUCHAR)((Me->BinAddress & HMAP_BASE) + FileBaseAddress - FreeBin->FileOffset );
} else {
NextBlock = (PUCHAR)Me->BlockAddress;
}
if ( (NextBlock - Block) != HBLOCK_SIZE) {
// We've hit a discontinuity in memory. RunLength is
// as long as it's going to get.
break;
}
if ((FileEndAddress - FileBaseAddress) <= HBLOCK_SIZE) {
// We've reached the tail block, all is contiguous,
// fill up to end and return.
*Length = FileLength;
*Current = End;
return TRUE;
}
// Just another contiguous block, fill forward
RunLength += HBLOCK_SIZE;
RunSpan += HSECTOR_COUNT;
FileBaseAddress += HBLOCK_SIZE;
Block = NextBlock;
}
// We either hit a discontinuity, OR, we're at the end of the range
// we're trying to fill. In either case, return.
*Length = RunLength;
*Current = Start + RunSpan;
return TRUE;
}
NTSTATUS
HvWriteHive(
PHHIVE Hive
)
/*++
Routine Description:
Write the hive out. Write only to the Primary file, neither
logs nor alternates will be updated. The hive will be written
to the HFILE_TYPE_EXTERNAL handle.
Intended for use in applications like SaveKey.
Only Stable storage will be written (as for any hive.)
Presumption is that layer above has set HFILE_TYPE_EXTERNAL
handle to point to correct place.
Applying this call to an active hive will generally hose integrity
measures.
HOW IT WORKS:
Make a new DirtyVector. Fill it with 1s (all dirty).
Make hive point at it. We now have what looks like
a completely dirty hive.
Call HvpWriteHive, which will write the whole thing to disk.
Put back DirtyVector, and free the extra one we used.
In failure case, force Sequence numbers in Hive and BaseBlock
to match.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest.
Return Value:
Status.
--*/
{
PULONG SaveDirtyVector;
ULONG SaveDirtyVectorSize;
PULONG AltDirtyVector;
ULONG AltDirtyVectorSize;
PHBASE_BLOCK SaveBaseBlock;
PHBASE_BLOCK AltBaseBlock;
ULONG Alignment;
NTSTATUS status;
CMLOG(CML_MAJOR, CMS_IO) {
KdPrint(("HvWriteHive: \n"));
KdPrint(("\tHive = %08lx\n"));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
// Punt if post shutdown
if (HvShutdownComplete) {
CMLOG(CML_BUGCHECK, CMS_IO) {
KdPrint(("HvWriteHive: Attempt to write hive AFTER SHUTDOWN\n"));
}
return STATUS_REGISTRY_IO_FAILED;
}
// Splice in a duplicate DirtyVector with all bits set.
SaveDirtyVector = Hive->DirtyVector.Buffer;
SaveDirtyVectorSize = Hive->DirtyVector.SizeOfBitMap;
SaveBaseBlock = Hive->BaseBlock;
AltDirtyVectorSize = (Hive->Storage[Stable].Length / HSECTOR_SIZE) / 8;
AltDirtyVector = (Hive->Allocate)(ROUND_UP(AltDirtyVectorSize,sizeof(ULONG)), FALSE);
if (AltDirtyVector == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit1;
}
Hive->DirtyVector.Buffer = AltDirtyVector;
Hive->DirtyVector.SizeOfBitMap = AltDirtyVectorSize * 8;
RtlSetAllBits(&(Hive->DirtyVector));
// Splice in a duplicate BaseBlock
AltBaseBlock = (Hive->Allocate)(sizeof(HBASE_BLOCK), TRUE);
if (AltBaseBlock == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit2;
}
// Make sure the buffer we got back is cluster-aligned. If not, try
// harder to get an aligned buffer.
Alignment = Hive->Cluster * HSECTOR_SIZE - 1;
if (((ULONG_PTR)AltBaseBlock & Alignment) != 0) {
(Hive->Free)(AltBaseBlock, sizeof(HBASE_BLOCK));
AltBaseBlock = (PHBASE_BLOCK)((Hive->Allocate)(PAGE_SIZE, TRUE));
if (AltBaseBlock == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit2;
}
// Return the quota for the extra allocation, as we are not really using
// it and it will not be accounted for later when we free it.
CmpReleaseGlobalQuota(PAGE_SIZE - sizeof(HBASE_BLOCK));
}
RtlMoveMemory(AltBaseBlock, SaveBaseBlock, HSECTOR_SIZE);
Hive->BaseBlock = AltBaseBlock;
// Ensure the file can be made big enough, then do the deed
status = CmpDoFileSetSize(Hive,
HFILE_TYPE_EXTERNAL,
Hive->Storage[Stable].Length);
if (NT_SUCCESS(status)) {
if (!HvpDoWriteHive(Hive, HFILE_TYPE_EXTERNAL)) {
status = STATUS_REGISTRY_IO_FAILED;
}
}
// Clean up, success or failure
CmpFree(AltBaseBlock, sizeof(HBASE_BLOCK));
Exit2:
CmpFree(AltDirtyVector, ROUND_UP(AltDirtyVectorSize,sizeof(ULONG)));
Exit1:
Hive->DirtyVector.Buffer = SaveDirtyVector;
Hive->DirtyVector.SizeOfBitMap = SaveDirtyVectorSize;
Hive->BaseBlock = SaveBaseBlock;
return status;
}
VOID
HvRefreshHive(
PHHIVE Hive
)
/*++
Routine Description:
Undo the last sync. We do this by reading back all data which
has been marked dirty from the file into memory. Update the
free list. We will then clear the dirty vector.
Any growth since the last sync will be discarded, and in fact,
the size of the file will be set down.
WARNNOTE: Failure will cause a bugcheck, as we cannot
keep the hive consistent if we fail part way through.
All I/O is done via HFILE_TYPE_PRIMARY.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest.
Return Value:
NONE. Either works or BugChecks.
--*/
{
ULONG Offset;
ULONG ReadLength;
ULONG checkstatus;
PUCHAR Address;
ULONG Current;
PRTL_BITMAP BitMap;
BOOLEAN rc;
ULONG Start;
ULONG End;
ULONG BitLength;
HCELL_INDEX TailStart;
HCELL_INDEX p;
PHMAP_ENTRY t;
ULONG i;
PHBIN Bin;
PLIST_ENTRY List;
PFREE_HBIN FreeBin;
PHMAP_ENTRY Map;
HCELL_INDEX RootCell;
PCM_KEY_NODE RootNode;
HCELL_INDEX LinkCell;
// this array stores the last elements in each free cell list for the stable storage
HCELL_INDEX TailDisplay[HHIVE_FREE_DISPLAY_SIZE];
// noop or assert on various uninteresting or bogus conditions
if (Hive->DirtyCount == 0) {
return;
}
ASSERT(Hive->HiveFlags & HIVE_NOLAZYFLUSH);
ASSERT(Hive->Storage[Volatile].Length == 0);
// be sure the hive is not already trash
checkstatus = HvCheckHive(Hive, NULL);
if (checkstatus != 0) {
KeBugCheckEx(REGISTRY_ERROR,7,0,(ULONG_PTR)Hive,checkstatus);
}
Hive->RefreshCount++;
// Capture the LinkCell backpointer in the hive's root cell. We need this in case
// the first bin is overwritten with what was on disk.
RootCell = Hive->BaseBlock->RootCell;
RootNode = (PCM_KEY_NODE)HvGetCell(Hive, RootCell);
LinkCell = RootNode->Parent;
// Any bins that have been marked as discardable, but not yet flushed to
// disk, are going to be overwritten with old data. Bring them back into
// memory and remove their FREE_HBIN marker from the list.
List = Hive->Storage[Stable].FreeBins.Flink;
while (List != &Hive->Storage[Stable].FreeBins) {
FreeBin = CONTAINING_RECORD(List, FREE_HBIN, ListEntry);
List = List->Flink;
if (FreeBin->Flags & FREE_HBIN_DISCARDABLE) {
for (i=0; i<FreeBin->Size; i+=HBLOCK_SIZE) {
Map = HvpGetCellMap(Hive, FreeBin->FileOffset+i);
VALIDATE_CELL_MAP(__LINE__,Map,Hive,FreeBin->FileOffset+i);
Map->BlockAddress = (Map->BinAddress & HMAP_BASE)+i;
Map->BinAddress &= ~HMAP_DISCARDABLE;
}
RemoveEntryList(&FreeBin->ListEntry);
(Hive->Free)(FreeBin, sizeof(FREE_HBIN));
}
}
// OverRead base block.
Offset = 0;
if ( (Hive->FileRead)(
Hive,
HFILE_TYPE_PRIMARY,
&Offset,
Hive->BaseBlock,
HBLOCK_SIZE
) != TRUE)
{
KeBugCheckEx(REGISTRY_ERROR,7,1,0,0);
}
TailStart = (HCELL_INDEX)(Hive->BaseBlock->Length);
// Free "tail" memory and maps for it, update hive size pointers
HvFreeHivePartial(Hive, TailStart, Stable);
// Clear dirty vector for data past Hive->BaseBlock->Length
Start = Hive->BaseBlock->Length / HSECTOR_SIZE;
End = Hive->DirtyVector.SizeOfBitMap;
BitLength = End - Start;
RtlClearBits(&(Hive->DirtyVector), Start, BitLength);
// Scan dirty blocks. Read contiguous blocks off disk into hive.
// Stop when we get to reduced length.
BitMap = &(Hive->DirtyVector);
Current = 0;
while (HvpFindNextDirtyBlock(
Hive,
&Hive->DirtyVector,
&Current, &Address,
&ReadLength,
&Offset
))
{
ASSERT(Offset < (Hive->BaseBlock->Length + sizeof(HBASE_BLOCK)));
rc = (Hive->FileRead)(
Hive,
HFILE_TYPE_PRIMARY,
&Offset,
(PVOID)Address,
ReadLength
);
if (rc == FALSE) {
KeBugCheckEx(REGISTRY_ERROR,7,2,(ULONG_PTR)&Offset,rc);
}
}
// If we read the start of any HBINs into memory, it is likely
// their MemAlloc fields are invalid. Walk through the HBINs
// and write valid MemAlloc values for any HBINs whose first
// sector was reread.
p=0;
while (p < Hive->Storage[Stable].Length) {
t = HvpGetCellMap(Hive, p);
VALIDATE_CELL_MAP(__LINE__,t,Hive,p);
Bin = (PHBIN)(t->BlockAddress & HMAP_BASE);
if ((t->BinAddress & HMAP_DISCARDABLE)==0) {
if (RtlCheckBit(&Hive->DirtyVector, p / HSECTOR_SIZE)==1) {
// The first sector in the HBIN is dirty.
// Reset the BinAddress to the Block address to cover
// the case where a few smaller bins have been coalesced
// into a larger bin. We want the smaller bins back now.
t->BinAddress = (t->BinAddress & ~HMAP_BASE) | t->BlockAddress;
// Check the map to see if this is the start
// of a memory allocation or not.
if (t->BinAddress & HMAP_NEWALLOC) {
// Walk through the map to determine the length
// of the allocation.
Bin->MemAlloc = 0;
do {
t = HvpGetCellMap(Hive, p+Bin->MemAlloc+HBLOCK_SIZE);
Bin->MemAlloc += HBLOCK_SIZE;
if (p+Bin->MemAlloc == Hive->Storage[Stable].Length) {
// Reached the end of the hive.
break;
}
VALIDATE_CELL_MAP(__LINE__,t,Hive,p+Bin->MemAlloc);
} while ( (t->BinAddress & HMAP_NEWALLOC) == 0);
} else {
Bin->MemAlloc = 0;
}
}
p += Bin->Size;
} else {
FreeBin = (PFREE_HBIN)t->BlockAddress;
p += FreeBin->Size;
}
}
// be sure we haven't filled memory with trash
checkstatus = HvCheckHive(Hive, NULL);
if (checkstatus != 0) {
KeBugCheckEx(REGISTRY_ERROR,7,3,(ULONG_PTR)Hive,checkstatus);
}
// reinit the free list
for (i = 0; i < HHIVE_FREE_DISPLAY_SIZE; i++) {
Hive->Storage[Stable].FreeDisplay[i] = HCELL_NIL;
TailDisplay[i] = HCELL_NIL;
}
Hive->Storage[Stable].FreeSummary = 0;
// rebuild the free list
p = 0;
while (p < Hive->Storage[Stable].Length) {
t = HvpGetCellMap(Hive, p);
VALIDATE_CELL_MAP(__LINE__,t,Hive,p);
if ((t->BinAddress & HMAP_DISCARDABLE) == 0) {
Bin = (PHBIN)((t->BinAddress) & HMAP_BASE);
if ( ! HvpEnlistFreeCells(Hive, Bin, Bin->FileOffset,TailDisplay)) {
KeBugCheckEx(REGISTRY_ERROR,7,5,(ULONG_PTR)Bin,Bin->FileOffset);
}
p = (ULONG)p + Bin->Size;
} else {
FreeBin = (PFREE_HBIN)t->BlockAddress;
p = (ULONG)p + FreeBin->Size;
}
}
// Finally we need to rewrite the parent field in the root hcell. This is
// patched in at hive load time so the correct value could have just been
// overwritten with whatever happened to be on disk.
RootNode = (PCM_KEY_NODE)HvGetCell(Hive, RootCell);
RootNode->Parent = LinkCell;
RootNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
// be sure the structure of the thing is OK after all this
checkstatus = CmCheckRegistry((PCMHIVE)Hive, FALSE);
if (checkstatus != 0) {
KeBugCheckEx(REGISTRY_ERROR,7,6,(ULONG_PTR)Hive,checkstatus);
}
// Clear dirty vector.
RtlClearAllBits(&(Hive->DirtyVector));
Hive->DirtyCount = 0;
// Adjust the file size, if this fails, ignore it, since it just
// means the file is too big.
(Hive->FileSetSize)(
Hive,
HFILE_TYPE_PRIMARY,
(Hive->BaseBlock->Length + HBLOCK_SIZE)
);
return;
}
VOID
HvpDiscardBins(
IN PHHIVE Hive
)
/*++
Routine Description:
Walks through the dirty bins in a hive to see if any are marked
discardable. If so, they are discarded and the map is updated to
reflect this.
Arguments:
Hive - Supplies the hive control structure.
Return Value:
None.
--*/
{
PHBIN Bin;
PHMAP_ENTRY Map;
PHMAP_ENTRY PreviousMap;
PHMAP_ENTRY NextMap;
PFREE_HBIN FreeBin;
PFREE_HBIN PreviousFreeBin;
PFREE_HBIN NextFreeBin;
PLIST_ENTRY List;
List = Hive->Storage[Stable].FreeBins.Flink;
while (List != &Hive->Storage[Stable].FreeBins) {
ASSERT_LISTENTRY(List);
FreeBin = CONTAINING_RECORD(List, FREE_HBIN, ListEntry);
if (FreeBin->Flags & FREE_HBIN_DISCARDABLE) {
Map = HvpGetCellMap(Hive, FreeBin->FileOffset);
VALIDATE_CELL_MAP(__LINE__,Map,Hive,FreeBin->FileOffset);
Bin = (PHBIN)(Map->BinAddress & HMAP_BASE);
ASSERT(Map->BinAddress & HMAP_DISCARDABLE);
// Note we use ExFreePool directly here to avoid
// giving back the quota for this bin. By charging
// registry quota for discarded bins, we prevent
// sparse hives from requiring more quota after
// a reboot than on a running system.
ExFreePool(Bin);
FreeBin->Flags &= ~FREE_HBIN_DISCARDABLE;
}
List=List->Flink;
}
}
VOID
HvpTruncateBins(
IN PHHIVE Hive
)
/*++
Routine Description:
Attempts to shrink the hive by truncating any bins that are discardable at
the end of the hive. Applies to both stable and volatile storage.
Arguments:
Hive - Supplies the hive to be truncated.
Return Value:
None.
--*/
{
HSTORAGE_TYPE i;
PHMAP_ENTRY Map;
ULONG NewLength;
PFREE_HBIN FreeBin;
// stable and volatile
for (i=0;i<HTYPE_COUNT;i++) {
// find the last in-use bin in the hive
NewLength = Hive->Storage[i].Length;
while (NewLength > 0) {
Map = HvpGetCellMap(Hive, (NewLength - HBLOCK_SIZE) + (i*HCELL_TYPE_MASK));
VALIDATE_CELL_MAP(__LINE__,Map,Hive,(NewLength - HBLOCK_SIZE) + (i*HCELL_TYPE_MASK));
if (Map->BinAddress & HMAP_DISCARDABLE) {
FreeBin = (PFREE_HBIN)Map->BlockAddress;
NewLength = FreeBin->FileOffset;
} else {
break;
}
}
if (NewLength < Hive->Storage[i].Length) {
// There are some free bins to truncate.
HvFreeHivePartial(Hive, NewLength, i);
}
}
}
#ifdef _WRITE_PROTECTED_REGISTRY_POOL
VOID
HvpChangeBinAllocation(
PHBIN Bin,
BOOLEAN ReadOnly
)
{
ASSERT(Bin->Signature == HBIN_SIGNATURE);
// Here to call the code to mark the memory pointed by Bin as Read/Write or ReadOnly, depending on the ReadOnly argument
}
VOID
HvpMarkBinReadWrite(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Marks the memory allocated for the bin containing the specified cell as read/write.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - hcell_index of cell
Return Value:
NONE (It should work!)
--*/
{
ULONG Type;
PHMAP_ENTRY Me;
PHBIN Bin;
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
Type = HvGetCellType(Cell);
if ( (Hive->HiveFlags & HIVE_VOLATILE) ||
(Type == Volatile) )
{
// nothing to do on a volatile hive
return;
}
Me = HvpGetCellMap(Hive, Cell);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,Cell);
Bin = (PHBIN)(Me->BinAddress & HMAP_BASE);
HvpChangeBinAllocation(Bin,FALSE);
}
#endif