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

1282 lines
34 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
hiveload.c
Abstract:
This module implements procedures to read a hive into memory, applying
logs, etc.
NOTE: Alternate image loading is not supported here, that is
done by the boot loader.
Author:
Bryan M. Willman (bryanwi) 30-Mar-92
Environment:
Revision History:
Dragos C. Sambotin (dragoss) 25-Jan-99
Implementation of bin-size chunk loading of hives.
Dragos C. Sambotin (dragoss) 10-Apr-99
64K IO reads when loading the hive
--*/
#include "cmp.h"
#define IO_BUFFER_SIZE 0x10000 //64K
typedef enum _RESULT {
NotHive,
Fail,
NoMemory,
HiveSuccess,
RecoverHeader,
RecoverData
} RESULT;
RESULT
HvpGetHiveHeader(
PHHIVE Hive,
PHBASE_BLOCK *BaseBlock,
PLARGE_INTEGER TimeStamp
);
RESULT
HvpGetLogHeader(
PHHIVE Hive,
PHBASE_BLOCK *BaseBlock,
PLARGE_INTEGER TimeStamp
);
RESULT
HvpRecoverData(
PHHIVE Hive,
BOOLEAN ReadOnly,
PHCELL_INDEX TailDisplay OPTIONAL
);
NTSTATUS
HvpReadFileImageAndBuildMap(
PHHIVE Hive,
ULONG Length,
PHCELL_INDEX TailDisplay OPTIONAL
);
VOID
HvpDelistBinFreeCells(
PHHIVE Hive,
PHBIN Bin,
HSTORAGE_TYPE Type,
PHCELL_INDEX TailDisplay OPTIONAL
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,HvLoadHive)
#pragma alloc_text(PAGE,HvpGetHiveHeader)
#pragma alloc_text(PAGE,HvpGetLogHeader)
#pragma alloc_text(PAGE,HvpRecoverData)
#pragma alloc_text(PAGE,HvpReadFileImageAndBuildMap)
#endif
extern struct {
PHHIVE Hive;
ULONG Status;
ULONG Space;
HCELL_INDEX MapPoint;
PHBIN BinPoint;
} HvCheckHiveDebug;
NTSTATUS
HvLoadHive(
PHHIVE Hive,
PHCELL_INDEX TailDisplay OPTIONAL
)
/*++
Routine Description:
Hive must be fully initialized, in particular, file handles
must be set up. This routine is not intended for loading hives
from images already in memory.
This routine will apply whatever fixes are available for errors
in the hive image. In particular, if a log exists, and is applicable,
this routine will automatically apply it.
ALGORITHM:
call HvpGetHiveHeader()
if (NoMemory or NoHive)
return failure
if (RecoverData or RecoverHeader) and (no log)
return falure
if (RecoverHeader)
call HvpGetLogHeader
if (fail)
return failure
fix up baseblock
Read Data
if (RecoverData or RecoverHeader)
HvpRecoverData
return STATUS_REGISTRY_RECOVERED
clean up sequence numbers
return success OR STATUS_REGISTRY_RECOVERED
If STATUS_REGISTRY_RECOVERED is returned, then
If (Log) was used, DirtyVector and DirtyCount are set,
caller is expected to flush the changes (using a
NEW log file)
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
TailDisplay - array containing the tail ends of the free cell lists - optional
Return Value:
STATUS:
STATUS_INSUFFICIENT_RESOURCES - memory alloc failure, etc
STATUS_NOT_REGISTRY_FILE - bad signatures and the like
STATUS_REGISTRY_CORRUPT - bad signatures in the log,
bad stuff in both in alternate,
inconsistent log
STATUS_REGISTRY_IO_FAILED - data read failed
STATUS_RECOVERED - successfully recovered the hive,
a semi-flush of logged data
is necessary.
STATUS_SUCCESS - it worked, no recovery needed
--*/
{
PHBASE_BLOCK BaseBlock;
ULONG result1;
ULONG result2;
NTSTATUS status;
LARGE_INTEGER TimeStamp;
ULONG FileOffset;
PHBIN pbin;
BOOLEAN ReadOnlyFlagCopy;
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
BaseBlock = NULL;
result1 = HvpGetHiveHeader(Hive, &BaseBlock, &TimeStamp);
// bomb out for total errors
if (result1 == NoMemory) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit1;
}
if (result1 == NotHive) {
status = STATUS_NOT_REGISTRY_FILE;
goto Exit1;
}
ReadOnlyFlagCopy = Hive->ReadOnly;
// if recovery needed, and no log, bomb out
if ( ((result1 == RecoverData) ||
(result1 == RecoverHeader)) )
{
// recovery needed
if(Hive->Log == FALSE)
{
// no log ==> bomb out
status = STATUS_REGISTRY_CORRUPT;
goto Exit1;
} else {
// TRICK: simulate hive as read-only; Free cells will not
// be enlisted in HvpReadFileImageAndBuildMap; Instead, they
// will be enlisted in HvpRecoverData, when we are sure we have
// the right info loaded up into memory
Hive->ReadOnly = TRUE;
}
}
// need to recover header using log, so try to get it from log
if (result1 == RecoverHeader) {
result2 = HvpGetLogHeader(Hive, &BaseBlock, &TimeStamp);
if (result2 == NoMemory) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit1;
}
if (result2 == Fail) {
status = STATUS_REGISTRY_CORRUPT;
goto Exit1;
}
BaseBlock->Type = HFILE_TYPE_PRIMARY;
}
Hive->BaseBlock = BaseBlock;
Hive->Version = Hive->BaseBlock->Minor;
// at this point, we have a sane baseblock. we may or may not still
// need to apply data recovery
status = HvpReadFileImageAndBuildMap(Hive,BaseBlock->Length,TailDisplay);
// if STATUS_REGISTRY_CORRUPT and RecoverData don't bail out, keep recovering
if( !NT_SUCCESS(status) && ((status != STATUS_REGISTRY_CORRUPT) || (result1 != RecoverData)) ) {
goto Exit2;
}
// apply data recovery if we need it
status = STATUS_SUCCESS;
if ( (result1 == RecoverHeader) || // -> implies recover data
(result1 == RecoverData) )
{
// recover data will enllist the free cells as well and
// will restore the original read-only state of the hive
result2 = HvpRecoverData(Hive,ReadOnlyFlagCopy,TailDisplay);
if (result2 == NoMemory) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit2;
}
if (result2 == Fail) {
status = STATUS_REGISTRY_CORRUPT;
goto Exit2;
}
status = STATUS_REGISTRY_RECOVERED;
}
BaseBlock->Sequence2 = BaseBlock->Sequence1;
return status;
Exit2:
// Clean up the bins already allocated
HvpFreeAllocatedBins( Hive );
// Clean up the directory table
HvpCleanMap( Hive );
Exit1:
if (BaseBlock != NULL) {
(Hive->Free)(BaseBlock, sizeof(HBASE_BLOCK));
}
Hive->BaseBlock = NULL;
Hive->DirtyCount = 0;
return status;
}
NTSTATUS
HvpReadFileImageAndBuildMap(
PHHIVE Hive,
ULONG Length,
PHCELL_INDEX TailDisplay OPTIONAL
)
/*++
Routine Description:
Read the hive from the file and allocate storage for the hive
image in chunks of HBINs. Build the hive map "on the fly".
Optimized to read chunks of 64K from the file.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Length - the length of the hive, in bytes
TailDisplay - array containing the tail ends of the free cell lists - optional
Return Value:
STATUS:
STATUS_INSUFFICIENT_RESOURCES - memory alloc failure, etc
STATUS_REGISTRY_IO_FAILED - data read failed
STATUS_REGISTRY_CORRUPT - base block is corrupt
STATUS_SUCCESS - it worked
--*/
{
ULONG FileOffset;
NTSTATUS Status = STATUS_SUCCESS;
PHBIN Bin; // current bin
ULONG BinSize; // size of the current bin
ULONG BinOffset; // current offset inside current bin
ULONG BinFileOffset; // physical offset of the bin in the file (used for consistency checking)
ULONG BinDataInBuffer;// the amount of data needed to be copied in the current bin available in the buffer
ULONG BinDataNeeded; //
PUCHAR IOBuffer;
ULONG IOBufferSize; // valid data in IOBuffer (only at the end of the file this is different than IO_BUFFER_SIZE)
ULONG IOBufferOffset; // current offset inside IOBuffer
// Init the map
Status = HvpInitMap(Hive);
if( !NT_SUCCESS(Status) ) {
// return failure
return Status;
}
// Allocate a IO_BUFFER_SIZE for I/O operations from paged pool.
// It will be freed at the end of the function.
IOBuffer = (PUCHAR)ExAllocatePool(PagedPool, IO_BUFFER_SIZE);
if (IOBuffer == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
HvpCleanMap( Hive );
return Status;
}
// Start right after the hive header
FileOffset = HBLOCK_SIZE;
BinFileOffset = FileOffset;
Bin = NULL;
// outer loop : reads IO_BUFFER_SIZE chunks from the file
while( FileOffset < (Length + HBLOCK_SIZE) ) {
// we are at the begining of the IO buffer
IOBufferOffset = 0;
// the buffer size will be either IO_BufferSize, or the amount
// uread from the file (when this is smaller than IO_BUFFER_SIZE)
IOBufferSize = Length + HBLOCK_SIZE - FileOffset;
IOBufferSize = ( IOBufferSize > IO_BUFFER_SIZE ) ? IO_BUFFER_SIZE : IOBufferSize;
ASSERT( (IOBufferSize % HBLOCK_SIZE) == 0 );
// read data from the file
if ( ! (Hive->FileRead)(
Hive,
HFILE_TYPE_PRIMARY,
&FileOffset,
(PVOID)IOBuffer,
IOBufferSize
)
)
{
Status = STATUS_REGISTRY_IO_FAILED;
goto ErrorExit;
}
// inner loop: breaks the buffer into bins
while( IOBufferOffset < IOBufferSize ) {
if( Bin == NULL ) {
// this is the beginning of a new bin
// perform bin validation and allocate the bin
// temporary bin points to the current location inside the buffer
Bin = (PHBIN)(IOBuffer + IOBufferOffset);
// Check the validity of the bin header
BinSize = Bin->Size;
if ( (BinSize > Length) ||
(BinSize < HBLOCK_SIZE) ||
(Bin->Signature != HBIN_SIGNATURE) ||
(Bin->FileOffset != (BinFileOffset - HBLOCK_SIZE) )) {
// Bin is bogus
Bin = (PHBIN)(Hive->Allocate)(HBLOCK_SIZE, TRUE);
if (Bin == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
// copy the data already read in the first HBLOCK of the bin
RtlCopyMemory(Bin,(IOBuffer + IOBufferOffset), HBLOCK_SIZE);
Status = STATUS_REGISTRY_CORRUPT;
HvCheckHiveDebug.Hive = Hive;
HvCheckHiveDebug.Status = 0xA001;
HvCheckHiveDebug.Space = Length;
HvCheckHiveDebug.MapPoint = BinFileOffset;
HvCheckHiveDebug.BinPoint = Bin;
//goto ErrorExit;
// DO NOT EXIT; Fix this bin header and go on. RecoverData should fix it.
// If not, CmCheckRegistry called later will prevent loading of an invalid hive
// NOTE: Still, mess the signature, to make sure that if this particular bin doesn't get recovered,
// we'll fail the hive loading request.
Bin->Signature = 0; //TRICK!!!!
BinSize = Bin->Size = HBLOCK_SIZE;
Bin->FileOffset = BinFileOffset - HBLOCK_SIZE;
// simulate as the entire bin is a used cell
((PHCELL)((PUCHAR)Bin + sizeof(HBIN)))->Size = sizeof(HBIN) - BinSize; //TRICK!!!!
// Now that we have the entire bin in memory, Enlist It!
Status = HvpEnlistBinInMap(Hive, Length, Bin, BinFileOffset - HBLOCK_SIZE, TailDisplay);
if( !NT_SUCCESS(Status) ) {
goto ErrorExit;
}
// Adjust the offsets
BinFileOffset += Bin->Size;
IOBufferOffset += Bin->Size;
// another bin is on his way
Bin = NULL;
} else {
// bin is valid; allocate a pool chunk of the right size
Bin = (PHBIN)(Hive->Allocate)(BinSize, TRUE);
if (Bin == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
// the chunk is allocated; set the offset inside the bin and continue
// the next iteration of the inner loop will start by copying data in this bin
BinOffset = 0;
}
} else {
// if we are here, the bin is allocated, the BinSize and BinOffset are set
// We have to calculate how much for this bin is available in the buffer,
// and copy it. If we finished with this bin, enlist it and mark the begining of a new one
ASSERT( Bin != NULL );
BinDataInBuffer = (IOBufferSize - IOBufferOffset);
BinDataNeeded = (BinSize - BinOffset);
if( BinDataInBuffer >= BinDataNeeded ) {
// we have available more than what we need; Finish the bin
RtlCopyMemory(((PUCHAR)Bin + BinOffset),(IOBuffer + IOBufferOffset), BinDataNeeded);
// enlist it
Status = HvpEnlistBinInMap(Hive, Length, Bin, BinFileOffset - HBLOCK_SIZE, TailDisplay);
if( !NT_SUCCESS(Status) ) {
goto ErrorExit;
}
// Adjust the offsets
BinFileOffset += BinSize;
IOBufferOffset += BinDataNeeded;
// mark the begining of a new bin
Bin = NULL;
} else {
// we do not have all bin data in the buffer
// copy what we can
RtlCopyMemory(((PUCHAR)Bin + BinOffset),(IOBuffer + IOBufferOffset), BinDataInBuffer);
// adjust the offsets; this should be the last iteration of the inner loop
BinOffset += BinDataInBuffer;
IOBufferOffset += BinDataInBuffer;
// if we are here, the buffer must have beed exausted
ASSERT( IOBufferOffset == IOBufferSize );
}
}
}
}
// if we got here, we shouldn't have a bin under construction
ASSERT( Bin == NULL );
// Free the buffer used for I/O operations
ExFreePool(IOBuffer);
return Status;
ErrorExit:
// Free the buffer used for I/O operations
ExFreePool(IOBuffer);
return Status;
}
RESULT
HvpGetHiveHeader(
PHHIVE Hive,
PHBASE_BLOCK *BaseBlock,
PLARGE_INTEGER TimeStamp
)
/*++
Routine Description:
Examine the base block sector and possibly the first sector of
the first bin, and decide what (if any) recovery needs to be applied
based on what we find there.
ALGORITHM:
read BaseBlock from offset 0
if ( (I/O error) OR
(checksum wrong) )
{
read bin block from offset HBLOCK_SIZE (4k)
if (2nd I/O error)
return NotHive
}
check bin sign., offset.
if (OK)
return RecoverHeader, TimeStamp=from Link field
} else {
return NotHive
}
}
if (wrong type or signature or version or format)
return NotHive
}
if (seq1 != seq2) {
return RecoverData, TimeStamp=BaseBlock->TimeStamp, valid BaseBlock
}
return ReadData, valid BaseBlock
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
FileType - HFILE_TYPE_PRIMARY or HFILE_TYPE_ALTERNATE - which copy
of the hive to read from.
BaseBlock - supplies pointer to variable to receive pointer to
HBASE_BLOCK, if we can successfully read one.
TimeStamp - pointer to variable to receive time stamp (serial number)
of hive, be it from the baseblock or from the Link field
of the first bin.
Return Value:
RESULT code
--*/
{
PHBASE_BLOCK buffer;
BOOLEAN rc;
ULONG FileOffset;
ULONG Alignment;
ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
// allocate buffer to hold base block
*BaseBlock = NULL;
buffer = (PHBASE_BLOCK)((Hive->Allocate)(sizeof(HBASE_BLOCK), TRUE));
if (buffer == NULL) {
return NoMemory;
}
// 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)buffer & Alignment) != 0) {
(Hive->Free)(buffer, sizeof(HBASE_BLOCK));
buffer = (PHBASE_BLOCK)((Hive->Allocate)(PAGE_SIZE, TRUE));
if (buffer == NULL) {
return NoMemory;
}
}
RtlZeroMemory((PVOID)buffer, sizeof(HBASE_BLOCK));
// attempt to read base block
FileOffset = 0;
rc = (Hive->FileRead)(Hive,
HFILE_TYPE_PRIMARY,
&FileOffset,
(PVOID)buffer,
HSECTOR_SIZE * Hive->Cluster);
if ( (rc == FALSE) ||
(HvpHeaderCheckSum(buffer) != buffer->CheckSum)) {
// base block is toast, try the first block in the first bin
FileOffset = HBLOCK_SIZE;
rc = (Hive->FileRead)(Hive,
HFILE_TYPE_PRIMARY,
&FileOffset,
(PVOID)buffer,
HSECTOR_SIZE * Hive->Cluster);
if ( (rc == FALSE) ||
( ((PHBIN)buffer)->Signature != HBIN_SIGNATURE) ||
( ((PHBIN)buffer)->FileOffset != 0)
)
{
// the bin is toast too, punt
(Hive->Free)(buffer, sizeof(HBASE_BLOCK));
return NotHive;
}
// base block is bogus, but bin is OK, so tell caller
// to look for a log file and apply recovery
*TimeStamp = ((PHBIN)buffer)->TimeStamp;
(Hive->Free)(buffer, sizeof(HBASE_BLOCK));
return RecoverHeader;
}
// base block read OK, but is it valid?
if ( (buffer->Signature != HBASE_BLOCK_SIGNATURE) ||
(buffer->Type != HFILE_TYPE_PRIMARY) ||
(buffer->Major != HSYS_MAJOR) ||
(buffer->Minor > HSYS_MINOR) ||
((buffer->Major == 1) && (buffer->Minor == 0)) ||
(buffer->Format != HBASE_FORMAT_MEMORY)
)
{
// file is simply not a valid hive
(Hive->Free)(buffer, sizeof(HBASE_BLOCK));
return NotHive;
}
// see if recovery is necessary
*BaseBlock = buffer;
*TimeStamp = buffer->TimeStamp;
if ( (buffer->Sequence1 != buffer->Sequence2) ) {
return RecoverData;
}
return HiveSuccess;
}
RESULT
HvpGetLogHeader(
PHHIVE Hive,
PHBASE_BLOCK *BaseBlock,
PLARGE_INTEGER TimeStamp
)
/*++
Routine Description:
Read and validate log file header. Return it if it's valid.
ALGORITHM:
read header
if ( (I/O error) or
(wrong signature,
wrong type,
seq mismatch
wrong checksum,
wrong timestamp
)
return Fail
}
return baseblock, OK
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
BaseBlock - supplies pointer to variable to receive pointer to
HBASE_BLOCK, if we can successfully read one.
TimeStamp - pointer to variable holding TimeStamp, which must
match the one in the log file.
Return Value:
RESULT
--*/
{
PHBASE_BLOCK buffer;
BOOLEAN rc;
ULONG FileOffset;
ASSERT(sizeof(HBASE_BLOCK) == HBLOCK_SIZE);
ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
// allocate buffer to hold base block
*BaseBlock = NULL;
buffer = (PHBASE_BLOCK)((Hive->Allocate)(sizeof(HBASE_BLOCK), TRUE));
if (buffer == NULL) {
return NoMemory;
}
RtlZeroMemory((PVOID)buffer, HSECTOR_SIZE);
// attempt to read base block
FileOffset = 0;
rc = (Hive->FileRead)(Hive,
HFILE_TYPE_LOG,
&FileOffset,
(PVOID)buffer,
HSECTOR_SIZE * Hive->Cluster);
if ( (rc == FALSE) ||
(buffer->Signature != HBASE_BLOCK_SIGNATURE) ||
(buffer->Type != HFILE_TYPE_LOG) ||
(buffer->Sequence1 != buffer->Sequence2) ||
(HvpHeaderCheckSum(buffer) != buffer->CheckSum) ||
(TimeStamp->LowPart != buffer->TimeStamp.LowPart) ||
(TimeStamp->HighPart != buffer->TimeStamp.HighPart)) {
// Log is unreadable, invalid, or doesn't apply the right hive
(Hive->Free)(buffer, sizeof(HBASE_BLOCK));
return Fail;
}
*BaseBlock = buffer;
return HiveSuccess;
}
RESULT
HvpRecoverData(
PHHIVE Hive,
BOOLEAN ReadOnly,
PHCELL_INDEX TailDisplay OPTIONAL
)
/*++
Routine Description:
Apply the corrections in the log file to the hive memory image.
ALGORITHM:
compute size of dirty vector
read in dirty vector
if (i/o error)
return Fail
skip first cluster of data (already processed as log)
sweep vector, looking for runs of bits
address of first bit is used to compute memory offset
length of run is length of block to read
assert always a cluster multiple
file offset kept by running counter
read
if (i/o error)
return fail
return success
NOTE: It is assumed that the data part of the Hive has been
read into a single contiguous block, at Image.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
ReadOnly - by the time this function is called, the hive is forced to the
ready-only state. At the end, if recovery goes OK, we restore the
hive at it's original state, and enlist all free cells.
TailDisplay - array containing the tail ends of the free cell lists - optional
Return Value:
RESULT
--*/
{
ULONG Cluster;
ULONG ClusterSize;
ULONG VectorSize;
PULONG Vector;
ULONG FileOffset;
BOOLEAN rc;
ULONG Current;
ULONG Start;
ULONG End;
ULONG Address;
PUCHAR MemoryBlock;
RTL_BITMAP BitMap;
ULONG Length;
ULONG DirtyVectorSignature = 0;
ULONG i;
PHMAP_ENTRY Me;
PHBIN Bin;
PHBIN NewBin;
PUCHAR SectorImage;
PUCHAR Source;
PHBIN SourceBin;
ULONG SectorOffsetInBin;
ULONG SectorOffsetInBlock;
ULONG BlockOffsetInBin;
ULONG NumberOfSectors;
// compute size of dirty vector, read and check signature, read vector
Cluster = Hive->Cluster;
ClusterSize = Cluster * HSECTOR_SIZE;
Length = Hive->BaseBlock->Length;
VectorSize = (Length / HSECTOR_SIZE) / 8; // VectorSize == Bytes
FileOffset = ClusterSize;
// get and check signature
rc = (Hive->FileRead)(
Hive,
HFILE_TYPE_LOG,
&FileOffset,
(PVOID)&DirtyVectorSignature,
sizeof(DirtyVectorSignature)
);
if (rc == FALSE) {
return Fail;
}
if (DirtyVectorSignature != HLOG_DV_SIGNATURE) {
return Fail;
}
// get the actual vector
Vector = (PULONG)((Hive->Allocate)(ROUND_UP(VectorSize,sizeof(ULONG)), TRUE));
if (Vector == NULL) {
return NoMemory;
}
rc = (Hive->FileRead)(
Hive,
HFILE_TYPE_LOG,
&FileOffset, // dirty vector right after header
(PVOID)Vector,
VectorSize
);
if (rc == FALSE) {
(Hive->Free)(Vector, VectorSize);
return Fail;
}
FileOffset = ROUND_UP(FileOffset, ClusterSize);
// step through the diry map, reading in the corresponding file bytes
Current = 0;
VectorSize = VectorSize * 8; // VectorSize == bits
RtlInitializeBitMap(&BitMap, Vector, VectorSize);
while (Current < VectorSize) {
// find next contiguous block of entries to read in
for (i = Current; i < VectorSize; i++) {
if (RtlCheckBit(&BitMap, i) == 1) {
break;
}
}
Start = i;
for ( ; i < VectorSize; i++) {
if (RtlCheckBit(&BitMap, i) == 0) {
break;
}
}
End = i;
Current = End;
// Start == number of 1st sector, End == number of Last sector + 1
Length = (End - Start) * HSECTOR_SIZE;
if( 0 == Length ) {
// no more dirty blocks.
break;
}
// allocate a buffer to read the whole run from the file; This is a temporary
// block that'll be freed immediately, so don't charge quota for it.
MemoryBlock = (PUCHAR)ExAllocatePoolWithTag(PagedPool, Length, CM_POOL_TAG);
if( MemoryBlock == NULL ) {
goto ErrorExit;
}
rc = (Hive->FileRead)(
Hive,
HFILE_TYPE_LOG,
&FileOffset,
(PVOID)MemoryBlock,
Length
);
ASSERT((FileOffset % ClusterSize) == 0);
if (rc == FALSE) {
ExFreePool(MemoryBlock);
goto ErrorExit;
}
Source = MemoryBlock;
// copy recovered data in the right locations inside the in-memory bins
while( Start < End ) {
Address = Start * HSECTOR_SIZE;
Me = HvpGetCellMap(Hive, Address);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,Address);
Bin = (PHBIN)(Me->BinAddress & HMAP_BASE);
// compute the memory address where data should be copied
SectorOffsetInBin = Address - Bin->FileOffset;
if( ( SectorOffsetInBin == 0 ) && ( ((PHBIN)Source)->Size > Bin->Size ) ){
// Bin in the log file is bigger than the one in memory;
// two or more bins must have been coalesced
ASSERT( Me->BinAddress & HMAP_NEWALLOC );
SourceBin = (PHBIN)Source;
// new bin must have the right offset
ASSERT( Address == SourceBin->FileOffset );
ASSERT( SourceBin->Signature == HBIN_SIGNATURE );
// entire bin should be dirty
ASSERT( (SourceBin->FileOffset + SourceBin->Size) <= End * HSECTOR_SIZE );
// Allocate the right size for the new bin
NewBin = (PHBIN)(Hive->Allocate)(SourceBin->Size, TRUE);
if (NewBin == NULL) {
goto ErrorExit;
}
// Copy the old data into the new bin and free old bins
while(Bin->FileOffset < (Address + SourceBin->Size)) {
RtlCopyMemory((PUCHAR)NewBin + (Bin->FileOffset - Address),Bin, Bin->Size);
// Do not delist as we didn't enlisted (when hive needs recovery)
//HvpDelistBinFreeCells(Hive,Bin,Stable,TailDisplay);
// Advance to the new bin
if( (Bin->FileOffset + Bin->Size) < Hive->BaseBlock->Length ) {
Me = HvpGetCellMap(Hive, Bin->FileOffset + Bin->Size);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,Bin->FileOffset + Bin->Size);
// Free the old bin
(Hive->Free)(Bin, Bin->Size);
// the new address must be the begining of a new allocation
ASSERT( Me->BinAddress & HMAP_NEWALLOC );
Bin = (PHBIN)(Me->BinAddress & HMAP_BASE);
} else {
// we are at the end of the hive here; just break out of the loop
ASSERT( (Address + SourceBin->Size) == Hive->BaseBlock->Length );
ASSERT( (Bin->FileOffset + Bin->Size) == Hive->BaseBlock->Length );
// Free the old bin
(Hive->Free)(Bin, Bin->Size);
// debug purposes only
ASSERT( (Bin = NULL) == NULL );
// bail out of while loop
break;
}
}
#if DBG
// validation: bin size increase must come from coalescing of former bins
// (i.e. bins are never split!!!)
if( Bin != NULL ) {
ASSERT( Bin->FileOffset == (Address + SourceBin->Size));
}
#endif
// Now overwrite the modified data !
while( (Address < (SourceBin->FileOffset + SourceBin->Size)) && (Start < End) ) {
RtlCopyMemory((PUCHAR)NewBin + (Address - SourceBin->FileOffset),Source, HSECTOR_SIZE);
// skip to the next sector
Start++;
Source += HSECTOR_SIZE;
Address += HSECTOR_SIZE;
}
// first sector of the new bin is always restaured from the log file!
ASSERT(NewBin->FileOffset == SourceBin->FileOffset);
ASSERT(NewBin->Size == SourceBin->Size);
} else {
// Normal case: sector recovery somewhere in the middle of the bin
// Do not delist as we didn't enlisted (when hive needs recovery)
//HvpDelistBinFreeCells(Hive,Bin,Stable,TailDisplay);
// Offset should fall within bin memory layout
ASSERT( SectorOffsetInBin < Bin->Size );
BlockOffsetInBin = (ULONG)((PUCHAR)Me->BlockAddress - (PUCHAR)Bin);
SectorOffsetInBlock = SectorOffsetInBin - BlockOffsetInBin;
// sanity check; address should be the same relative to eigther begining of the bin or begining of the block
ASSERT(((PUCHAR)Me->BlockAddress + SectorOffsetInBlock) == ((PUCHAR)Bin + SectorOffsetInBin));
SectorImage = (PUCHAR)((PUCHAR)Me->BlockAddress + SectorOffsetInBlock);
// both source and destination should be valid at this point
ASSERT( SectorImage < ((PUCHAR)Bin + Bin->Size) );
ASSERT( Source < (MemoryBlock + Length) );
NumberOfSectors = 0;
while( ( (SectorImage + (NumberOfSectors * HSECTOR_SIZE)) < (PUCHAR)((PUCHAR)Bin + Bin->Size) ) &&
( (Start + NumberOfSectors ) < End ) ) {
// we are still inside the same bin;
// deal with all sectors inside the same bin at once
NumberOfSectors++;
}
// finally, copy the memory
RtlCopyMemory(SectorImage,Source, NumberOfSectors * HSECTOR_SIZE);
NewBin = Bin;
// skip to the next sector
Start += NumberOfSectors;
Source += NumberOfSectors * HSECTOR_SIZE;
}
// rebuild the map anyway
if( !NT_SUCCESS(HvpEnlistBinInMap(Hive, Length, NewBin, NewBin->FileOffset, TailDisplay)) ) {
goto ErrorExit;
}
}
// get rid of the temporary pool
ExFreePool(MemoryBlock);
}
// now, after we have successfully recovered, enlist all free cells
// and restore the hive to it's original state
Hive->ReadOnly = ReadOnly;
if( ReadOnly == FALSE ) {
// no point going through this loop if the hive is read-only
Address = 0;
while( Address < Hive->BaseBlock->Length ) {
Me = HvpGetCellMap(Hive, Address);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,Address);
Bin = (PHBIN)(Me->BinAddress & HMAP_BASE);
ASSERT( Bin->FileOffset == Address );
ASSERT( Bin->Signature == HBIN_SIGNATURE );
// add free cells in the bin to the appropriate free lists
if ( ! HvpEnlistFreeCells(Hive, Bin, Address,TailDisplay)) {
HvCheckHiveDebug.Hive = Hive;
HvCheckHiveDebug.Status = 0xA004;
HvCheckHiveDebug.Space = Bin->Size;
HvCheckHiveDebug.MapPoint = Address;
HvCheckHiveDebug.BinPoint = Bin;
goto ErrorExit;
}
Address += Bin->Size;
}
}
// put correct dirty vector in Hive so that recovered data
// can be correctly flushed
RtlInitializeBitMap(&(Hive->DirtyVector), Vector, VectorSize);
Hive->DirtyCount = RtlNumberOfSetBits(&Hive->DirtyVector);
Hive->DirtyAlloc = VectorSize * 8;
HvMarkDirty(Hive, 0, sizeof(HBIN)); // force header of 1st bin dirty
return HiveSuccess;
ErrorExit:
// free the dirty vector and return failure
(Hive->Free)(Vector, VectorSize);
return Fail;
}