// // 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; }