windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/xfiles/varobjheap.cpp
2020-09-26 16:20:57 +08:00

1597 lines
54 KiB
C++

/*++
Copyright (C) 2000-2001 Microsoft Corporation
Module Name:
VarObjHeap.CPP
Abstract:
Implements the storage of variable length objects over the top of of a fixed
length page system. It keeps a set of admin pages for holding the pages active
by this subsystem, along with how much space is used on each. When a page becomes
empty it frees up the page to the page system. It also deals with blocks that span
multiple pages
History:
paulall 02-Feb-2001 Created
--*/
#include <windows.h>
#include "VarObjHeap.h"
#include "pagemgr.h"
#ifdef DBG
#include <tchar.h>
#endif
#include <wbemutil.h>
//**************************************************************************************
//CVarObjHeap will do little other than initialize variables. The Initialize method
//deals with starting everything up.
//**************************************************************************************
CVarObjHeap::CVarObjHeap()
: m_pObjectFile(NULL), m_dwPageSize(0), m_dwStatus(AdminPagesNeedReading)
{
}
//**************************************************************************************
//~CVarObjHeap will break the connection with the transacted object file layer
//**************************************************************************************
CVarObjHeap::~CVarObjHeap()
{
Shutdown(0);
}
//**************************************************************************************
//Initialize will create the link with the transacted object file layer
//**************************************************************************************
DWORD CVarObjHeap::Initialize(CPageSource *pPageManager)
{
//Initialize the object file layer...
DWORD dwRes = pPageManager->GetObjectHeapPageFile(&m_pObjectFile);
if (dwRes == ERROR_SUCCESS)
m_dwPageSize = m_pObjectFile->GetPageSize();
if (dwRes == ERROR_SUCCESS)
{
m_dwStatus = AdminPagesNeedReading;
dwRes = ReadAdminPages(pPageManager, true);
}
return dwRes;
}
//**************************************************************************************
//Shutdown will close the transactioned object file layer and tidy up anything else
//that is needed.
//**************************************************************************************
DWORD CVarObjHeap::Shutdown(DWORD dwShutdownType)
{
DWORD dwRes = ERROR_SUCCESS;
//Flush the admin pages... remember the error though!
dwRes = FlushAdminPages();
if (m_pObjectFile)
{
m_pObjectFile->Release();
m_pObjectFile = NULL;
}
//Delete the admin pages structures
m_aAdminPages.Lock();
while (m_aAdminPages.Size())
{
VarObjAdminPageEntry * pEntry = (VarObjAdminPageEntry*)m_aAdminPages[0];
delete [] pEntry->pbPage;
delete pEntry;
m_aAdminPages.RemoveAt(0);
}
m_aAdminPages.Unlock();
return dwRes;
}
//**************************************************************************************
//InvalidateCache - called when a transaction is aborted and we need to re-read cached
//data. Data we cache is mainlyh the admin pages, therefore we should just re-read
//them.
//**************************************************************************************
DWORD CVarObjHeap::InvalidateCache()
{
m_dwStatus = AdminPagesNeedReading;
return ReadAdminPages(0, true);
}
DWORD CVarObjHeap::FlushCaches()
{
m_dwStatus = AdminPagesNeedReading;
return ReadAdminPages(0, false);
}
//**************************************************************************************
//ReadBuffer retrieves the appropriate page(s) from the virtual page store and copies
//off the actual block from those page(s). It may reside on the main page if it is small,
//it may reside on main page and some of the next page, or it may reside on main page,
//one or more whole pages following that, followed by a partial (or full) page.
//**************************************************************************************
DWORD CVarObjHeap::ReadBuffer(/* in */ ULONG ulPageId,
/* in */ ULONG ulOffsetId,
/* out */ BYTE **ppReturnedBlock,
/* out */ DWORD *pdwBlockSize)
{
if ((ulPageId == 0) || (ulOffsetId == 0))
DebugBreak();
DWORD dwRes = ERROR_SUCCESS;
if (m_dwStatus == NoError)
{
//Nothing to do... help with compiler prediction logic
}
else if ((m_dwStatus == AdminPageReadFailure) || (m_dwStatus == AdminPagesNeedReading))
{
dwRes = ReadAdminPages(0, true);
if (dwRes != ERROR_SUCCESS)
return dwRes;
}
else if (m_dwStatus == RootAdminPageCreationFailure)
{
OutputDebugString(L"WinMgmt: Repository initialization failed and we were still called to retrieve stuff! Yeah, right!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
//We need to retrieve the page from the file store, and then we need to retrieve the
//block from the the page. We need to allocate the appropriate memory and copy it
//into that memory.
BYTE *pbPage = new BYTE[m_dwPageSize];
if (pbPage == NULL)
dwRes = ERROR_OUTOFMEMORY;
CVectorDeleteMe<BYTE> vdm1(pbPage);
//So, first we need to retrieve the page...
if (dwRes == ERROR_SUCCESS)
dwRes = m_pObjectFile->GetPage(ulPageId, 0, pbPage);
//Retrieve the REAL offset and size to the block based on offsetId
BYTE *pOffsetPointer = NULL;
DWORD dwBlockSize = 0;
DWORD dwCRC32 = 0;
if (dwRes == ERROR_SUCCESS)
dwRes = OffsetToPointer(ulOffsetId, pbPage, &pOffsetPointer, &dwBlockSize, &dwCRC32);
//We can now allocate the real block now as we know how big it is. We may not have all
//the pages in memory yet though!
BYTE *pBlock = NULL;
if (dwRes == ERROR_SUCCESS)
pBlock = new BYTE[dwBlockSize];
if ((dwRes == ERROR_SUCCESS) && (pBlock == NULL))
dwRes = ERROR_OUTOFMEMORY;
DWORD dwBlockNumber = 0;
DWORD dwAmountCopiedSoFar = 0;
//Copy off the first block
if (dwRes == ERROR_SUCCESS)
{
DWORD dwSizeOfFirstPageBlock = min(dwBlockSize, DWORD((pbPage + m_dwPageSize) - pOffsetPointer));
dwAmountCopiedSoFar = dwSizeOfFirstPageBlock;
memcpy(pBlock, pOffsetPointer, dwSizeOfFirstPageBlock);
}
//We should now loop through the pages (retrieving them if necessary) and copying
//the data into our buffer
while ((dwRes == ERROR_SUCCESS) && (dwAmountCopiedSoFar < dwBlockSize))
{
dwBlockNumber++;
//Read the next page...
dwRes = m_pObjectFile->GetPage(ulPageId + dwBlockNumber, 0, pbPage);
if (dwRes == ERROR_SUCCESS)
{
//Distinguish if this is a full page or not...
if ((dwAmountCopiedSoFar + m_dwPageSize) > dwBlockSize)
{
//This is a partial block, so copy as much as is needed
DWORD dwPartialSize = dwBlockSize - dwAmountCopiedSoFar;
memcpy((pBlock + dwAmountCopiedSoFar), pbPage, dwPartialSize);
dwAmountCopiedSoFar += dwPartialSize;
}
else
{
//This is a full block, so grab it all...
memcpy((pBlock + dwAmountCopiedSoFar), pbPage, m_dwPageSize);
dwAmountCopiedSoFar += m_dwPageSize;
}
}
}
#ifdef DBG
if (dwRes == ERROR_SUCCESS)
{
//Can only check for single-page blocks!
if (ulOffsetId != 1)
{
dwRes = ValidatePageCRCWithAdminPage(pbPage, ulPageId);
}
}
#endif
//If we are successful, lets do a CRC check on the object
if (dwRes == ERROR_SUCCESS)
{
try
{
DWORD dwNewCRC32 = CreateCRC32(pBlock, dwBlockSize);
FINALIZE_CRC32(dwNewCRC32);
if (dwNewCRC32 != dwCRC32)
{
#ifdef DBG
OutputDebugString(L"WinMgmt: CRC check on an object retrieved from repository is invalid\n");
DebugBreak();
#endif
dwRes = ERROR_INTERNAL_ERROR;
}
}
catch (...)
{
#ifdef DBG
OutputDebugString(L"WinMgmt: CRC check on an object retrieved from repository is invalid\n");
DebugBreak();
#endif
dwRes = ERROR_INTERNAL_ERROR;
}
}
//If successful we need to return the pointer to the object
if (dwRes == ERROR_SUCCESS)
{
*ppReturnedBlock = pBlock;
*pdwBlockSize = dwBlockSize;
}
else
{
delete [] pBlock;
}
return dwRes;
}
//**************************************************************************************
//WriteNewBuffer will write a new page based on size of BYTE *, and return the
//new virtual pageId and offsetId of the block. Although we may use multiple pages
//we only need to return the details of the first page.
//**************************************************************************************
DWORD CVarObjHeap::WriteNewBuffer(/* in */ ULONG ulBlockSize,
/* in */ const BYTE *pBlock,
/* out */ ULONG *pulPageId,
/* out */ ULONG *pulOffsetId)
{
DWORD dwRes = ERROR_SUCCESS;
if (m_dwStatus == NoError)
{
//Nothing to do... help with compiler prediction logic
}
else if ((m_dwStatus == AdminPageReadFailure) || (m_dwStatus == AdminPagesNeedReading))
{
dwRes = ReadAdminPages(0, true);
if (dwRes != ERROR_SUCCESS)
return dwRes;
}
else if (m_dwStatus == RootAdminPageCreationFailure)
{
OutputDebugString(L"WinMgmt: Repository initialization failed and we were still called to retrieve stuff! Yeah, right!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
//If this block will not fit on a single page we call the dedicated method!
if (ulBlockSize > (m_dwPageSize - (sizeof(VarObjObjOffsetEntry) * 2)))
return AllocateMultiPageBuffer(ulBlockSize, pBlock, pulPageId, pulOffsetId);
BYTE *pbPage = new BYTE[m_dwPageSize];
if (pbPage == NULL)
dwRes = ERROR_OUTOFMEMORY;
CVectorDeleteMe<BYTE> vdm(pbPage);
DWORD dwPageId = 0;
//Find a page that has enough space for this
if (dwRes == ERROR_SUCCESS)
dwRes = FindPageWithSpace(ulBlockSize, &dwPageId);
if (dwRes == ERROR_SUCCESS)
{
//There is a page with space in it!
//Read the page from the file
dwRes = m_pObjectFile->GetPage(dwPageId, 0, pbPage);
#ifdef DBG
if (dwRes == ERROR_SUCCESS)
dwRes = ValidatePageCRCWithAdminPage(pbPage, dwPageId);
if (dwRes == ERROR_SUCCESS)
dwRes = ValidatePageFreeSpaceWithAdminPage(pbPage, dwPageId);
if (dwRes == ERROR_SUCCESS)
dwRes = ValidatePageFreeSpace(pbPage, ulBlockSize);
#endif /* DBG */
}
else if (dwRes == ERROR_FILE_NOT_FOUND)
{
//We didn't find space so we allocate a new page
dwRes = AllocateNewPage(ulBlockSize, &dwPageId, pbPage);
}
//We now have a page, albeit a new page or an existing one, so now we need
//to allocate space from it
if (dwRes == ERROR_SUCCESS)
{
dwRes = AllocateFromPage(dwPageId, pbPage, ulBlockSize, pBlock, pulOffsetId);
}
//Write the page to the object file
if (dwRes == ERROR_SUCCESS)
dwRes = m_pObjectFile->PutPage(dwPageId, 0, pbPage);
DWORD dwCRC32 = 0;
if (dwRes == ERROR_SUCCESS)
{
dwCRC32 = CreateCRC32(pbPage, m_dwPageSize);
FINALIZE_CRC32(dwCRC32);
}
if (dwRes == ERROR_SUCCESS)
dwRes = UpdateAdminPageForAllocate(dwPageId, ulBlockSize, dwCRC32);
if (dwRes == ERROR_SUCCESS)
dwRes = FlushAdminPages();
//Update the pageId for the client caller
if (dwRes == ERROR_SUCCESS)
{
*pulPageId = dwPageId;
if ((*pulPageId == 0) || (*pulOffsetId == 0))
DebugBreak();
}
#ifdef DBG
if (dwRes == ERROR_SUCCESS)
dwRes = ValidatePageFreeSpaceWithAdminPage(pbPage, dwPageId);
if (dwRes == ERROR_SUCCESS)
dwRes = ValidateAllCRC32OnPage(pbPage);
#endif
return dwRes;
}
//**************************************************************************************
//WriteExistingBuffer will update an existing block with new data. The old virtual page
//and offset are passed in, and new ones are returned. They may or may not be the same
//depending on if it still fits in the page or not.
//**************************************************************************************
DWORD CVarObjHeap::WriteExistingBuffer(/* in */ ULONG ulBlockSize,
/* in */ const BYTE *pBlock,
/* in */ ULONG ulOldPageId,
/* in */ ULONG ulOldOffsetId,
/* out */ ULONG *pulNewPageId,
/* out */ ULONG *pulNewOffsetId)
{
//Validate the in parameters!
if ((ulOldPageId == 0) || (ulOldOffsetId == 0))
{
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
DWORD dwRes = ERROR_SUCCESS;
if (m_dwStatus == NoError)
{
//Nothing to do... help with compiler prediction logic
}
else if ((m_dwStatus == AdminPageReadFailure) || (m_dwStatus == AdminPagesNeedReading))
{
dwRes = ReadAdminPages(0, true);
if (dwRes != ERROR_SUCCESS)
return dwRes;
}
else if (m_dwStatus == RootAdminPageCreationFailure)
{
OutputDebugString(L"WinMgmt: Repository initialization failed and we were still called to retrieve stuff! Yeah, right!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
//We need to retrieve the page that is being updated, then we need to overwrite the
//original stuff in the page. We may need to shuffle all the existing blocks around
//within the page to make sure everything is fully packed. We may need to adjust
//the free-page list with the amount of space we have available on this page.
//TODO! Do this properly!!!
dwRes = DeleteBuffer(ulOldPageId, ulOldOffsetId);
if (dwRes == ERROR_SUCCESS)
dwRes = WriteNewBuffer(ulBlockSize, pBlock, pulNewPageId, pulNewOffsetId);
//Validate the out parameters!
if ((*pulNewPageId == 0) || (*pulNewOffsetId == 0))
{
DebugBreak();
dwRes = ERROR_INTERNAL_ERROR;
}
return dwRes;
}
//**************************************************************************************
//DeleteBuffer is called to delete the item in the store given the virtual pageId and
//offsetId.
//**************************************************************************************
DWORD CVarObjHeap::DeleteBuffer(/* in */ ULONG ulPageId,
/* in */ ULONG ulOffsetId)
{
DWORD dwRes = ERROR_SUCCESS;
if (m_dwStatus == NoError)
{
//Nothing to do... help with compiler prediction logic
}
else if ((m_dwStatus == AdminPageReadFailure) || (m_dwStatus == AdminPagesNeedReading))
{
dwRes = ReadAdminPages(0, true);
if (dwRes != ERROR_SUCCESS)
return dwRes;
}
else if (m_dwStatus == RootAdminPageCreationFailure)
{
OutputDebugString(L"WinMgmt: Repository initialization failed and we were still called to retrieve stuff! Yeah, right!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
//Allocate space for the page we are going to manipulate
BYTE *pbPage = new BYTE[m_dwPageSize];
if (pbPage == NULL)
dwRes = ERROR_OUTOFMEMORY;
CVectorDeleteMe<BYTE> vdm(pbPage);
//Retrieve the page that contains this object
if (dwRes == ERROR_SUCCESS)
dwRes = m_pObjectFile->GetPage(ulPageId, 0, pbPage);
//If this object is a multi-page object we have a different algorithm
if (dwRes == ERROR_SUCCESS)
{
if (MultiPageObject(pbPage))
return DeleteMultiPageBuffer(ulPageId, ulOffsetId, pbPage);
}
//Remove the object from this page
DWORD dwSize = 0;
if (dwRes == ERROR_SUCCESS)
dwRes = RemoveFromPage(ulPageId, ulOffsetId, pbPage, &dwSize);
DWORD dwCRC32 = 0;
if (dwRes == ERROR_SUCCESS)
{
dwCRC32 = CreateCRC32(pbPage, m_dwPageSize);
FINALIZE_CRC32(dwCRC32);
}
//Update the admin page, possibly even deleting the page!
bool bPageDeleted = false;
if (dwRes == ERROR_SUCCESS)
dwRes = UpdateAdminPageForDelete(ulPageId, dwSize, dwCRC32, &bPageDeleted);
//Flush the page back to the object file and update admin page
if ((dwRes == ERROR_SUCCESS) && !bPageDeleted)
{
dwRes = m_pObjectFile->PutPage(ulPageId, 0, pbPage);
}
//Flush the admin pages
if (dwRes == ERROR_SUCCESS)
dwRes = FlushAdminPages();
return dwRes;
}
//**************************************************************************************
//AllocateFromPage - adds an allocation to the end of the existing allocations
//**************************************************************************************
DWORD CVarObjHeap::AllocateFromPage(/* in */ DWORD dwPageId,
/* in */ BYTE *pbPage,
/* in */ ULONG ulBlockSize,
/* in */ const BYTE *pBlock,
/* out*/ ULONG *pdwNewOffset)
{
#ifdef XFILES_DEBUG
if (dwPageId == 0x125)
{
OutputDebugString(L"===============================\n");
OutputDebugString(L"Start of AllocateFromPage\n");
DumpPageOffsetTable(dwPageId, pbPage);
}
#endif
DWORD dwRes = ERROR_SUCCESS;
//Get a pointer to the start of the offset table
VarObjObjOffsetEntry *pOffsetEntry = (VarObjObjOffsetEntry *) pbPage;
//This is the location where the last block resided and size so we can calculate
//where the new block goes
DWORD dwLastOffset = pOffsetEntry[0].dwPhysicalStartOffset;
DWORD dwLastSize = 0;
DWORD dwNewOffsetId = GetTickCount() + (DWORD)rand();
bool bNewOffsetIdClash = false;
//Loop through the table until we get to the end... adjusting the offset within the
//entries along the way to account for the fact we will be shifting them by the size
//of an offset entry.
for (DWORD dwOffsetIndex = 0; pOffsetEntry[dwOffsetIndex].dwOffsetId != 0; dwOffsetIndex++)
{
//Shuffle the size of this offset by one entry because we will be doing a memcpy
//when this is done so we have room for our new entry.
pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset += sizeof VarObjObjOffsetEntry;
dwLastOffset = pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset;
dwLastSize = pOffsetEntry[dwOffsetIndex].dwBlockLength;
if (pOffsetEntry[dwOffsetIndex].dwOffsetId == dwNewOffsetId)
{
bNewOffsetIdClash = true;
}
}
//While we have an offset class we need to keep re-calculating
while (bNewOffsetIdClash)
{
bNewOffsetIdClash = false;
dwNewOffsetId = GetTickCount() + (DWORD)rand();
for (DWORD dwIndex = 0; pOffsetEntry[dwIndex].dwOffsetId != 0; dwIndex++)
{
if (pOffsetEntry[dwIndex].dwOffsetId == dwNewOffsetId)
{
bNewOffsetIdClash = true;
break;
}
}
}
//Now dwOffsetIndex is where we are going to insert this new offset entry, and dwLastOffset + dwLastSize
//is the new location where we need to copy this data...
//Only problem now though is that we need to shuffle all data along by the size of an offset entry!
MoveMemory(&pOffsetEntry[dwOffsetIndex+1], &pOffsetEntry[dwOffsetIndex], ((dwLastOffset + dwLastSize) - pOffsetEntry[0].dwPhysicalStartOffset) + sizeof(VarObjObjOffsetEntry));
//Write the new entry in the offset table
pOffsetEntry[dwOffsetIndex].dwOffsetId = dwNewOffsetId;
if (dwLastOffset == 0)
{
//First block of the page!
pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset = (sizeof(VarObjObjOffsetEntry) * 2);
}
else
{
pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset = dwLastOffset + dwLastSize;
}
pOffsetEntry[dwOffsetIndex].dwBlockLength = ulBlockSize;
#if XFILES_DEBUG
if (dwPageId == 0x125)
{
OutputDebugString(L"===============================\n");
OutputDebugString(L"Start of AllocateFromPage\n");
DumpPageOffsetTable(dwPageId, pbPage);
}
#endif
//Write the block to the page
#ifdef DBG
if (pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset + ulBlockSize > m_dwPageSize)
{
OutputDebugString(L"WinMgmt: Object heap is about to write past the end of a page boundary and will cause heap corruption if we continue!\n");
DebugBreak();
dwRes = ERROR_INTERNAL_ERROR;
}
#endif
if (dwRes == ERROR_SUCCESS)
{
//Generate the CRC
DWORD dwCRC32 = CreateCRC32(pBlock, ulBlockSize);
FINALIZE_CRC32(dwCRC32);
pOffsetEntry[dwOffsetIndex].dwCRC = dwCRC32;
//Copy the blob into the block
CopyMemory(pbPage + pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset, pBlock, ulBlockSize);
//Return the offset ID
*pdwNewOffset = dwNewOffsetId;
}
return dwRes;
}
//**************************************************************************************
//OffsetToPointer - Given and offsetId and a page, calculate the physical pointer to the
//object and also return the size of the block.
//**************************************************************************************
DWORD CVarObjHeap::OffsetToPointer(/* in */ ULONG ulOffsetId,
/* in */ BYTE *pbPage,
/* out*/ BYTE **pOffsetPointer,
/* out*/ ULONG *pdwBlockSize,
/* out*/ DWORD *pdwCRC32)
{
DWORD dwRes = ERROR_FILE_NOT_FOUND;
//Get a pointer to the start of the offset table
VarObjObjOffsetEntry *pOffsetEntry = (VarObjObjOffsetEntry *) pbPage;
//Loop through the table until we find the one we are interested in
for (DWORD dwOffsetIndex = 0; pOffsetEntry[dwOffsetIndex].dwOffsetId != 0; dwOffsetIndex++)
{
if (pOffsetEntry[dwOffsetIndex].dwOffsetId == ulOffsetId)
{
dwRes = ERROR_SUCCESS;
*pdwBlockSize = pOffsetEntry[dwOffsetIndex].dwBlockLength;
*pOffsetPointer = pbPage + pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset;
*pdwCRC32 = pOffsetEntry[dwOffsetIndex].dwCRC;
break;
}
}
return dwRes;
}
//**************************************************************************************
//ReadAdminPages - Reads the admin pages into memory and marks them as clean (no changes)
//**************************************************************************************
DWORD CVarObjHeap::ReadAdminPages(CPageSource *pTransactionManager, bool bReReadPages)
{
m_aAdminPages.Lock();
//Check it wasn't lock contention that meant we had multiple
//threads trying to re-read the admin pages.
if (m_dwStatus == NoError)
{
m_aAdminPages.Unlock();
return ERROR_SUCCESS;
}
//Delete anything we may already have in the list in case we need to re-read it in
//case of an aborted transaction.
while (m_aAdminPages.Size())
{
VarObjAdminPageEntry * pEntry = (VarObjAdminPageEntry*)m_aAdminPages[0];
delete [] pEntry->pbPage;
delete pEntry;
m_aAdminPages.RemoveAt(0);
}
DWORD dwRes = ERROR_SUCCESS;
if (bReReadPages)
{
m_dwStatus = AdminPagesNeedReading;
DWORD dwAdminPageId = 0; //First admin page always resides on page 0
do
{
bool bDirty = false;
BYTE *pbAdminPage = new BYTE[m_dwPageSize];
VarObjAdminPageEntry *pEntry = new VarObjAdminPageEntry;
if ((pbAdminPage == NULL) || (pEntry == NULL))
{
dwRes = ERROR_OUTOFMEMORY;
m_dwStatus = AdminPageReadFailure;
}
if (dwRes == ERROR_SUCCESS)
{
dwRes = m_pObjectFile->GetPage(dwAdminPageId, 0, pbAdminPage);
if ((dwRes == ERROR_FILE_NOT_FOUND) && (dwAdminPageId == 0))
{
//This is the first attempt, so we need to create the admin page!
dwRes = m_pObjectFile->NewPage(1, 1, &dwAdminPageId);
if (dwRes == ERROR_SUCCESS)
{
//Write the default data to the admin page
bDirty = true;
VarObjHeapAdminPage* pAdminPage = (VarObjHeapAdminPage*)pbAdminPage;
pAdminPage->dwNextAdminPage = 0;
pAdminPage->dwNumberEntriesOnPage = 0;
pAdminPage->dwVersion = VAROBJ_VERSION;
dwRes = m_pObjectFile->PutPage(dwAdminPageId, 0, pbAdminPage);
if (dwRes != ERROR_SUCCESS)
{
m_dwStatus = RootAdminPageCreationFailure;
}
}
else
{
m_dwStatus = RootAdminPageCreationFailure;
}
}
else if ((dwAdminPageId == 0) && (dwRes != ERROR_SUCCESS))
{
m_dwStatus = AdminPageReadFailure;
}
}
if (dwRes == ERROR_SUCCESS)
{
pEntry->dwPageId = dwAdminPageId;
pEntry->pbPage = pbAdminPage;
pEntry->bDirty = bDirty;
}
if ((dwRes == ERROR_SUCCESS) && (m_aAdminPages.Add(pEntry) != CFlexArray::no_error))
{
dwRes = ERROR_OUTOFMEMORY;
m_dwStatus = AdminPageReadFailure;
}
if (dwRes == ERROR_SUCCESS)
dwAdminPageId = ((VarObjHeapAdminPage*)pbAdminPage)->dwNextAdminPage;
else
{
//Tidy up!
delete [] pbAdminPage;
delete pEntry;
}
}
while ((dwRes == ERROR_SUCCESS) && (dwAdminPageId != 0));
//If we had a problem we need to delete everything in the admin list
if (dwRes != ERROR_SUCCESS)
{
while (m_aAdminPages.Size())
{
VarObjAdminPageEntry * pEntry = (VarObjAdminPageEntry*)m_aAdminPages[0];
delete [] pEntry->pbPage;
delete pEntry;
m_aAdminPages.RemoveAt(0);
}
}
if (dwRes == ERROR_SUCCESS)
{
m_dwStatus = NoError;
}
}
m_aAdminPages.Unlock();
return dwRes;
}
//**************************************************************************************
//FlushAdminPages - Writes each of the changed admin pages back to the object file
//**************************************************************************************
DWORD CVarObjHeap::FlushAdminPages()
{
DWORD dwRes = ERROR_SUCCESS;
m_aAdminPages.Lock();
for (DWORD dwIndex = 0; dwIndex != m_aAdminPages.Size(); dwIndex++)
{
VarObjAdminPageEntry * pEntry = (VarObjAdminPageEntry*)m_aAdminPages[dwIndex];
#if DBG
if ((dwIndex == 0) && (pEntry->dwPageId != 0))
{
OutputDebugString(L"WinMgmt: Repository corrupt! First admin page should always be page 0!\n");
DebugBreak();
}
VarObjHeapAdminPage *pAdminPage = (VarObjHeapAdminPage*) pEntry->pbPage;
if ((dwIndex != 0) && (pAdminPage->dwVersion != 0))
{
OutputDebugString(L"WinMmgt: Repository corrupt! Trailing admin pages should have version stamp of 0!\n");
DebugBreak();
}
#endif
if (pEntry->bDirty)
dwRes = m_pObjectFile->PutPage(pEntry->dwPageId, 0, pEntry->pbPage);
if (dwRes == ERROR_SUCCESS)
pEntry->bDirty = false;
else
break;
}
m_aAdminPages.Unlock();
return dwRes;
}
//**************************************************************************************
//Find a page form the admin pages that can accomodate a particular buffer size
//**************************************************************************************
DWORD CVarObjHeap::FindPageWithSpace(/* in */ DWORD dwRequiredSize,
/* out*/ DWORD *pdwPageId)
{
DWORD dwRes = ERROR_FILE_NOT_FOUND;
m_aAdminPages.Lock();
for (DWORD dwPageIndex = 0; (*pdwPageId == 0) && (dwPageIndex != m_aAdminPages.Size()); dwPageIndex++)
{
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)(((VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex])->pbPage);
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
#if DBG
if ((dwPageIndex != 0) && (pAdminPage->dwVersion != 0))
{
OutputDebugString(L"WinMgmt: Repository admin page is corrupt as version is invalid!\n");
DebugBreak();
}
if (pAdminPage->dwNumberEntriesOnPage > ((m_dwPageSize - sizeof(VarObjHeapAdminPage)) / sizeof(VarObjHeapFreeList)))
{
OutputDebugString(L"WinMgmt: Repository admin page is corrupt because it thinks there are more entries than fit on the page!\n");
DebugBreak();
}
#endif
for (DWORD dwFreeIndex = 0; (*pdwPageId == 0) && (dwFreeIndex != pAdminPage->dwNumberEntriesOnPage); dwFreeIndex++)
{
if (pFreeListEntry[dwFreeIndex].dwFreeSpace >= (dwRequiredSize + sizeof(VarObjObjOffsetEntry)))
{
*pdwPageId = pFreeListEntry[dwFreeIndex].dwPageId;
dwRes = ERROR_SUCCESS;
}
}
}
m_aAdminPages.Unlock();
return dwRes;
}
//**************************************************************************************
//Allocate a new page for use with objects.
//**************************************************************************************
DWORD CVarObjHeap::AllocateNewPage(/* in */ DWORD ulBlockSize,
/* out*/ DWORD *pdwPageId,
/* in */ BYTE *pbNewObjectPage)
{
DWORD dwRes = ERROR_SUCCESS;
//Allocate a new page from the object file
if (dwRes == ERROR_SUCCESS)
dwRes = m_pObjectFile->NewPage(0, 1, pdwPageId);
if (dwRes != ERROR_SUCCESS)
return dwRes;
//We need to know if we get to the end without finding space because at that point
//we need to allocate a new admin page!
dwRes = ERROR_FILE_NOT_FOUND;
//Find an admin page that has space for this new entry
m_aAdminPages.Lock();
for (DWORD dwPageIndex = 0; dwPageIndex != m_aAdminPages.Size(); dwPageIndex++)
{
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)(((VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex])->pbPage);
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
if ((sizeof(VarObjHeapAdminPage) + ((pAdminPage->dwNumberEntriesOnPage + 1) * sizeof(VarObjHeapFreeList))) <= m_dwPageSize)
{
dwRes = ERROR_SUCCESS;
break;
}
}
m_aAdminPages.Unlock();
if (dwRes == ERROR_FILE_NOT_FOUND)
{
//TODO! REMOVE DEBUG CODE!
if (m_aAdminPages.Size() == 0)
{
OutputDebugString(L"Winmgmt: Repository admin page list is empty (1)\n");
DebugBreak();
}
//We did not find an admin page with any additional slots available. We need to allocate
//a new admin page
dwRes = ERROR_SUCCESS;
DWORD dwNewAdminPageId = 0;
//We need to allocate a new page in the object file
dwRes = m_pObjectFile->NewPage(0, 1, &dwNewAdminPageId);
if (m_aAdminPages.Size() == 0)
{
OutputDebugString(L"Winmgmt: Repository admin page list is empty (2)\n");
DebugBreak();
}
//we need to allocate all the memory for the admin page cache
BYTE *pbNewAdminPage = NULL;
VarObjAdminPageEntry *pAdminPageEntry = NULL;
if (dwRes == ERROR_SUCCESS)
{
pbNewAdminPage = new BYTE[m_dwPageSize];
pAdminPageEntry = new VarObjAdminPageEntry;
if ((pbNewAdminPage == NULL) || (pAdminPageEntry == NULL))
dwRes = ERROR_OUTOFMEMORY;
}
if (dwRes == ERROR_SUCCESS)
{
if (m_aAdminPages.Add(pAdminPageEntry) != CFlexArray::no_error)
{
dwRes = ERROR_OUTOFMEMORY;
}
}
if (m_aAdminPages.Size() == 0)
{
OutputDebugString(L"Winmgmt: Repository admin page list is empty (3)\n");
DebugBreak();
}
if (dwRes != ERROR_SUCCESS)
{
delete [] pbNewAdminPage;
delete pAdminPageEntry;
}
if (dwRes == ERROR_SUCCESS)
{
//Write the admin page entry detail
pAdminPageEntry->dwPageId = dwNewAdminPageId;
pAdminPageEntry->pbPage = pbNewAdminPage;
pAdminPageEntry->bDirty = true; //new page needs to be written!
//Hook the previous admin page to this one (we have already added the new one remember!
if (m_aAdminPages.Size() == 0)
{
OutputDebugString(L"Winmgmt: Repository admin page list is empty (4)\n");
DebugBreak();
}
VarObjAdminPageEntry *pPreviousAdminPageEntry = (VarObjAdminPageEntry*)m_aAdminPages[m_aAdminPages.Size() - 2];
VarObjHeapAdminPage *pPreviousAdminPage = (VarObjHeapAdminPage *)(pPreviousAdminPageEntry->pbPage);
pPreviousAdminPage->dwNextAdminPage = dwNewAdminPageId;
pPreviousAdminPageEntry->bDirty = true; //We just changed the page so it needs to be marked for flushing
//Initialize this new admin page with everything necessary
VarObjHeapAdminPage *pNewAdminPage = (VarObjHeapAdminPage *)pbNewAdminPage;
pNewAdminPage->dwNextAdminPage = 0;
pNewAdminPage->dwNumberEntriesOnPage = 0;
pNewAdminPage->dwVersion = 0; //not used on anything but the first page!
//Now we have all the details in there, we can set the index to this page so we can allocate
//add the new object page to it!
dwPageIndex = m_aAdminPages.Size() - 1;
}
}
//By here we now have the admin page we have space to put this new page entry!
if (dwRes == ERROR_SUCCESS)
{
//cached admin page entry, update the dirty bit as we are changing it
VarObjAdminPageEntry *pAdminPageEntry = (VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex];
pAdminPageEntry->bDirty = true;
//Admin page header, update the number of entries
VarObjHeapAdminPage *pAdminPage = (VarObjHeapAdminPage *)pAdminPageEntry->pbPage;
pAdminPage->dwNumberEntriesOnPage++;
//Add the entry to the end!
VarObjHeapFreeList *pFreeList = (VarObjHeapFreeList *)(pAdminPageEntry->pbPage + sizeof(VarObjHeapAdminPage) + (sizeof(VarObjHeapFreeList) * (pAdminPage->dwNumberEntriesOnPage - 1)));
pFreeList->dwPageId = *pdwPageId;
pFreeList->dwFreeSpace = m_dwPageSize - sizeof(VarObjObjOffsetEntry);
pFreeList->dwCRC32 = 0;
pFreeList->dwReserved = 0;
//Now we need to need to initialize the new object page to look like an empty page
ZeroMemory(pbNewObjectPage, sizeof(VarObjObjOffsetEntry));
}
return dwRes;
}
//**************************************************************************************
//Allocates a multi-page entry in the object file. This requires
//different algorithms to work things out so is a special case
//**************************************************************************************
DWORD CVarObjHeap::AllocateMultiPageBuffer(/* in */ ULONG ulBlockSize,
/* in */ const BYTE *pBlock,
/* out */ ULONG *pulPageId,
/* out */ ULONG *pulOffsetId)
{
DWORD dwRes = ERROR_SUCCESS;
//Whole pages calculation
DWORD dwNumberPagesNeeded = (ulBlockSize + (sizeof(VarObjObjOffsetEntry) * 2)) / m_dwPageSize;
//Partial page calculation
if ((ulBlockSize + (sizeof(VarObjObjOffsetEntry) * 2) % m_dwPageSize) != 0)
dwNumberPagesNeeded++;
DWORD dwFirstPageId = 0;
dwRes = m_pObjectFile->NewPage(0, dwNumberPagesNeeded, &dwFirstPageId);
for (DWORD dwCurrentOffset = 0, dwPageIndex = 0; dwPageIndex != dwNumberPagesNeeded; dwPageIndex++)
{
BYTE *pPage = new BYTE[m_dwPageSize];
if (pPage == NULL)
dwRes = ERROR_OUTOFMEMORY;
CVectorDeleteMe<BYTE> vdm(pPage);
if (dwRes == ERROR_SUCCESS)
{
if (dwCurrentOffset == 0)
{
//We have to write the header for the offset page
ZeroMemory(pPage, sizeof(VarObjObjOffsetEntry) * 2);
VarObjObjOffsetEntry *pEntry = (VarObjObjOffsetEntry*) pPage;
pEntry->dwBlockLength = ulBlockSize;
pEntry->dwOffsetId = 1;
pEntry->dwPhysicalStartOffset = sizeof(VarObjObjOffsetEntry) * 2;
DWORD dwCRC32 = CreateCRC32(pBlock, ulBlockSize);
FINALIZE_CRC32(dwCRC32);
pEntry->dwCRC = dwCRC32;
//Fill the rest of this page
CopyMemory(pPage + (sizeof(VarObjObjOffsetEntry) * 2), pBlock, m_dwPageSize - (sizeof(VarObjObjOffsetEntry) * 2));
dwCurrentOffset = m_dwPageSize - (sizeof(VarObjObjOffsetEntry) * 2);
}
else
{
if (ulBlockSize - dwCurrentOffset > m_dwPageSize)
{
CopyMemory(pPage, pBlock + dwCurrentOffset, m_dwPageSize);
dwCurrentOffset += m_dwPageSize;
}
else
{
CopyMemory(pPage, pBlock + dwCurrentOffset, ulBlockSize - dwCurrentOffset);
dwCurrentOffset += (ulBlockSize - dwCurrentOffset);
//NOTE!!! dwCurrentOffset should equal ulBlockSize now!!!!!
}
}
dwRes = m_pObjectFile->PutPage(dwFirstPageId + dwPageIndex, 0, pPage);
}
if (FAILED(dwRes))
break;
}
if (dwRes == ERROR_SUCCESS)
{
//Set up the pageId and offset details the client requested
*pulPageId = dwFirstPageId;
*pulOffsetId = 1;
}
return dwRes;
}
//**************************************************************************************
//DeleteMultiPageBuffer - handles the deletion of an object when it spans
//multiple pages
//******************4********************************************************************
DWORD CVarObjHeap::DeleteMultiPageBuffer(/* in */ ULONG ulPageId,
/* in */ ULONG ulOffsetId,
/* in */ BYTE *pbPage)
{
DWORD dwRes = ERROR_SUCCESS;
//Calculate how many pages are used:
VarObjObjOffsetEntry *pEntry = (VarObjObjOffsetEntry*)pbPage;
//Whole pages calculation
DWORD dwNumberPagesNeeded = (pEntry->dwBlockLength + (sizeof(VarObjObjOffsetEntry) * 2)) / m_dwPageSize;
//Partial page calculation
if ((pEntry->dwBlockLength + (sizeof(VarObjObjOffsetEntry) * 2) % m_dwPageSize) != 0)
dwNumberPagesNeeded++;
for (DWORD dwPageIndex = 0; dwPageIndex != dwNumberPagesNeeded; dwPageIndex ++)
{
dwRes = m_pObjectFile->FreePage(0, ulPageId + dwPageIndex);
if (FAILED(dwRes))
break;
}
return dwRes;
}
//**************************************************************************************
//DeleteFromPage - removes an object from a specific object page
//**************************************************************************************
DWORD CVarObjHeap::RemoveFromPage(/* in */ ULONG ulPageId,
/* in */ ULONG ulOffsetId,
/* in */ BYTE *pbPage,
/* out*/ DWORD *pdwSize)
{
DWORD dwRes = ERROR_FILE_NOT_FOUND;
//Need to remove the entry from the offset table, subtracting the deleted object size
//from the offset of all items following it
VarObjObjOffsetEntry *pEntry = (VarObjObjOffsetEntry*)pbPage;
DWORD dwFoundOffset = 0;
DWORD dwFoundSize = 0;
DWORD dwFoundIndex = 0;
for (DWORD dwIndex = 0; pEntry[dwIndex].dwOffsetId != 0; dwIndex ++)
{
if (pEntry[dwIndex].dwOffsetId == ulOffsetId)
{
//This is ours, so record the details
dwFoundOffset = pEntry[dwIndex].dwPhysicalStartOffset;
dwFoundSize = pEntry[dwIndex].dwBlockLength;
*pdwSize = dwFoundSize;
dwRes = ERROR_SUCCESS;
dwFoundIndex = dwIndex;
}
else if (dwRes == ERROR_SUCCESS)
{
//We have already found it so we need to adjust this entry
//to account for the removed space
pEntry[dwIndex - 1].dwPhysicalStartOffset = pEntry[dwIndex].dwPhysicalStartOffset - dwFoundSize - sizeof(VarObjObjOffsetEntry);
pEntry[dwIndex - 1].dwBlockLength = pEntry[dwIndex].dwBlockLength;
pEntry[dwIndex - 1].dwOffsetId = pEntry[dwIndex].dwOffsetId;
pEntry[dwIndex - 1].dwCRC = pEntry[dwIndex].dwCRC;
}
else
{
//Adjust for the fact that we are removing an entry for the offset table
pEntry[dwIndex].dwPhysicalStartOffset -= sizeof(VarObjObjOffsetEntry);
}
}
if (dwRes == ERROR_SUCCESS)
{
//We need to adjust the end-of-list by one place also
pEntry[dwIndex - 1].dwPhysicalStartOffset = 0;
pEntry[dwIndex - 1].dwBlockLength = 0;
pEntry[dwIndex - 1].dwOffsetId = 0;
pEntry[dwIndex - 1].dwCRC = 0;
//Now we need to adjust all entries up to the deleted one by the size of
//the offset table entry... although if this was the first item in the list then there
//is nothing to do
if (dwFoundIndex != 0)
{
MoveMemory(pbPage + pEntry[0].dwPhysicalStartOffset,
pbPage + pEntry[0].dwPhysicalStartOffset + sizeof(VarObjObjOffsetEntry),
dwFoundOffset - (pEntry[0].dwPhysicalStartOffset + sizeof(VarObjObjOffsetEntry)));
}
//Now we need to shuffle all entries that appeared after this entry back one... if this
//was the last entry then we don't have anything to do.
if (pEntry[dwFoundIndex].dwOffsetId != 0)
{
MoveMemory(pbPage + pEntry[dwFoundIndex].dwPhysicalStartOffset,
pbPage + dwFoundOffset + dwFoundSize,
(pEntry[dwIndex - 2].dwPhysicalStartOffset + pEntry[dwIndex - 2].dwBlockLength) - (pEntry[dwFoundIndex].dwPhysicalStartOffset));
}
}
return dwRes;
}
//**************************************************************************************
//UpdateAdminPageForAllocate - Updates the admin page to decrement the amount
//of free space on a page by this amount ( + sizeof(VarObjObjOffsetEntry))
//**************************************************************************************
DWORD CVarObjHeap::UpdateAdminPageForAllocate(/* in */ ULONG ulPageId,
/* in */ ULONG ulBlockSize,
/* in */ DWORD dwNewCRC)
{
DWORD dwRes = ERROR_FILE_NOT_FOUND;
//Find an admin page that has this page
for (DWORD dwPageIndex = 0; dwPageIndex != m_aAdminPages.Size(); dwPageIndex++)
{
VarObjAdminPageEntry* pAdminPageEntry = (VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex];
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)(pAdminPageEntry->pbPage);
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
for (DWORD dwEntry = 0; dwEntry != pAdminPage->dwNumberEntriesOnPage; dwEntry ++)
{
if (ulPageId == pFreeListEntry[dwEntry].dwPageId)
{
pFreeListEntry[dwEntry].dwFreeSpace -= (ulBlockSize + sizeof(VarObjObjOffsetEntry));
pFreeListEntry[dwEntry].dwCRC32 = dwNewCRC;
pAdminPageEntry->bDirty = true;
#if XFILES_DEBUG
wchar_t buf[100];
swprintf(buf, L"Page 0x%08X has allocated 0x%08X bytes. Space left 0x%08X\n", ulPageId, ulBlockSize + sizeof(VarObjObjOffsetEntry), pFreeListEntry[dwEntry].dwFreeSpace);
OutputDebugString(buf);
#endif
dwRes = ERROR_SUCCESS;
break;
}
}
if (dwRes == ERROR_SUCCESS)
break;
}
return dwRes;
}
//**************************************************************************************
//UpdateAdminPageForDelete - Updates the admin page for giving space back. If the page
//is totally empty we should delete the page altogether. Note that the space taken
//up is sizeof(VarObjObjOffsetEntry) more than specified because of the offset entry!
//**************************************************************************************
DWORD CVarObjHeap::UpdateAdminPageForDelete(/* in */ ULONG ulPageId,
/* in */ ULONG ulBlockSize,
/* in */ DWORD dwNewCRC,
/* out */ bool *pbPageDeleted)
{
DWORD dwRes = ERROR_FILE_NOT_FOUND;
bool bFinished = false;
//Find an admin page that has this page
for (DWORD dwPageIndex = 0; dwPageIndex != m_aAdminPages.Size(); dwPageIndex++)
{
VarObjAdminPageEntry* pAdminPageEntry = (VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex];
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)(pAdminPageEntry->pbPage);
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
for (DWORD dwEntry = 0; dwEntry != pAdminPage->dwNumberEntriesOnPage; dwEntry ++)
{
if (ulPageId == pFreeListEntry[dwEntry].dwPageId)
{
pFreeListEntry[dwEntry].dwFreeSpace += (ulBlockSize + sizeof(VarObjObjOffsetEntry));
pFreeListEntry[dwEntry].dwCRC32 = dwNewCRC;
dwRes = ERROR_SUCCESS;
bFinished = true;
pAdminPageEntry->bDirty = true;
#if XFILES_DEBUG
wchar_t buf[100];
swprintf(buf, L"Page 0x%08X has deallocated 0x%08X bytes. Space left 0x%08X\n", ulPageId, ulBlockSize + sizeof(VarObjObjOffsetEntry), pFreeListEntry[dwEntry].dwFreeSpace);
OutputDebugString(buf);
#endif
if (pFreeListEntry[dwEntry].dwFreeSpace == (m_dwPageSize - sizeof(VarObjObjOffsetEntry)))
{
dwRes = RemoveEntryFromAdminPage(dwPageIndex, dwEntry);
if (dwRes == ERROR_SUCCESS)
{
dwRes = m_pObjectFile->FreePage(0, ulPageId);
*pbPageDeleted = true;
}
}
break;
}
}
if (bFinished)
break;
}
return dwRes;
}
//**************************************************************************************
//Deletes a page, and updates the admin pages as appropriage. If the admin page
//is now enpty we delete this admin page and update the next pointer of the previous
//page. We do not delete the first admin page however as it has a special pageId
//that is reserved.
//**************************************************************************************
DWORD CVarObjHeap::DeletePage(/* in */ DWORD ulPageId)
{
DWORD dwRes = m_pObjectFile->FreePage(0, ulPageId);
bool bFinished = false;
if (dwRes == ERROR_SUCCESS)
{
dwRes = ERROR_FILE_NOT_FOUND;
for (DWORD dwPageIndex = 0; dwPageIndex != m_aAdminPages.Size(); dwPageIndex++)
{
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)(((VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex])->pbPage);
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
for (DWORD dwEntry = 0; dwEntry != pAdminPage->dwNumberEntriesOnPage; dwEntry ++)
{
if (ulPageId == pFreeListEntry[dwEntry].dwPageId)
{
dwRes = RemoveEntryFromAdminPage(dwPageIndex, dwEntry);
bFinished = true;
break;
}
}
if (bFinished)
break;
}
}
return dwRes;
}
//**************************************************************************************
//Removes an object page entry from an admin page, removing the
//admin page if it is no longer needed
//**************************************************************************************
DWORD CVarObjHeap::RemoveEntryFromAdminPage(/* in */ DWORD dwAdminPageIndex,
/* in */ DWORD dwAdminPageEntry)
{
DWORD dwRes = ERROR_SUCCESS;
VarObjAdminPageEntry *pAdminPageEntry = (VarObjAdminPageEntry*)m_aAdminPages[dwAdminPageIndex];
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)pAdminPageEntry->pbPage;
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
pAdminPage->dwNumberEntriesOnPage--;
if ((pAdminPage->dwNumberEntriesOnPage == 0) && (dwAdminPageIndex != 0))
{
//Need to delete this admin page... update the previous pages next admin page entry
VarObjAdminPageEntry *pPreviousAdminPageEntry = (VarObjAdminPageEntry*)m_aAdminPages[dwAdminPageIndex - 1];
VarObjHeapAdminPage * pPreviousAdminPage = (VarObjHeapAdminPage *)pPreviousAdminPageEntry->pbPage;
pPreviousAdminPage->dwNextAdminPage = pAdminPage->dwNextAdminPage;
//Set the dirty bit on that page so it gets flushed!
pPreviousAdminPageEntry->bDirty = true;
//Do the actual free page of this admin page
dwRes = m_pObjectFile->FreePage(0, pAdminPageEntry->dwPageId);
if (dwRes == ERROR_SUCCESS)
{
m_aAdminPages.RemoveAt(dwAdminPageIndex);
delete pAdminPageEntry;
}
}
else if ((pAdminPage->dwNumberEntriesOnPage == 0) && (dwAdminPageIndex == 0))
{
//The first admin page cannot be deleted so we just ignore this
}
else if (pAdminPage->dwNumberEntriesOnPage != 0)
{
//We just need to delete the entry, so shuffle the entries about
//in the page
MoveMemory(&pFreeListEntry[dwAdminPageEntry], &pFreeListEntry[dwAdminPageEntry+1], sizeof(VarObjHeapFreeList) * (pAdminPage->dwNumberEntriesOnPage - dwAdminPageEntry));
}
return dwRes;
}
#ifdef DBG
//Given a page we validate that there is in fact enough space
//for this block. If there is not it asserts. This implies
//that the admin page is not in sync with the actual pages.
DWORD CVarObjHeap::ValidatePageFreeSpace(/* in */ const BYTE *pbPage,
/* in */ DWORD ulBlockSize)
{
DWORD dwRes = ERROR_SUCCESS;
VarObjObjOffsetEntry *pEntry = (VarObjObjOffsetEntry*) pbPage;
DWORD dwNextAvailableOffset = pEntry[0].dwPhysicalStartOffset + pEntry[0].dwBlockLength;
//Search through the offset table until we find the last entry
for (DWORD dwIndex = 0; pEntry[dwIndex].dwOffsetId != 0; dwIndex++)
{
dwNextAvailableOffset = pEntry[dwIndex].dwPhysicalStartOffset + pEntry[dwIndex].dwBlockLength;
}
if ((dwNextAvailableOffset + ulBlockSize + sizeof(VarObjObjOffsetEntry))> m_dwPageSize)
{
dwRes = ERROR_INTERNAL_ERROR;
OutputDebugString(L"WinMgmt Repository Corruption: Object heap admin page free space information is out of sync with actual pages!\n");
DebugBreak();
}
return dwRes;
}
//Given a page and a page ID, it validates the amount of free space
//on the page is equal to the amount the admin page thinks is on
//there.
DWORD CVarObjHeap::ValidatePageFreeSpaceWithAdminPage(/* in */ const BYTE *pbPage,
/* in */ DWORD ulPageId)
{
DWORD dwRes = ERROR_SUCCESS;
VarObjObjOffsetEntry *pEntry = (VarObjObjOffsetEntry*) pbPage;
DWORD dwNextAvailableOffset = pEntry[0].dwPhysicalStartOffset + pEntry[0].dwBlockLength;
//Search through the offset table until we find the last entry
for (DWORD dwIndex = 0; pEntry[dwIndex].dwOffsetId != 0; dwIndex++)
{
dwNextAvailableOffset = pEntry[dwIndex].dwPhysicalStartOffset + pEntry[dwIndex].dwBlockLength;
}
DWORD dwFreeSpace = m_dwPageSize - dwNextAvailableOffset;
//Find the page in the admin page table
for (DWORD dwPageIndex = 0; dwPageIndex != m_aAdminPages.Size(); dwPageIndex++)
{
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)(((VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex])->pbPage);
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
for (DWORD dwEntry = 0; dwEntry != pAdminPage->dwNumberEntriesOnPage; dwEntry ++)
{
//If this is the page we are interested in...
if (ulPageId == pFreeListEntry[dwEntry].dwPageId)
{
//check the free space matches...
if (pFreeListEntry[dwEntry].dwFreeSpace != dwFreeSpace)
{
//Oops, it doesn't! We have a problem!
OutputDebugString(L"WinMgmt Repository Corruption: Free space in page is out of sink with the free space listed in admin page!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
return ERROR_SUCCESS;
}
}
}
return dwRes;
}
//Dumps the offset table of a page to the debugger
DWORD CVarObjHeap::DumpPageOffsetTable(/* in */ DWORD dwPageId,
/* in */ const BYTE *pbPage)
{
wchar_t buf[100];
OutputDebugString(L"================================\n");
_stprintf(buf, L"Dumping offset table for pageId <0x%X>\n", dwPageId);
OutputDebugString(buf);
OutputDebugString(L"================================\n");
DWORD dwRes = ERROR_SUCCESS;
VarObjObjOffsetEntry *pEntry = (VarObjObjOffsetEntry*) pbPage;
for (DWORD dwIndex = 0; pEntry[dwIndex].dwOffsetId != 0; dwIndex++)
{
_stprintf(buf, L"0x%08X, 0x%08X, 0x%08X, 0x%08X\n", dwIndex, pEntry[dwIndex].dwOffsetId, pEntry[dwIndex].dwBlockLength, pEntry[dwIndex].dwPhysicalStartOffset);
OutputDebugString(buf);
}
_stprintf(buf, L"0x%08X, 0x%08X, 0x%08X, 0x%08X\n", dwIndex, pEntry[dwIndex].dwOffsetId, pEntry[dwIndex].dwBlockLength, pEntry[dwIndex].dwPhysicalStartOffset);
OutputDebugString(buf);
OutputDebugString(L"================================\n");
return ERROR_SUCCESS;
}
#endif /* DBG */
static DWORD g_CRCTable[] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
//Creates a CRC for a block
DWORD CVarObjHeap::CreateCRC32(/* in */ const BYTE *pBlock,
/* in */ DWORD dwSize,
/* in */ DWORD dwPreviousCRC)
{
if(dwSize == 0)
return dwPreviousCRC;
DWORD dwNewCRC = 0;
for (int n = 0; n < dwSize; n++)
{
dwNewCRC = g_CRCTable[ BYTE(dwPreviousCRC ^ DWORD(pBlock[n]))]
^ ((dwPreviousCRC >> 8) & 0x00FFFFFF);
dwPreviousCRC = dwNewCRC;
}
return dwNewCRC;
}
#ifdef DBG
//Checks the CRCs of all objects on a page (cannot do this
//for a multi-page object though as we only have the first
//page!)
DWORD CVarObjHeap::ValidateAllCRC32OnPage(/* in */ const BYTE *pbPage)
{
DWORD dwRes = ERROR_SUCCESS;
//Get a pointer to the start of the offset table
VarObjObjOffsetEntry *pOffsetEntry = (VarObjObjOffsetEntry *) pbPage;
//Loop through the table until we find the one we are interested in
for (DWORD dwOffsetIndex = 0; pOffsetEntry[dwOffsetIndex].dwOffsetId != 0; dwOffsetIndex++)
{
if (pOffsetEntry[dwOffsetIndex].dwBlockLength > (m_dwPageSize - (sizeof(VarObjObjOffsetEntry) * 2)))
{
break;
}
else
{
try
{
DWORD dwNewCRC32 = CreateCRC32(pbPage + pOffsetEntry[dwOffsetIndex].dwPhysicalStartOffset, pOffsetEntry[dwOffsetIndex].dwBlockLength);
FINALIZE_CRC32(dwNewCRC32);
if (dwNewCRC32 != pOffsetEntry[dwOffsetIndex].dwCRC)
{
OutputDebugString(L"WinMgmt: Page Check: CRC check on an object retrieved from repository is invalid\n");
DebugBreak();
dwRes = ERROR_INTERNAL_ERROR;
}
}
catch (...)
{
OutputDebugString(L"WinMgmt: Page Check: CRC check on an object retrieved from repository is invalid\n");
DebugBreak();
dwRes = ERROR_INTERNAL_ERROR;
}
}
}
return dwRes;
}
//Validates the page check-sum with the admin page
DWORD CVarObjHeap::ValidatePageCRCWithAdminPage(/* in */ const BYTE *pbPage,
/* in */ DWORD dwPageId)
{
DWORD dwCRC32 = 0;
dwCRC32 = CreateCRC32(pbPage, m_dwPageSize);
FINALIZE_CRC32(dwCRC32);
for (DWORD dwPageIndex = 0; dwPageIndex != m_aAdminPages.Size(); dwPageIndex++)
{
VarObjHeapAdminPage * pAdminPage = (VarObjHeapAdminPage *)(((VarObjAdminPageEntry*)m_aAdminPages[dwPageIndex])->pbPage);
VarObjHeapFreeList *pFreeListEntry = (VarObjHeapFreeList *)(((BYTE*)pAdminPage) + sizeof (VarObjHeapAdminPage));
if ((dwPageIndex != 0) && (pAdminPage->dwVersion != 0))
{
OutputDebugString(L"WinMgmt: Repository admin page is corrupt as version is invalid!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
if (pAdminPage->dwNumberEntriesOnPage > ((m_dwPageSize - sizeof(VarObjHeapAdminPage)) / sizeof(VarObjHeapFreeList)))
{
OutputDebugString(L"WinMgmt: Repository admin page is corrupt because it thinks there are more entries than fit on the page!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
for (DWORD dwFreeIndex = 0; dwFreeIndex != pAdminPage->dwNumberEntriesOnPage; dwFreeIndex++)
{
if (pFreeListEntry[dwFreeIndex].dwPageId == dwPageId)
{
if (pFreeListEntry[dwFreeIndex].dwCRC32 == 0)
return ERROR_SUCCESS;
else if (pFreeListEntry[dwFreeIndex].dwCRC32 == dwCRC32)
return ERROR_SUCCESS;
else
{
OutputDebugString(L"WinMgmt: Repository admin page has an invalid CRC for the object page!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
}
}
}
OutputDebugString(L"WinMgmt: Requested page was not found in the admin page list!\n");
DebugBreak();
return ERROR_INTERNAL_ERROR;
}
#endif