windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/roswell/heap.cpp

705 lines
17 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include <wbemcomn.h>
#include "a51tools.h"
#include "heap.h"
#define ROSWELL_MAIN_FILE_SUFFIX L".hea"
#define ROSWELL_FREE_FILE_SUFFIX L".fre"
#define ROSWELL_INFINITE_BLOCK_SIZE 0x7FFFFFFF
#define A51_HEAP_MAIN_FILE_INDEX 0
#define A51_HEAP_FREE_FILE_INDEX 1
CFileHeap::~CFileHeap()
{
}
long CFileHeap::Initialize( CAbstractFileSource* pSource,
LPCWSTR wszFileNameBase)
{
CInCritSec ics(&m_cs);
if (m_bInit)
return ERROR_SUCCESS;
long lRes;
//
// Open both files
//
WCHAR wszMainFilePath[MAX_PATH+1];
wcscpy(wszMainFilePath, wszFileNameBase);
wcscat(wszMainFilePath, ROSWELL_MAIN_FILE_SUFFIX);
HANDLE h;
h = CreateFileW(wszMainFilePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_ALWAYS,
FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED,
NULL);
if(h == INVALID_HANDLE_VALUE)
{
lRes = GetLastError();
_ASSERT(lRes != ERROR_SUCCESS, L"Success from failure");
return lRes;
}
lRes = pSource->Register(h, A51_HEAP_MAIN_FILE_INDEX, false, &m_pMainFile);
if (ERROR_SUCCESS != lRes)
{
return lRes;
}
WCHAR wszFreeFilePath[MAX_PATH+1];
wcscpy(wszFreeFilePath, wszFileNameBase);
wcscat(wszFreeFilePath, ROSWELL_FREE_FILE_SUFFIX);
h = CreateFileW(wszFreeFilePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_ALWAYS,
FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED,
NULL);
if(h == INVALID_HANDLE_VALUE)
{
lRes = GetLastError();
_ASSERT(lRes != ERROR_SUCCESS, L"Success from failure");
return lRes;
}
lRes = pSource->Register(h, A51_HEAP_FREE_FILE_INDEX, false, &m_pFreeFile);
if (ERROR_SUCCESS != lRes)
{
return lRes;
}
//
// Read in the free list
//
m_bInit = TRUE; // set the status here, otherwise it will fail
lRes = ReadFreeList();
if(lRes != ERROR_SUCCESS)
{
m_bInit = FALSE;
return lRes;
}
return ERROR_SUCCESS;
}
long CFileHeap::Uninitialize()
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return ERROR_SUCCESS;
if (m_pMainFile)
{
delete m_pMainFile;
m_pMainFile = NULL;
}
if (m_pFreeFile)
{
delete m_pFreeFile;
m_pFreeFile = NULL;
}
m_mapFree.clear();
m_mapFreeOffset.clear();
m_bInit = FALSE;
return ERROR_SUCCESS;
}
//
// this is a private function that uses a critsec,
// but the public function invalidate cache does not use it
//
long CFileHeap::ReadFreeList()
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return -1;
long lRes;
//
// Clear it out
//
m_mapFree.clear();
m_mapFreeOffset.clear();
DWORD dwSize;
//
// Figure out the size of the free list file
//
lRes = m_pFreeFile->GetFileLength(&dwSize);
if(dwSize > 0)
{
//
// Figure out how many records that is
//
_ASSERT(dwSize % GetFreeListRecordSize() == 0,
L"Wrong length of free file");
int nNumRecords = dwSize / GetFreeListRecordSize();
//ERRORTRACE((LOG_WBEMCORE, "Rereading %d records\n", nNumRecords));
BYTE* aBuffer = (BYTE*)TempAlloc(dwSize);
if(aBuffer == NULL)
return ERROR_OUTOFMEMORY;
CTempFreeMe tfm(aBuffer);
lRes = ReadFromFreeFile(0, aBuffer, dwSize);
if(lRes != ERROR_SUCCESS)
{
return lRes;
}
//
// Insert them all into the map
//
BYTE* pCurrent = aBuffer;
for(int i = 0; i < nNumRecords; i++)
{
DWORD dwSize;
TOffset nOffset;
memcpy(&dwSize, pCurrent, sizeof(DWORD));
memcpy(&nOffset, pCurrent + sizeof(DWORD), sizeof(TOffset));
//ERRORTRACE((LOG_WBEMCORE, "Offset %d, Size %d\n", nOffset, dwSize));
pCurrent += GetFreeListRecordSize();
try
{
m_mapFree.insert(TFreeValue(dwSize, CRecordInfo(i, nOffset)));
if(dwSize)
m_mapFreeOffset.insert(TFreeOffsetValue(nOffset, dwSize));
}
catch(...)
{
ERRORTRACE((LOG_WBEMCORE, "Crash1\n"));
return ERROR_OUTOFMEMORY;
}
}
}
else
{
//
// Populate with an inifinite block
//
lRes = InsertFreeBlock(0, ROSWELL_INFINITE_BLOCK_SIZE);
if(lRes != ERROR_SUCCESS)
return lRes;
}
return ERROR_SUCCESS;
}
long CFileHeap::InvalidateCache()
{
return ReadFreeList();
}
long CFileHeap::FindNextFree(TOffset nCurrentOffset, TOffset* pnNextFreeOffset,
DWORD* pdwNextFreeLength)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return -1;
TFreeOffsetIterator it = m_mapFreeOffset.lower_bound(nCurrentOffset);
_ASSERT(it != m_mapFreeOffset.end(), L"Missing end-block");
*pnNextFreeOffset = it->first;
*pdwNextFreeLength = it->second;
return ERROR_SUCCESS;
}
DWORD CFileHeap::GetFreeListRecordSize()
{
return sizeof(DWORD) // size
+ sizeof(TOffset); // offset
}
long CFileHeap::Allocate(DWORD dwLength, TOffset* pnOffset)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return -1;
long lRes;
//
// Inform transaction manager that we are doing stuff in this transaction,
// so that real rollback needs to be performed in case of failure
//
m_pMainFile->Touch();
//
// Find the smallest number not smaller than dwLength in the map
//
TFreeIterator it = m_mapFree.lower_bound(dwLength);
_ASSERT(it != m_mapFree.end(), L"Missing infinite block");
//
// Cut it off
//
*pnOffset = it->second.m_nOffset;
DWORD dwBlockSize = it->first;
_ASSERT(dwBlockSize >= dwLength, L"Found a smaller block");
DWORD dwSig = 0;
lRes = ReadBytes(*pnOffset, (BYTE*)&dwSig, sizeof(DWORD));
if(lRes != ERROR_SUCCESS)
return lRes;
/*
_ASSERT(dwSig == 0x51515151, L"Mismatched block");
ERRORTRACE((LOG_WBEMCORE, "Allocate %d at %d out of %d\n",
(int)dwLength, (int)*pnOffset, (int)dwBlockSize));
*/
//
// Real block --- remove it and insert a shorter one (unless exact)
//
if(dwBlockSize != dwLength)
{
lRes = ReplaceFreeBlockByFreeIt(it, *pnOffset + dwLength,
dwBlockSize - dwLength);
if(lRes != ERROR_SUCCESS)
return lRes;
}
else
{
lRes = EraseFreeBlockByFreeIt(it);
if(lRes != ERROR_SUCCESS)
return lRes;
}
return ERROR_SUCCESS;
}
long CFileHeap::FreeAllocation(TOffset nOffset, DWORD dwLength)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return -1;
long lRes;
if(dwLength == 0)
return ERROR_SUCCESS;
//
// Inform transaction manager that we are doing stuff in this transaction,
// so that real rollback needs to be performed in case of failure
//
m_pMainFile->Touch();
//
// First, see if there is a free block after us
//
int nExtraLengthAtEnd = 0;
TFreeOffsetIterator itAfter = m_mapFreeOffset.lower_bound(nOffset);
_ASSERT(itAfter != m_mapFreeOffset.end(), L"Missing end-block");
_ASSERT(itAfter->first != nOffset, L"Found block we are deallocating "
L"in free list");
// ERRORTRACE((LOG_WBEMCORE, "Free %d at %d\n", (int)dwLength, (int)nOffset));
//
// See if it starts at our end
//
if(itAfter->first == nOffset + dwLength)
{
nExtraLengthAtEnd = itAfter->second;
}
//
// Now, check the block before us
//
int nExtraLengthInFront = 0;
TFreeOffsetIterator itBefore = itAfter;
if(itBefore != m_mapFreeOffset.begin())
{
itBefore--;
//
// See if it ends at our start
//
if(itBefore->first + itBefore->second == nOffset)
{
nExtraLengthInFront = itBefore->second;
}
}
if(nExtraLengthInFront == 0 && nExtraLengthAtEnd == 0)
{
//
// No coalescing.
//
lRes = InsertFreeBlock(nOffset, dwLength);
if(lRes != ERROR_SUCCESS)
return lRes;
}
else
{
//
// We shall coalesce this block into our neighbor
//
if(nExtraLengthInFront && nExtraLengthAtEnd)
{
//
// Coalesce all three
//
TOffset nNewOffset = itBefore->first;
DWORD dwNewLength = itBefore->second + dwLength + itAfter->second;
lRes = EraseFreeBlockByOffsetIt(itBefore);
if(lRes != ERROR_SUCCESS)
return lRes;
//
// NOTE: it is very important that we Replace the block that
// follows us, since the very last block is special, and we never
// want to erase it.
//
lRes = ReplaceFreeBlockByOffsetIt(itAfter, nNewOffset, dwNewLength);
if(lRes != ERROR_SUCCESS)
return lRes;
}
else if(nExtraLengthInFront)
{
//
// Collapse into the front block
//
TOffset nNewOffset = itBefore->first;
DWORD dwNewLength = itBefore->second + dwLength;
lRes = ReplaceFreeBlockByOffsetIt(itBefore, nNewOffset,
dwNewLength);
if(lRes != ERROR_SUCCESS)
return lRes;
}
else
{
//
// Collapse into the back block
//
TOffset nNewOffset = nOffset;
DWORD dwNewLength = itAfter->second + dwLength;
lRes = ReplaceFreeBlockByOffsetIt(itAfter, nNewOffset, dwNewLength);
if(lRes != ERROR_SUCCESS)
return lRes;
}
}
return ERROR_SUCCESS;
}
long CFileHeap::ReadBytes(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return -1;
return m_pMainFile->Read(nOffset, pBuffer, dwLength, NULL);
}
long CFileHeap::WriteBytes(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return -1;
// ERRORTRACE((LOG_WBEMCORE, "Write %d bytes at %d\n", (int)dwLength, (int)nOffset));
return m_pMainFile->Write(nOffset, pBuffer, dwLength, NULL);
}
//
//
// here are the private functions
// the private finctions are supposed to be called by the public ones
// that are guarded by a Critical Section
//
///////////////////////////////////////////////////////////////////
long CFileHeap::InsertFreeBlock(TOffset nOffset, DWORD dwLength)
{
//
// We need to find an empty record --- they are the ones with 0 length
//
DWORD dwIndex;
TFreeIterator it = m_mapFree.find(0);
if(it == m_mapFree.end())
{
//
// No free records --- just add one.
//
dwIndex = m_mapFree.size();
}
else
{
dwIndex = it->second.m_dwIndex;
try
{
m_mapFree.erase(it);
}
catch(...)
{
ERRORTRACE((LOG_WBEMCORE, "Crash2\n"));
return ERROR_OUTOFMEMORY;
}
}
CRecordInfo Info(dwIndex, nOffset);
try
{
m_mapFree.insert(TFreeValue(dwLength, Info));
m_mapFreeOffset.insert(TFreeOffsetValue(nOffset, dwLength));
}
catch(...)
{
ERRORTRACE((LOG_WBEMCORE, "Crash3\n"));
return ERROR_OUTOFMEMORY;
}
return WriteAllocationRecordToDisk(Info, dwLength);
}
CFileHeap::TFreeOffsetIterator CFileHeap::GetOffsetIteratorFromFree(
TFreeIterator itFree)
{
return m_mapFreeOffset.find(itFree->second.m_nOffset);
}
CFileHeap::TFreeIterator CFileHeap::GetFreeIteratorFromOffset(
TFreeOffsetIterator itOffset)
{
//
// Can't just look it up --- walk all entries of this size looking for the
// right offset
//
TOffset nOffset = itOffset->first;
DWORD dwSize = itOffset->second;
TFreeIterator itFree = m_mapFree.lower_bound(dwSize);
while(itFree != m_mapFree.end() && itFree->first == dwSize &&
itFree->second.m_nOffset != nOffset)
{
itFree++;
}
return itFree;
}
long CFileHeap::EraseFreeBlockByFreeIt(CFileHeap::TFreeIterator itFree)
{
return EraseFreeBlock(itFree, GetOffsetIteratorFromFree(itFree));
}
long CFileHeap::EraseFreeBlockByOffsetIt(CFileHeap::TFreeOffsetIterator itFreeOffset)
{
return EraseFreeBlock(GetFreeIteratorFromOffset(itFreeOffset),
itFreeOffset);
}
long CFileHeap::EraseFreeBlock(CFileHeap::TFreeIterator itFree,
CFileHeap::TFreeOffsetIterator itOffset)
{
_ASSERT(itFree != m_mapFree.end() && itOffset != m_mapFreeOffset.end(),
L"Erasing a block that's not in one of the maps");
//
// Move the block to the free free-block list
//
CRecordInfo Info = itFree->second;
Info.m_nOffset = ROSWELL_INVALID_OFFSET;
try
{
m_mapFree.erase(itFree);
m_mapFree.insert(TFreeValue(0, Info));
m_mapFreeOffset.erase(itOffset);
}
catch(...)
{
ERRORTRACE((LOG_WBEMCORE, "Crash4\n"));
return ERROR_OUTOFMEMORY;
}
return WriteAllocationRecordToDisk(Info, 0);
}
long CFileHeap::ReplaceFreeBlockByFreeIt(CFileHeap::TFreeIterator itFree, TOffset nOffset,
DWORD dwSize)
{
return ReplaceFreeBlock(itFree, GetOffsetIteratorFromFree(itFree),
nOffset, dwSize);
}
long CFileHeap::ReplaceFreeBlockByOffsetIt(CFileHeap::TFreeOffsetIterator itFreeOffset,
TOffset nOffset, DWORD dwSize)
{
return ReplaceFreeBlock(GetFreeIteratorFromOffset(itFreeOffset),
itFreeOffset, nOffset, dwSize);
}
long CFileHeap::ReplaceFreeBlock(CFileHeap::TFreeIterator itFree,
CFileHeap::TFreeOffsetIterator itOffset,
TOffset nOffset, DWORD dwSize)
{
long lRes;
//
// Check if the offset we are replacing is the ending offset.
//
bool bTruncate = false;
TFreeOffsetIterator itNext = itOffset;
itNext++;
if(itNext == m_mapFreeOffset.end())
{
if(nOffset < itOffset->first)
bTruncate = true;
}
//
// Compute the info block --- old index, new offset
//
CRecordInfo Info = itFree->second;
Info.m_nOffset = nOffset;
try
{
//
// If the size changed, remove and insert an entry into the by-size
// map. Otherwise, just change the info there
//
if(itFree->first != dwSize)
{
m_mapFree.erase(itFree);
m_mapFree.insert(TFreeValue(dwSize, Info));
}
else
{
itFree->second = Info;
}
//
// If the offset changed, remove and insert an entry into the by-offset
// map. Otherwise, just change the info there
//
if(itOffset->first != nOffset)
{
m_mapFreeOffset.erase(itOffset);
m_mapFreeOffset.insert(TFreeOffsetValue(Info.m_nOffset, dwSize));
}
else
{
itOffset->second = dwSize;
}
}
catch(...)
{
ERRORTRACE((LOG_WBEMCORE, "Crash5\n"));
return ERROR_OUTOFMEMORY;
}
if(bTruncate && 0)
{
//
// Truncate the file
//
lRes = m_pMainFile->SetFileLength(nOffset);
if(lRes != ERROR_SUCCESS)
return lRes;
}
return WriteAllocationRecordToDisk(Info, dwSize);
}
long CFileHeap::WriteAllocationRecordToDisk(const CRecordInfo& Info,
DWORD dwSize)
{
BYTE* pBuffer = (BYTE*)TempAlloc(GetFreeListRecordSize());
if(pBuffer == NULL)
return ERROR_OUTOFMEMORY;
CTempFreeMe tfm(pBuffer);
memcpy(pBuffer, &dwSize, sizeof(DWORD));
memcpy(pBuffer + sizeof(DWORD), &Info.m_nOffset, sizeof(TOffset));
if(Info.m_nOffset != ROSWELL_INVALID_OFFSET && dwSize >= sizeof(DWORD))
{
DWORD dwSig = 0x51515151;
long lRes = WriteBytes(Info.m_nOffset, (BYTE*)&dwSig, sizeof(DWORD));
if(lRes != ERROR_SUCCESS)
return lRes;
}
//
// Position in the file is identified by the index
//
return WriteToFreeFile(GetFreeListRecordSize() * Info.m_dwIndex,
pBuffer, GetFreeListRecordSize());
}
long CFileHeap::ReadFromFreeFile(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
{
return m_pFreeFile->Read(nOffset, pBuffer, dwLength, NULL);
}
long CFileHeap::WriteToFreeFile(TOffset nOffset, BYTE* pBuffer, DWORD dwLength)
{
return m_pFreeFile->Write(nOffset, pBuffer, dwLength, NULL);
}