1322 lines
37 KiB
C
1322 lines
37 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();
|
|
|
|
// Old code tries to reclaim some of the data.
|
|
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=%lx\n", lpDatablockInfo-> lpDatablockHeader));
|
|
NOISE((" lpKeyRecordTable=%lx\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;
|
|
// Extending a datablock does not necessarily mean "rewrite the
|
|
// whole file again", but it works for now...
|
|
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);
|
|
// Extending a datablock does not necessarily mean "rewrite the
|
|
// whole file again", but it works for now...
|
|
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;
|
|
|
|
}
|