1872 lines
45 KiB
C
1872 lines
45 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)
|
|||
|
#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);
|
|||
|
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);
|
|||
|
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);
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// quick out for common case where it's already dirty.
|
|||
|
//
|
|||
|
if (DirtySectors==0) {
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
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));
|
|||
|
if ((Hive->DirtyCount==0) && (!(Hive->HiveFlags & HIVE_NOLAZYFLUSH))) {
|
|||
|
CmpLazyFlush();
|
|||
|
}
|
|||
|
Hive->DirtyCount += DirtySectors;
|
|||
|
RtlSetBits(BitMap, First, Last-First+1);
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Write a log.
|
|||
|
//
|
|||
|
if (Hive->Log == TRUE) {
|
|||
|
if (HvpWriteLog(Hive) == FALSE) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Write the primary
|
|||
|
//
|
|||
|
if (HvpDoWriteHive(Hive, HFILE_TYPE_PRIMARY) == FALSE) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Write an alternate
|
|||
|
//
|
|||
|
if (Hive->Alternate == TRUE) {
|
|||
|
if (HvpDoWriteHive(Hive, HFILE_TYPE_ALTERNATE) == FALSE) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
|
|||
|
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;
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
FileType,
|
|||
|
&Offset,
|
|||
|
(PVOID)BaseBlock,
|
|||
|
HSECTOR_SIZE * Hive->Cluster
|
|||
|
);
|
|||
|
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);
|
|||
|
Address = (PUCHAR)Me->BlockAddress;
|
|||
|
Length = Hive->Cluster * HSECTOR_SIZE;
|
|||
|
Bin = (PHBIN)Address;
|
|||
|
Bin->TimeStamp = BaseBlock->TimeStamp;
|
|||
|
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
FileType,
|
|||
|
&Offset,
|
|||
|
(PVOID)Address,
|
|||
|
Length
|
|||
|
);
|
|||
|
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
|
|||
|
while (HvpFindNextDirtyBlock(
|
|||
|
Hive,
|
|||
|
BitMap,
|
|||
|
&Current,
|
|||
|
&Address,
|
|||
|
&Length,
|
|||
|
&Offset
|
|||
|
) == TRUE)
|
|||
|
{
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
FileType,
|
|||
|
&Offset,
|
|||
|
(PVOID)Address,
|
|||
|
Length
|
|||
|
);
|
|||
|
ASSERT((Offset % (Hive->Cluster * HSECTOR_SIZE)) == 0);
|
|||
|
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;
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
FileType,
|
|||
|
&Offset,
|
|||
|
BaseBlock,
|
|||
|
HSECTOR_SIZE * Hive->Cluster
|
|||
|
);
|
|||
|
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;
|
|||
|
|
|||
|
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;
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
HFILE_TYPE_LOG,
|
|||
|
&Offset,
|
|||
|
BaseBlock,
|
|||
|
HSECTOR_SIZE * Hive->Cluster
|
|||
|
);
|
|||
|
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
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
HFILE_TYPE_LOG,
|
|||
|
&Offset,
|
|||
|
(PVOID)&DirtyVectorSignature,
|
|||
|
sizeof(DirtyVectorSignature)
|
|||
|
);
|
|||
|
if (rc == FALSE) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Length = Hive->DirtyVector.SizeOfBitMap / 8;
|
|||
|
Address = (PUCHAR)(Hive->DirtyVector.Buffer);
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
HFILE_TYPE_LOG,
|
|||
|
&Offset,
|
|||
|
(PVOID)Address,
|
|||
|
Length
|
|||
|
);
|
|||
|
if (rc == FALSE) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
Offset = ROUND_UP(Offset, ClusterSize);
|
|||
|
|
|||
|
//
|
|||
|
// --- Write out body of log ---
|
|||
|
//
|
|||
|
Current = 0;
|
|||
|
while (HvpFindNextDirtyBlock(
|
|||
|
Hive,
|
|||
|
BitMap,
|
|||
|
&Current,
|
|||
|
&Address,
|
|||
|
&Length,
|
|||
|
&junk
|
|||
|
) == TRUE)
|
|||
|
{
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
HFILE_TYPE_LOG,
|
|||
|
&Offset,
|
|||
|
(PVOID)Address,
|
|||
|
Length
|
|||
|
);
|
|||
|
ASSERT((Offset % ClusterSize) == 0);
|
|||
|
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;
|
|||
|
rc = (Hive->FileWrite)(
|
|||
|
Hive,
|
|||
|
HFILE_TYPE_LOG,
|
|||
|
&Offset,
|
|||
|
BaseBlock,
|
|||
|
HSECTOR_SIZE * Hive->Cluster
|
|||
|
);
|
|||
|
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);
|
|||
|
|
|||
|
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);
|
|||
|
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)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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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, AltDirtyVectorSize);
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
//
|
|||
|
// 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)Hive,checkstatus);
|
|||
|
}
|
|||
|
|
|||
|
Hive->RefreshCount++;
|
|||
|
|
|||
|
//
|
|||
|
// 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);
|
|||
|
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)&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);
|
|||
|
ASSERT(t != NULL);
|
|||
|
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;
|
|||
|
}
|
|||
|
} 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)Hive,checkstatus);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// reinit the free list
|
|||
|
//
|
|||
|
for (i = 0; i < HHIVE_FREE_DISPLAY_SIZE; i++) {
|
|||
|
Hive->Storage[Stable].FreeDisplay[i] = HCELL_NIL;
|
|||
|
}
|
|||
|
Hive->Storage[Stable].FreeSummary = 0;
|
|||
|
|
|||
|
//
|
|||
|
// rebuild the free list
|
|||
|
//
|
|||
|
p = 0;
|
|||
|
while (p < Hive->Storage[Stable].Length) {
|
|||
|
t = HvpGetCellMap(Hive, p);
|
|||
|
if (t == NULL) {
|
|||
|
KeBugCheckEx(REGISTRY_ERROR,7,4,(ULONG)Hive,p);
|
|||
|
}
|
|||
|
|
|||
|
if ((t->BinAddress & HMAP_DISCARDABLE) == 0) {
|
|||
|
Bin = (PHBIN)((t->BinAddress) & HMAP_BASE);
|
|||
|
|
|||
|
if ( ! HvpEnlistFreeCells(Hive, Bin, Bin->FileOffset)) {
|
|||
|
KeBugCheckEx(REGISTRY_ERROR,7,5,(ULONG)Bin,Bin->FileOffset);
|
|||
|
}
|
|||
|
|
|||
|
p = (ULONG)p + Bin->Size;
|
|||
|
} else {
|
|||
|
FreeBin = (PFREE_HBIN)t->BlockAddress;
|
|||
|
p = (ULONG)p + FreeBin->Size;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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)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);
|
|||
|
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));
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|