#include #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); }