windows-nt/Source/XPSP1/NT/sdktools/vmmreg32/regdblk.c
2020-09-26 16:20:57 +08:00

1318 lines
39 KiB
C

//
// REGDBLK.C
//
// Copyright (C) Microsoft Corporation, 1995
//
#include "pch.h"
DECLARE_DEBUG_COUNT(g_RgDatablockLockCount);
// Don't let a FREE_RECORD shrink less than this value.
#define MINIMUM_FREE_RECORD_LENGTH (sizeof(KEY_RECORD) + sizeof(VALUE_RECORD))
//
// RgAllocDatablockInfoBuffers
//
// Allocates the buffers associated with a DATABLOCK_INFO structure. The
// size of the datablock buffer is determined by the BlockSize member.
//
int
INTERNAL
RgAllocDatablockInfoBuffers(
LPDATABLOCK_INFO lpDatablockInfo
)
{
lpDatablockInfo-> lpDatablockHeader = (LPDATABLOCK_HEADER)
RgAllocMemory(lpDatablockInfo-> BlockSize);
if (!IsNullPtr(lpDatablockInfo-> lpDatablockHeader)) {
lpDatablockInfo-> lpKeyRecordTable = (LPKEY_RECORD_TABLE_ENTRY)
RgSmAllocMemory(sizeof(KEY_RECORD_TABLE_ENTRY) *
KEY_RECORDS_PER_DATABLOCK);
if (!IsNullPtr(lpDatablockInfo-> lpKeyRecordTable))
return ERROR_SUCCESS;
RgFreeDatablockInfoBuffers(lpDatablockInfo);
}
return ERROR_OUTOFMEMORY;
}
//
// RgFreeDatablockInfoBuffers
//
// Frees the buffers associated with a DATABLOCK_INFO structure.
//
VOID
INTERNAL
RgFreeDatablockInfoBuffers(
LPDATABLOCK_INFO lpDatablockInfo
)
{
if (!IsNullPtr(lpDatablockInfo-> lpDatablockHeader)) {
RgFreeMemory(lpDatablockInfo-> lpDatablockHeader);
lpDatablockInfo-> lpDatablockHeader = NULL;
}
if (!IsNullPtr(lpDatablockInfo-> lpKeyRecordTable)) {
RgSmFreeMemory(lpDatablockInfo-> lpKeyRecordTable);
lpDatablockInfo-> lpKeyRecordTable = NULL;
}
}
//
// RgBuildKeyRecordTable
//
// Builds a KEY_RECORD index table for the given datablock.
//
// A datablock consists of a header followed by a series of variable-sized
// KEY_RECORDs, each with a unique id. To make lookups fast, an index table is
// used to map from the unique id to that KEY_RECORD's location.
//
// As we walk over each KEY_RECORD, we do checks to validate the structure of
// the datablock, so the error code should be checked for corruption.
//
int
INTERNAL
RgBuildKeyRecordTable(
LPDATABLOCK_INFO lpDatablockInfo
)
{
LPDATABLOCK_HEADER lpDatablockHeader;
UINT Offset;
UINT BytesRemaining;
LPKEY_RECORD lpKeyRecord;
DWORD DatablockAddress;
ZeroMemory(lpDatablockInfo-> lpKeyRecordTable,
sizeof(KEY_RECORD_TABLE_ENTRY) * KEY_RECORDS_PER_DATABLOCK);
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
Offset = sizeof(DATABLOCK_HEADER);
BytesRemaining = lpDatablockInfo-> BlockSize - sizeof(DATABLOCK_HEADER);
while (BytesRemaining) {
lpKeyRecord = (LPKEY_RECORD) ((LPBYTE) lpDatablockHeader + Offset);
DatablockAddress = lpKeyRecord-> DatablockAddress;
if ((lpKeyRecord-> AllocatedSize == 0) || (lpKeyRecord-> AllocatedSize >
BytesRemaining) || ((DatablockAddress != REG_NULL) &&
(LOWORD(DatablockAddress) >= KEY_RECORDS_PER_DATABLOCK))) {
TRACE(("RgBuildKeyRecordTable: invalid key record detected\n"));
TRACE(("lpdh=%x\n", lpDatablockHeader));
TRACE(("lpkr=%x\n", lpKeyRecord));
TRACE(("as=%x\n", lpKeyRecord-> AllocatedSize));
TRACE(("br=%x\n", BytesRemaining));
TRACE(("dba=%x\n", DatablockAddress));
TRAP();
return ERROR_BADDB;
}
if (DatablockAddress != REG_NULL) {
lpDatablockInfo-> lpKeyRecordTable[LOWORD(DatablockAddress)] =
(KEY_RECORD_TABLE_ENTRY) Offset;
}
Offset += SmallDword(lpKeyRecord-> AllocatedSize);
BytesRemaining -= SmallDword(lpKeyRecord-> AllocatedSize);
}
return ERROR_SUCCESS;
}
//
// RgLockDatablock
//
// Locks the specified datablock in memory, indicating that it is about to be
// used. If the datablock is not currently in memory, then it is brought in.
// Unlocked datablocks are freed as necessary to make room for this new
// datablock.
//
// IMPORTANT: Locking a datablock only means that it's guaranteed to be kept
// in memory. It does not mean that pointers contained in a DATABLOCK_INFO
// structure will remain the same: routines that could change the
// DATABLOCK_INFO pointers are labeled "IMPORTANT" as well.
//
// lpFileInfo, registry file containing the datablock.
// BlockIndex, index of the datablock.
//
int
INTERNAL
RgLockDatablock(
LPFILE_INFO lpFileInfo,
UINT BlockIndex
)
{
int ErrorCode;
LPDATABLOCK_INFO lpDatablockInfo;
HFILE hFile = HFILE_ERROR;
if (BlockIndex >= lpFileInfo-> FileHeader.BlockCount) {
TRACE(("RgLockDatablock: invalid datablock number\n"));
return ERROR_BADDB;
}
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
//
// Is the datablock currently in memory?
//
if (!(lpDatablockInfo-> Flags & DIF_PRESENT)) {
NOISE(("RgLockDatablock: "));
NOISE((lpFileInfo-> FileName));
NOISE((", block %d\n", BlockIndex));
ASSERT(lpDatablockInfo-> FileOffset != -1);
if ((ErrorCode = RgAllocDatablockInfoBuffers(lpDatablockInfo)) !=
ERROR_SUCCESS)
goto CleanupAfterError;
NOISE((" lpDatablockHeader=%x\n", lpDatablockInfo-> lpDatablockHeader));
NOISE((" lpKeyRecordTable=%x\n", lpDatablockInfo-> lpKeyRecordTable));
if ((hFile = RgOpenFile(lpFileInfo-> FileName, OF_READ)) == HFILE_ERROR)
goto CleanupAfterFileError;
if (!RgSeekFile(hFile, lpDatablockInfo-> FileOffset))
goto CleanupAfterFileError;
if (!RgReadFile(hFile, lpDatablockInfo-> lpDatablockHeader,
(UINT) lpDatablockInfo-> BlockSize))
goto CleanupAfterFileError;
if (!RgIsValidDatablockHeader(lpDatablockInfo-> lpDatablockHeader)) {
ErrorCode = ERROR_BADDB;
goto CleanupAfterError;
}
if ((ErrorCode = RgBuildKeyRecordTable(lpDatablockInfo)) !=
ERROR_SUCCESS)
goto CleanupAfterError;
RgCloseFile(hFile);
}
lpDatablockInfo-> Flags |= (DIF_ACCESSED | DIF_PRESENT);
lpDatablockInfo-> LockCount++;
INCREMENT_DEBUG_COUNT(g_RgDatablockLockCount);
return ERROR_SUCCESS;
CleanupAfterFileError:
ErrorCode = ERROR_REGISTRY_IO_FAILED;
CleanupAfterError:
if (hFile != HFILE_ERROR)
RgCloseFile(hFile);
RgFreeDatablockInfoBuffers(lpDatablockInfo);
DEBUG_OUT(("RgLockDatablock() returning error %d\n", ErrorCode));
return ErrorCode;
}
//
// RgUnlockDatablock
//
// Unlocks the datablock, indicating that the datablock is no longer in active
// use. After a datablock has been unlocked, the datablock may be freed after
// flushing to disk if dirty.
//
VOID
INTERNAL
RgUnlockDatablock(
LPFILE_INFO lpFileInfo,
UINT BlockIndex,
BOOL fMarkDirty
)
{
LPDATABLOCK_INFO lpDatablockInfo;
ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
ASSERT(lpDatablockInfo-> LockCount > 0);
lpDatablockInfo-> LockCount--;
if (fMarkDirty) {
lpDatablockInfo-> Flags |= DIF_DIRTY;
lpFileInfo-> Flags |= FI_DIRTY;
RgDelayFlush();
}
DECREMENT_DEBUG_COUNT(g_RgDatablockLockCount);
}
//
// RgLockKeyRecord
//
// Wraps RgLockDatablock, returning the address of the specified KEY_RECORD
// structure.
//
int
INTERNAL
RgLockKeyRecord(
LPFILE_INFO lpFileInfo,
UINT BlockIndex,
BYTE KeyRecordIndex,
LPKEY_RECORD FAR* lplpKeyRecord
)
{
int ErrorCode;
LPDATABLOCK_INFO lpDatablockInfo;
if ((ErrorCode = RgLockDatablock(lpFileInfo, BlockIndex)) ==
ERROR_SUCCESS) {
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
if (IsNullKeyRecordTableEntry(lpDatablockInfo->
lpKeyRecordTable[KeyRecordIndex])) {
RgUnlockDatablock(lpFileInfo, BlockIndex, FALSE);
TRACE(("RgLockKeyRecord: invalid datablock address %x:%x\n",
BlockIndex, KeyRecordIndex));
ErrorCode = ERROR_BADDB;
}
else {
*lplpKeyRecord = RgIndexKeyRecordPtr(lpDatablockInfo,
KeyRecordIndex);
}
}
return ErrorCode;
}
//
// RgCompactDatablock
//
// Compacts the datablock by pushing all KEY_RECORDS together and leaving a
// single FREEKEY_RECORD at the end.
//
// The datablock must be marked dirty by the caller, if desired.
//
// Returns TRUE if any action was taken.
//
BOOL
INTERNAL
RgCompactDatablock(
LPDATABLOCK_INFO lpDatablockInfo
)
{
LPDATABLOCK_HEADER lpDatablockHeader;
LPFREEKEY_RECORD lpFreeKeyRecord;
LPBYTE lpSource;
LPBYTE lpDestination;
UINT Offset;
UINT BlockSize;
UINT BytesToPushDown;
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
// Only need to compact if there's a free record in this datablock.
if (lpDatablockHeader-> FirstFreeOffset == REG_NULL)
return FALSE;
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
SmallDword(lpDatablockHeader-> FirstFreeOffset));
// Only need to compact if the all the free bytes aren't already at the end
// of the datablock (datablocks can't be greater than 64K-1, so no overflow
// is possible).
if ((SmallDword(lpDatablockHeader-> FirstFreeOffset) +
SmallDword(lpFreeKeyRecord-> AllocatedSize) >= lpDatablockInfo->
BlockSize) && (lpFreeKeyRecord-> NextFreeOffset == REG_NULL))
return FALSE;
NOISE(("RgCompactDatablock: block %d\n", lpDatablockHeader-> BlockIndex));
lpSource = NULL;
lpDestination = NULL;
Offset = sizeof(DATABLOCK_HEADER);
BlockSize = lpDatablockInfo-> BlockSize;
while (Offset < BlockSize) {
// Advance to the next free record or the end of the block.
for (;;) {
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
Offset);
if (Offset >= BlockSize || IsKeyRecordFree(lpFreeKeyRecord)) {
//
// If lpSource is valid, then we can push down the bytes from
// lpSource through lpFreeKeyRecord to lpDestination.
//
if (!IsNullPtr(lpSource)) {
BytesToPushDown = (LPBYTE) lpFreeKeyRecord -
(LPBYTE) lpSource;
MoveMemory(lpDestination, lpSource, BytesToPushDown);
lpDestination += BytesToPushDown;
}
if (IsNullPtr(lpDestination))
lpDestination = (LPBYTE) lpFreeKeyRecord;
break;
}
Offset += SmallDword(lpFreeKeyRecord-> AllocatedSize);
}
// Advance to the next key record.
while (Offset < BlockSize) {
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
Offset);
if (!IsKeyRecordFree(lpFreeKeyRecord)) {
lpSource = (LPBYTE) lpFreeKeyRecord;
break;
}
Offset += SmallDword(lpFreeKeyRecord-> AllocatedSize);
}
}
// lpDestination now points at the end of the datablock where the giant
// free record is to be placed. Initialize this record and patch up the
// datablock header.
lpDatablockHeader-> FirstFreeOffset = (LPBYTE) lpDestination -
(LPBYTE) lpDatablockHeader;
((LPFREEKEY_RECORD) lpDestination)-> AllocatedSize = lpDatablockInfo->
FreeBytes;
((LPFREEKEY_RECORD) lpDestination)-> DatablockAddress = REG_NULL;
((LPFREEKEY_RECORD) lpDestination)-> NextFreeOffset = REG_NULL;
// The key record table is now invalid, so we must refresh its contents.
RgBuildKeyRecordTable(lpDatablockInfo);
return TRUE;
}
//
// RgCreateDatablock
//
// Creates a new datablock at the end of the file of the specified length (plus
// padding to align the block).
//
// The datablock is locked, so RgUnlockDatablock must be called on the last
// datablock in the file.
//
int
INTERNAL
RgCreateDatablock(
LPFILE_INFO lpFileInfo,
UINT Length
)
{
UINT BlockCount;
LPDATABLOCK_INFO lpDatablockInfo;
LPDATABLOCK_HEADER lpDatablockHeader;
LPFREEKEY_RECORD lpFreeKeyRecord;
BlockCount = lpFileInfo-> FileHeader.BlockCount;
if (BlockCount >= DATABLOCKS_PER_FILE)
return ERROR_OUTOFMEMORY;
if (BlockCount >= lpFileInfo-> DatablockInfoAllocCount) {
// lpDatablockInfo is too small to hold the info for a new datablock,
// so we must grow it a bit.
if (IsNullPtr((lpDatablockInfo = (LPDATABLOCK_INFO)
RgSmReAllocMemory(lpFileInfo-> lpDatablockInfo, (BlockCount +
DATABLOCK_INFO_SLACK_ALLOC) * sizeof(DATABLOCK_INFO)))))
return ERROR_OUTOFMEMORY;
lpFileInfo-> lpDatablockInfo = lpDatablockInfo;
lpFileInfo-> DatablockInfoAllocCount += DATABLOCK_INFO_SLACK_ALLOC;
}
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockCount);
Length = RgAlignBlockSize(Length + sizeof(DATABLOCK_HEADER));
lpDatablockInfo-> BlockSize = Length;
if (RgAllocDatablockInfoBuffers(lpDatablockInfo) != ERROR_SUCCESS)
return ERROR_OUTOFMEMORY;
lpDatablockInfo-> FreeBytes = Length - sizeof(DATABLOCK_HEADER);
lpDatablockInfo-> FirstFreeIndex = 0;
lpDatablockInfo-> FileOffset = -1; // Set during file flush
lpDatablockInfo-> Flags = DIF_PRESENT | DIF_ACCESSED | DIF_DIRTY;
lpDatablockInfo-> LockCount = 1;
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
lpDatablockHeader-> Signature = DH_SIGNATURE;
lpDatablockHeader-> BlockSize = Length;
lpDatablockHeader-> FreeBytes = lpDatablockInfo-> FreeBytes;
lpDatablockHeader-> Flags = DHF_HASBLOCKNUMBERS;
lpDatablockHeader-> BlockIndex = (WORD) BlockCount;
lpDatablockHeader-> FirstFreeOffset = sizeof(DATABLOCK_HEADER);
lpDatablockHeader-> MaxAllocatedIndex = 0;
// lpDatablockHeader-> FirstFreeIndex is copied back on the flush.
// lpDatablockHeader-> Reserved is worthless because it was randomly set
// to a pointer in the old code.
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
sizeof(DATABLOCK_HEADER));
lpFreeKeyRecord-> AllocatedSize = lpDatablockInfo-> FreeBytes;
lpFreeKeyRecord-> DatablockAddress = REG_NULL;
lpFreeKeyRecord-> NextFreeOffset = REG_NULL;
lpFileInfo-> FileHeader.BlockCount++;
lpFileInfo-> FileHeader.Flags |= FHF_DIRTY;
lpFileInfo-> Flags |= FI_DIRTY | FI_EXTENDED;
RgDelayFlush();
INCREMENT_DEBUG_COUNT(g_RgDatablockLockCount);
// We must initialize the key record table, so we might as well let
// RgBuildKeyRecordTable check the validity of what we just created...
return RgBuildKeyRecordTable(lpDatablockInfo);
}
//
// RgExtendDatablock
//
// Extends the given datablock to the specified size. If successful, then the
// resulting datablock will be compacted with a single FREEKEY_RECORD at the
// end of the datablock which will include the added space.
//
int
INTERNAL
RgExtendDatablock(
LPFILE_INFO lpFileInfo,
UINT BlockIndex,
UINT Length
)
{
LPDATABLOCK_INFO lpDatablockInfo;
DWORD NewBlockSize;
LPDATABLOCK_HEADER lpNewDatablockHeader;
LPFREEKEY_RECORD lpFreeKeyRecord;
ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
ASSERT(lpDatablockInfo-> Flags & DIF_PRESENT);
// Check if enough free bytes already exist: if so, no need to extend.
if (lpDatablockInfo-> FreeBytes >= Length) {
DEBUG_OUT(("RgExtendDatablock: unexpectedly called\n"));
return ERROR_SUCCESS;
}
NewBlockSize = RgAlignBlockSize(lpDatablockInfo-> BlockSize + Length -
lpDatablockInfo-> FreeBytes);
if (NewBlockSize > MAXIMUM_DATABLOCK_SIZE) {
TRACE(("RgExtendDatablock: datablock too big\n"));
return ERROR_OUTOFMEMORY;
}
NOISE(("RgExtendDatablock: block %d\n", BlockIndex));
NOISE(("block size=%x, new block size=%x\n", lpDatablockInfo-> BlockSize,
NewBlockSize));
if (IsNullPtr((lpNewDatablockHeader = (LPDATABLOCK_HEADER)
RgReAllocMemory(lpDatablockInfo-> lpDatablockHeader, (UINT)
NewBlockSize))))
return ERROR_OUTOFMEMORY;
lpDatablockInfo-> lpDatablockHeader = lpNewDatablockHeader;
RgCompactDatablock(lpDatablockInfo);
if (lpNewDatablockHeader-> FirstFreeOffset == REG_NULL) {
lpNewDatablockHeader-> FirstFreeOffset = lpDatablockInfo-> BlockSize;
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpNewDatablockHeader +
SmallDword(lpNewDatablockHeader-> FirstFreeOffset));
lpFreeKeyRecord-> DatablockAddress = REG_NULL;
lpFreeKeyRecord-> NextFreeOffset = REG_NULL;
}
else {
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpNewDatablockHeader +
SmallDword(lpNewDatablockHeader-> FirstFreeOffset));
}
lpDatablockInfo-> FreeBytes += (UINT) NewBlockSize - lpDatablockInfo->
BlockSize;
lpFreeKeyRecord-> AllocatedSize = lpDatablockInfo-> FreeBytes;
lpDatablockInfo-> BlockSize = (UINT) NewBlockSize;
lpDatablockInfo-> Flags |= (DIF_DIRTY | DIF_EXTENDED);
lpFileInfo-> Flags |= FI_DIRTY | FI_EXTENDED;
RgDelayFlush();
return ERROR_SUCCESS;
}
//
// RgAllocKeyRecordFromDatablock
//
// Creates an uninitialized KEY_RECORD of the desired size from the provided
// datablock. On exit, only AllocatedSize is valid.
//
// The datablock referred to by lpDatablockInfo must have been locked to
// guarantee that the its data is actually present. The datablock is not
// dirtied.
//
// IMPORTANT: Any datablock may be relocated as a result of calling this
// routine. All pointers to datablocks should be refetched.
//
int
INTERNAL
RgAllocKeyRecordFromDatablock(
LPFILE_INFO lpFileInfo,
UINT BlockIndex,
UINT Length,
LPKEY_RECORD FAR* lplpKeyRecord
)
{
LPDATABLOCK_INFO lpDatablockInfo;
LPDATABLOCK_HEADER lpDatablockHeader;
LPFREEKEY_RECORD lpFreeKeyRecord;
UINT AllocatedSize;
LPFREEKEY_RECORD lpNewFreeKeyRecord;
UINT ExtraBytes;
ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
ASSERT(lpDatablockInfo-> Flags & DIF_PRESENT);
if (Length > lpDatablockInfo-> FreeBytes)
return ERROR_OUTOFMEMORY;
RgCompactDatablock(lpDatablockInfo);
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
SmallDword(lpDatablockHeader-> FirstFreeOffset));
AllocatedSize = SmallDword(lpFreeKeyRecord-> AllocatedSize);
if (Length > AllocatedSize) {
TRACE(("RgAllocKeyRecordFromDatablock() detected corruption?\n"));
return ERROR_OUTOFMEMORY;
}
ExtraBytes = AllocatedSize - Length;
//
// If we were to break this FREEKEY_RECORD into two records, would the
// second chunk be too small? If so, then don't do it. Just give back
// the full allocated size to the caller.
//
if (ExtraBytes >= MINIMUM_FREE_RECORD_LENGTH) {
lpNewFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpFreeKeyRecord +
Length);
lpDatablockHeader-> FirstFreeOffset += Length;
lpFreeKeyRecord-> AllocatedSize = Length;
// IMPORTANT: Note that lpNewFreeKeyRecord and lpFreeKeyRecord may
// overlap so we have to be careful when changing these fields!
lpNewFreeKeyRecord-> NextFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
lpNewFreeKeyRecord-> DatablockAddress = REG_NULL;
lpNewFreeKeyRecord-> AllocatedSize = ExtraBytes;
}
else {
Length = AllocatedSize;
lpDatablockHeader-> FirstFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
}
// Adjust the number of free bytes in this datablock. At this point,
// Length is equal to the size of the newly formed record.
lpDatablockInfo-> FreeBytes -= Length;
*lplpKeyRecord = (LPKEY_RECORD) lpFreeKeyRecord;
return ERROR_SUCCESS;
}
//
// RgAllocKeyRecordIndex
//
// Allocates a key record index from the provided datablock. If no indexs
// are available in the datablock, then KEY_RECORDS_PER_DATABLOCK is returned.
//
// The datablock referred to by lpDatablockInfo must have been locked to
// guarantee that the its data is actually present. The datablock is not
// dirtied.
//
UINT
INTERNAL
RgAllocKeyRecordIndex(
LPDATABLOCK_INFO lpDatablockInfo
)
{
LPDATABLOCK_HEADER lpDatablockHeader;
UINT KeyRecordIndex;
UINT NextFreeIndex;
LPKEY_RECORD_TABLE_ENTRY lpKeyRecordTableEntry;
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
KeyRecordIndex = lpDatablockInfo-> FirstFreeIndex;
NextFreeIndex = KeyRecordIndex + 1;
ASSERT(KeyRecordIndex < KEY_RECORDS_PER_DATABLOCK);
ASSERT(IsNullKeyRecordTableEntry(lpDatablockInfo->
lpKeyRecordTable[KeyRecordIndex]));
if (KeyRecordIndex > lpDatablockHeader-> MaxAllocatedIndex)
lpDatablockHeader-> MaxAllocatedIndex = (WORD) KeyRecordIndex;
else {
// Find the next free hole in the key record table or leave ourselves
// at the end of the table.
for (lpKeyRecordTableEntry =
&lpDatablockInfo-> lpKeyRecordTable[NextFreeIndex]; NextFreeIndex <=
lpDatablockHeader-> MaxAllocatedIndex; NextFreeIndex++,
lpKeyRecordTableEntry++) {
if (IsNullKeyRecordTableEntry(*lpKeyRecordTableEntry))
break;
}
}
lpDatablockInfo-> FirstFreeIndex = NextFreeIndex;
return KeyRecordIndex;
}
//
// RgAllocKeyRecord
//
//
// IMPORTANT: Any datablock may be relocated as a result of calling this
// routine. All pointers to datablocks should be refetched.
//
int
INTERNAL
RgAllocKeyRecord(
LPFILE_INFO lpFileInfo,
UINT Length,
LPKEY_RECORD FAR* lplpKeyRecord
)
{
BOOL fExtendDatablock;
UINT BlockIndex;
LPDATABLOCK_INFO lpDatablockInfo;
UINT KeyRecordIndex;
if (lpFileInfo-> FileHeader.BlockCount == 0)
goto MakeNewDatablock;
//
// Find a datablock that can satisfy the allocation request. Two passes
// may be made over this routine-- during the second pass, datablocks may
// be extended.
//
fExtendDatablock = FALSE;
DoSecondPass:
BlockIndex = lpFileInfo-> FileHeader.BlockCount;
// We overindex by one, but this gets decremented at the start of the loop.
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
while (BlockIndex--) {
lpDatablockInfo--;
// Are there any more ids available in this datablock?
if (lpDatablockInfo-> FirstFreeIndex >= KEY_RECORDS_PER_DATABLOCK)
continue;
if (fExtendDatablock) {
// Can we grow this datablock without exceeding the maximum size?
if ((DWORD) (lpDatablockInfo-> BlockSize - lpDatablockInfo->
FreeBytes) + Length > MAXIMUM_DATABLOCK_SIZE)
continue;
} else {
// Is there enough free space in this datablock for this record?
if (Length > lpDatablockInfo-> FreeBytes)
continue;
}
if (RgLockDatablock(lpFileInfo, BlockIndex) == ERROR_SUCCESS) {
if (!fExtendDatablock || RgExtendDatablock(lpFileInfo, BlockIndex,
Length) == ERROR_SUCCESS) {
if (RgAllocKeyRecordFromDatablock(lpFileInfo, BlockIndex,
Length, lplpKeyRecord) == ERROR_SUCCESS)
goto AllocatedKeyRecord;
}
RgUnlockDatablock(lpFileInfo, BlockIndex, FALSE);
}
}
// If we haven't already tried to extend some datablock, make another
// pass over the blocks to do so.
if (!fExtendDatablock) {
fExtendDatablock = TRUE;
goto DoSecondPass;
}
//
// No datablock has enough space to satisfy the request, so attempt to
// create a new one at the end of the file.
//
MakeNewDatablock:
if (RgCreateDatablock(lpFileInfo, Length) == ERROR_SUCCESS) {
BlockIndex = lpFileInfo-> FileHeader.BlockCount - 1;
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
if (RgAllocKeyRecordFromDatablock(lpFileInfo, BlockIndex, Length,
lplpKeyRecord) == ERROR_SUCCESS) {
AllocatedKeyRecord:
KeyRecordIndex = RgAllocKeyRecordIndex(lpDatablockInfo);
(*lplpKeyRecord)-> DatablockAddress = MAKELONG(KeyRecordIndex,
BlockIndex);
lpDatablockInfo-> lpKeyRecordTable[KeyRecordIndex] =
(KEY_RECORD_TABLE_ENTRY) ((LPBYTE) (*lplpKeyRecord) -
(LPBYTE) lpDatablockInfo-> lpDatablockHeader);
return ERROR_SUCCESS;
}
RgUnlockDatablock(lpFileInfo, BlockIndex, FALSE);
}
return ERROR_OUTOFMEMORY;
}
//
// RgExtendKeyRecord
//
// Attempts to extend the given KEY_RECORD by combining it with an adjacent
// FREE_RECORD.
//
// The datablock referred to by lpDatablockInfo must have been locked to
// guarantee that the its data is actually present. The datablock is not
// dirtied.
//
// Returns ERROR_SUCCESS if the KEY_RECORD could be extended, else
// ERROR_OUTOFMEMORY.
//
int
INTERNAL
RgExtendKeyRecord(
LPFILE_INFO lpFileInfo,
UINT BlockIndex,
UINT Length,
LPKEY_RECORD lpKeyRecord
)
{
LPDATABLOCK_INFO lpDatablockInfo;
LPDATABLOCK_HEADER lpDatablockHeader;
LPFREEKEY_RECORD lpFreeKeyRecord;
UINT AllocatedSize;
UINT FreeSizeAllocation;
UINT ExtraBytes;
LPFREEKEY_RECORD lpTempFreeKeyRecord;
DWORD NewFreeOffset; // May be REG_NULL
UINT FreeOffset;
DWORD Offset; // May be REG_NULL
ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
AllocatedSize = SmallDword(lpKeyRecord-> AllocatedSize);
lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpKeyRecord +
AllocatedSize);
FreeOffset = (LPBYTE) lpFreeKeyRecord - (LPBYTE) lpDatablockHeader;
// Check if this key record is at the very end of the datablock and that
// lpFreeKeyRecord is really a free key record.
if (FreeOffset >= lpDatablockInfo-> BlockSize ||
!IsKeyRecordFree(lpFreeKeyRecord))
return ERROR_OUTOFMEMORY;
ASSERT(Length >= AllocatedSize);
FreeSizeAllocation = Length - AllocatedSize;
AllocatedSize = SmallDword(lpFreeKeyRecord-> AllocatedSize);
if (FreeSizeAllocation > AllocatedSize)
return ERROR_OUTOFMEMORY;
ExtraBytes = AllocatedSize - FreeSizeAllocation;
//
// If we were to break this FREEKEY_RECORD into two records, would the
// second chunk be too small? If so, then don't do it. Just give back
// the full allocated size to the caller.
//
if (ExtraBytes >= MINIMUM_FREE_RECORD_LENGTH) {
NewFreeOffset = FreeOffset + FreeSizeAllocation;
lpTempFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpFreeKeyRecord +
FreeSizeAllocation);
// IMPORTANT: Note that lpNewFreeKeyRecord and lpFreeKeyRecord may
// overlap so we have to be careful when changing these fields!
lpTempFreeKeyRecord-> NextFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
lpTempFreeKeyRecord-> DatablockAddress = REG_NULL;
lpTempFreeKeyRecord-> AllocatedSize = ExtraBytes;
}
else {
NewFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
// The key record's allocated length will also include all of the extra
// bytes.
FreeSizeAllocation += ExtraBytes;
}
lpKeyRecord-> AllocatedSize += FreeSizeAllocation;
lpDatablockInfo-> FreeBytes -= FreeSizeAllocation;
//
// Unlink the free record that we just extended into and possibly link in
// the new FREEKEY_RECORD if a split occurred.
//
Offset = lpDatablockHeader-> FirstFreeOffset;
if (Offset == FreeOffset) {
lpDatablockHeader-> FirstFreeOffset = NewFreeOffset;
}
else {
while (Offset != REG_NULL) {
lpTempFreeKeyRecord =
(LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
SmallDword(Offset));
Offset = lpTempFreeKeyRecord-> NextFreeOffset;
if (Offset == FreeOffset) {
lpTempFreeKeyRecord-> NextFreeOffset = NewFreeOffset;
break;
}
}
}
return ERROR_SUCCESS;
}
//
// RgFreeKeyRecord
//
// The datablock referred to by lpDatablockInfo must have been locked to
// guarantee that the its data is actually present. The datablock is not
// dirtied.
//
VOID
INTERNAL
RgFreeKeyRecord(
LPDATABLOCK_INFO lpDatablockInfo,
LPKEY_RECORD lpKeyRecord
)
{
LPDATABLOCK_HEADER lpDatablockHeader;
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
((LPFREEKEY_RECORD) lpKeyRecord)-> DatablockAddress = REG_NULL;
((LPFREEKEY_RECORD) lpKeyRecord)-> NextFreeOffset = lpDatablockHeader->
FirstFreeOffset;
lpDatablockHeader-> FirstFreeOffset = (LPBYTE) lpKeyRecord - (LPBYTE)
lpDatablockHeader;
lpDatablockInfo-> FreeBytes += SmallDword(((LPFREEKEY_RECORD) lpKeyRecord)->
AllocatedSize);
}
//
// RgFreeKeyRecordIndex
//
// The datablock referred to by lpDatablockInfo must have been locked to
// guarantee that the its data is actually present. The datablock is not
// dirtied.
//
// We don't bother updated MaxAllocatedIndex because it's only really useful
// if we're always freeing from the maximum index to zero. This is very
// rarely the case, so no point in keeping that test around or touching the
// datablock header page just to do it.
//
VOID
INTERNAL
RgFreeKeyRecordIndex(
LPDATABLOCK_INFO lpDatablockInfo,
UINT KeyRecordIndex
)
{
ASSERT(lpDatablockInfo-> lpDatablockHeader-> MaxAllocatedIndex >=
KeyRecordIndex);
if (lpDatablockInfo-> FirstFreeIndex > KeyRecordIndex)
lpDatablockInfo-> FirstFreeIndex = KeyRecordIndex;
lpDatablockInfo-> lpKeyRecordTable[KeyRecordIndex] =
NULL_KEY_RECORD_TABLE_ENTRY;
}
//
// RgWriteDatablocks
//
// Writes all dirty datablocks to the file specified by the file handle.
//
int
INTERNAL
RgWriteDatablocks(
LPFILE_INFO lpFileInfo,
HFILE hSourceFile,
HFILE hDestinationFile
)
{
UINT BlockIndex;
LPDATABLOCK_INFO lpDatablockInfo;
LPDATABLOCK_HEADER lpDatablockHeader;
LONG FileOffset;
lpDatablockInfo = lpFileInfo-> lpDatablockInfo;
FileOffset = lpFileInfo-> FileHeader.Size;
for (BlockIndex = 0; BlockIndex < lpFileInfo-> FileHeader.BlockCount;
BlockIndex++, lpDatablockInfo++) {
if (lpDatablockInfo-> Flags & DIF_PRESENT) {
// The block is currently in memory. If we're either extending
// the file or the block is dirty, then write out our in-memory
// copy to disk.
if (hSourceFile != HFILE_ERROR || lpDatablockInfo-> Flags &
DIF_DIRTY) {
NOISE(("writing datablock #%d of ", BlockIndex));
NOISE((lpFileInfo-> FileName));
NOISE(("\n"));
lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
// Copy back the fields that we've been maintaining in the
// DATABLOCK_INFO structure.
lpDatablockHeader-> BlockSize = lpDatablockInfo-> BlockSize;
lpDatablockHeader-> FreeBytes = lpDatablockInfo-> FreeBytes;
lpDatablockHeader-> FirstFreeIndex = (WORD) lpDatablockInfo->
FirstFreeIndex;
// The checksum is not currently calculated, so we must clear
// the flag so we don't confuse Win95.
lpDatablockHeader-> Flags &= ~DHF_HASCHECKSUM;
if (!RgSeekFile(hDestinationFile, FileOffset))
return ERROR_REGISTRY_IO_FAILED;
if (!RgWriteFile(hDestinationFile, lpDatablockHeader,
lpDatablockInfo-> BlockSize))
return ERROR_REGISTRY_IO_FAILED;
}
}
else {
// The block is not currently in memory. If we're extending the
// file, then we must write out this datablock. The overhead is
// too great to lock the datablock down, so just copy it from the
// original file to the extended file.
if (hSourceFile != HFILE_ERROR) {
if (RgCopyFileBytes(hSourceFile, lpDatablockInfo-> FileOffset,
hDestinationFile, FileOffset, lpDatablockInfo->
BlockSize) != ERROR_SUCCESS)
return ERROR_REGISTRY_IO_FAILED;
}
}
FileOffset += lpDatablockInfo-> BlockSize;
}
return ERROR_SUCCESS;
}
//
// RgWriteDatablocksComplete
//
// Called after a file has been successfully written. We can now safely clear
// all dirty flags and update our state information with the knowledge that
// the file is in a consistent state.
//
VOID
INTERNAL
RgWriteDatablocksComplete(
LPFILE_INFO lpFileInfo
)
{
UINT BlockIndex;
LPDATABLOCK_INFO lpDatablockInfo;
LONG FileOffset;
lpDatablockInfo = lpFileInfo-> lpDatablockInfo;
FileOffset = lpFileInfo-> FileHeader.Size;
for (BlockIndex = 0; BlockIndex < lpFileInfo-> FileHeader.BlockCount;
BlockIndex++, lpDatablockInfo++) {
lpDatablockInfo-> Flags &= ~DIF_DIRTY;
lpDatablockInfo-> FileOffset = FileOffset;
FileOffset += lpDatablockInfo-> BlockSize;
}
}
//
// RgSweepDatablocks
//
// Makes a pass through all the present datablocks of the given FILE_INFO
// structure and discards datablocks that have not been accessed since the last
// sweep.
//
VOID
INTERNAL
RgSweepDatablocks(
LPFILE_INFO lpFileInfo
)
{
UINT BlocksLeft;
LPDATABLOCK_INFO lpDatablockInfo;
for (BlocksLeft = lpFileInfo-> FileHeader.BlockCount, lpDatablockInfo =
lpFileInfo-> lpDatablockInfo; BlocksLeft > 0; BlocksLeft--,
lpDatablockInfo++) {
if (((lpDatablockInfo-> Flags & (DIF_PRESENT | DIF_ACCESSED |
DIF_DIRTY)) == DIF_PRESENT) && (lpDatablockInfo-> LockCount == 0)) {
NOISE(("discarding datablock #%d of ",
lpFileInfo-> FileHeader.BlockCount - BlocksLeft));
NOISE((lpFileInfo-> FileName));
NOISE(("\n"));
RgFreeDatablockInfoBuffers(lpDatablockInfo);
lpDatablockInfo-> Flags = 0;
}
// Reset the accessed bit for the next sweep.
lpDatablockInfo-> Flags &= ~DIF_ACCESSED;
}
}
//
// RgIsValidDatablockHeader
//
// Returns TRUE if lpDatablockHeader is a valid DATABLOCK_HEADER structure.
//
BOOL
INTERNAL
RgIsValidDatablockHeader(
LPDATABLOCK_HEADER lpDatablockHeader
)
{
if (lpDatablockHeader-> Signature != DH_SIGNATURE ||
HIWORD(lpDatablockHeader-> BlockSize) != 0)
return FALSE;
return TRUE;
}
#ifdef VXD
#pragma VxD_RARE_CODE_SEG
#endif
//
// RgInitDatablockInfo
//
// Initializes fields in the provided FILE_INFO related to the datablocks.
//
int
INTERNAL
RgInitDatablockInfo(
LPFILE_INFO lpFileInfo,
HFILE hFile
)
{
UINT BlockCount;
UINT BlockIndex;
LPDATABLOCK_INFO lpDatablockInfo;
LONG FileOffset;
DATABLOCK_HEADER DatablockHeader;
BlockCount = lpFileInfo-> FileHeader.BlockCount;
if (IsNullPtr((lpDatablockInfo = (LPDATABLOCK_INFO)
RgSmAllocMemory((BlockCount + DATABLOCK_INFO_SLACK_ALLOC) *
sizeof(DATABLOCK_INFO)))))
return ERROR_OUTOFMEMORY;
ZeroMemory(lpDatablockInfo, BlockCount * sizeof(DATABLOCK_INFO));
lpFileInfo-> lpDatablockInfo = lpDatablockInfo;
lpFileInfo-> DatablockInfoAllocCount = BlockCount +
DATABLOCK_INFO_SLACK_ALLOC;
FileOffset = lpFileInfo-> FileHeader.Size;
for (BlockIndex = 0; BlockIndex < BlockCount; BlockIndex++,
lpDatablockInfo++) {
if (!RgSeekFile(hFile, FileOffset))
return ERROR_REGISTRY_IO_FAILED;
if (!RgReadFile(hFile, &DatablockHeader, sizeof(DATABLOCK_HEADER)))
return ERROR_REGISTRY_IO_FAILED;
if (!RgIsValidDatablockHeader(&DatablockHeader))
return ERROR_BADDB;
// Following fields already zeroed by above ZeroMemory.
// lpDatablockInfo-> lpDatablockHeader = NULL;
// lpDatablockInfo-> lpKeyRecordTable = NULL;
// lpDatablockInfo-> Flags = 0;
// lpDatablockInfo-> LockCount = 0;
lpDatablockInfo-> FileOffset = FileOffset;
// Cache these fields from the datablock header. These fields should
// not be considered valid when the datablock is physically in memory.
lpDatablockInfo-> BlockSize = SmallDword(DatablockHeader.BlockSize);
lpDatablockInfo-> FreeBytes = SmallDword(DatablockHeader.FreeBytes);
lpDatablockInfo-> FirstFreeIndex = DatablockHeader.FirstFreeIndex;
NOISE(("DB#%d fileoff=%lx, size=%x free=%x 1stindex=%d\n", BlockIndex,
FileOffset, lpDatablockInfo-> BlockSize, lpDatablockInfo->
FreeBytes, lpDatablockInfo-> FirstFreeIndex));
FileOffset += lpDatablockInfo-> BlockSize;
}
return ERROR_SUCCESS;
}