/*++ Copyright (C) 1996-2000 Microsoft Corporation Module Name: mmfarena2.cpp Abstract: CMMFArena2 implementation (arenas based on memory-mapped files). Used for database upgrade History: --*/ #include "precomp.h" #include #include "wbemutil.h" #include "mmfarena2.h" extern CMMFArena2 * g_pDbArena; #define MAX_PAGE_SIZE_WIN9X 0x200000 /*2MB*/ #define MAX_PAGE_SIZE_NT 0x3200000 /*50MB*/ struct MMFOffsetItem { DWORD_PTR m_dwBaseOffset; LPBYTE m_pBasePointer; HANDLE m_hMappingHandle; DWORD m_dwBlockSize; }; #if (defined DEBUG || defined _DEBUG) void MMFDebugBreak() { DebugBreak(); } #else inline void MMFDebugBreak() {} #endif //*************************************************************************** // // CMMFArena2::CMMFArena2 // // Constructor. Initialises a few things. // //*************************************************************************** CMMFArena2::CMMFArena2(bool bReadOnly) : m_bReadOnlyAccess(bReadOnly), m_dwStatus(0), m_hFile(INVALID_HANDLE_VALUE) { g_pDbArena = this; //Get processor granularity SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); m_dwMappingGranularity = sysInfo.dwAllocationGranularity; m_dwMaxPageSize = MAX_PAGE_SIZE_NT; } //*************************************************************************** // // CMMFArena2::CreateNewMMF // // Creates a new MMF. This creates a single page with the header initialised // for with the MMF header information. // // pszFile : Filename of the MMF to open // dwGranularity : Minimum block allocation size // dwInitSize : Initial size of the repository. // // Return value : false if we failed, true if we succeed. // //*************************************************************************** bool CMMFArena2::CreateNewMMF(const TCHAR *pszFile, //File of MMF to open DWORD dwGranularity, // Granularity per allocation (min block size) DWORD dwInitSize) // Initial heap size { //Size should be of a specific granularity... if (dwInitSize % m_dwMappingGranularity) dwInitSize += (m_dwMappingGranularity - (dwInitSize % m_dwMappingGranularity)); // Create a new file // ================= m_hFile = CreateFile( pszFile, GENERIC_READ | (m_bReadOnlyAccess ? 0 : GENERIC_WRITE), FILE_SHARE_READ | (m_bReadOnlyAccess ? FILE_SHARE_WRITE : 0), 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0 ); if (m_hFile == INVALID_HANDLE_VALUE) { _ASSERT(0, "WinMgmt: Failed to create a new repository file"); m_dwStatus = 7; return false; } //Create the end page marker here in case we have no memory... MMFOffsetItem *pOffsetEnd = 0; pOffsetEnd = new MMFOffsetItem; if (pOffsetEnd == 0) { _ASSERT(0, "WinMgmt: Out of memory"); CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; throw CX_MemoryException(); } //Create the base page... MMFOffsetItem *pOffsetItem = CreateBasePage(dwInitSize, dwGranularity); if (pOffsetItem == 0) { _ASSERT(0, "WinMgmt: Failed to create base MMF page"); delete pOffsetEnd; CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; return false; } int nStatus = -1; //Add this page information to the offset manager... nStatus = m_OffsetManager.Add(pOffsetItem); if (nStatus != 0) { _ASSERT(0, "WinMgmt: Failed to add offset information into offset table"); //We are out of memory... lets tidy up... ClosePage(pOffsetItem); delete pOffsetItem; delete pOffsetEnd; CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; throw CX_MemoryException(); } //Fill in the end page marker information and add it to the offset manager... pOffsetEnd->m_dwBaseOffset = m_pHeapDescriptor->m_dwCurrentSize; pOffsetEnd->m_pBasePointer = 0; pOffsetEnd->m_hMappingHandle = 0; pOffsetEnd->m_dwBlockSize = 0; nStatus = -1; nStatus = m_OffsetManager.Add(pOffsetEnd); if (nStatus != 0) { _ASSERT(0, "WinMgmt: Failed to add end block marker into offset table"); //We are out of memory... lets tidy up... ClosePage(pOffsetItem); m_OffsetManager.Empty(); delete pOffsetItem; delete pOffsetEnd; CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; throw CX_MemoryException(); } return true; } //*************************************************************************** // // CMMFArena2::LoadMMF // // Loads an existing MMF. Loads in the base page and all pages following // that // // pszFile : Filename of the MMF to open // // Return value : false if we failed, true if we succeed. // //*************************************************************************** bool CMMFArena2::LoadMMF(const TCHAR *pszFile) { //Open the file... m_hFile = CreateFile( pszFile, GENERIC_READ | (m_bReadOnlyAccess ? 0 : GENERIC_WRITE), FILE_SHARE_READ | (m_bReadOnlyAccess ? FILE_SHARE_WRITE : 0), 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0 ); if (m_hFile == INVALID_HANDLE_VALUE) { _ASSERT(0, "WinMgmt: Failed to open existing repository file"); m_dwStatus = 7; return false; } DWORD dwSizeOfRepository = 0; MMFOffsetItem *pOffsetItem = 0; //Open the base page... pOffsetItem = OpenBasePage(dwSizeOfRepository); if (pOffsetItem == 0) { _ASSERT(0, "WinMgmt: Failed to open base page in MMF"); CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; return false; } //Add the details to the offset manager... int nStatus = -1; nStatus = m_OffsetManager.Add(pOffsetItem); if (nStatus) { _ASSERT(0, "WinMgmt: Failed to add offset information into offset table"); ClosePage(pOffsetItem); delete pOffsetItem; CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; throw CX_MemoryException(); } DWORD_PTR dwPageBase = 0; if (m_pHeapDescriptor->m_dwVersion == 9) { //Now loop through all the following pages and load them... DWORD dwSizeLastPage = 0; nStatus = -1; for (dwPageBase = pOffsetItem->m_dwBlockSize; dwPageBase < dwSizeOfRepository; dwPageBase += dwSizeLastPage) { //Open the next... pOffsetItem = OpenExistingPage(dwPageBase); if (pOffsetItem == 0) { _ASSERT(0, "WinMgmt: Failed to open an existing page in the MMF"); //Failed to do that! CloseAllPages(); CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; return false; } //Add the information to the offset manager... nStatus = -1; nStatus = m_OffsetManager.Add(pOffsetItem); if (nStatus) { _ASSERT(0, "WinMgmt: Failed to add offset information into offset table"); //Failed to do that! ClosePage(pOffsetItem); delete pOffsetItem; CloseAllPages(); CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; throw CX_MemoryException(); } dwSizeLastPage = pOffsetItem->m_dwBlockSize; } } else if ((m_pHeapDescriptor->m_dwVersion == 10) || (m_pHeapDescriptor->m_dwVersion < 9)) { dwPageBase = pOffsetItem->m_dwBlockSize; } else { _ASSERT(0, "WinMgmt: Database error... Code has not been added to support the opening of this database!!!!!"); ERRORTRACE((LOG_WBEMCORE, "Database error... Code has not been added to support the opening of this database!!!!!\n")); } //Create a mapping entry to mark the end of the MMF pOffsetItem = new MMFOffsetItem; if (pOffsetItem == 0) { _ASSERT(0, "WinMgmt: Out of memory"); //Failed to do that! CloseAllPages(); CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; throw CX_MemoryException(); } pOffsetItem->m_dwBaseOffset = dwPageBase; pOffsetItem->m_dwBlockSize = 0; pOffsetItem->m_hMappingHandle = 0; pOffsetItem->m_pBasePointer = 0; nStatus = -1; nStatus = m_OffsetManager.Add(pOffsetItem); if (nStatus) { _ASSERT(0, "WinMgmt: Failed to add offset information into offset table"); //Failed to do that! ClosePage(pOffsetItem); delete pOffsetItem; CloseAllPages(); CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; m_dwStatus = 7; throw CX_MemoryException(); } return true; }; //*************************************************************************** // // CMMFArena2::OpenBasePage // // Opens the MMF first page which has all the information about the rest // of the MMF as well as the first page of data. // // dwSizeOfRepository : Returns the current size of the repository // // Return value : Pointer to an offset item filled in with the base // page information. NULL if we fail to open the // base page. // //*************************************************************************** MMFOffsetItem *CMMFArena2::OpenBasePage(DWORD &dwSizeOfRepository) { MMFOffsetItem *pOffsetItem = 0; pOffsetItem = new MMFOffsetItem; if (pOffsetItem == 0) throw CX_MemoryException(); //Seek to the start of this page... if (SetFilePointer(m_hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { _ASSERT(0, "WinMgmt: Failed to set file pointer on MMF"); delete pOffsetItem; return 0; } //Read in the hear information so we can find the size of this block... DWORD dwActualRead; MMF_ARENA_HEADER mmfHeader; if ((ReadFile(m_hFile, &mmfHeader, sizeof(MMF_ARENA_HEADER), &dwActualRead, 0) == 0) || (dwActualRead != sizeof(MMF_ARENA_HEADER))) { _ASSERT(0, "WinMgmt: Failed to read MMF header information"); delete pOffsetItem; return 0; } //Record the current size information... dwSizeOfRepository = mmfHeader.m_dwCurrentSize; DWORD dwSizeToMap = 0; if ((mmfHeader.m_dwVersion < 9) || (mmfHeader.m_dwVersion == 10)) { //old style database, we map in everything... dwSizeToMap = mmfHeader.m_dwCurrentSize; } else if (mmfHeader.m_dwVersion == 9) { //We get the first page... dwSizeToMap = mmfHeader.m_dwSizeOfFirstPage; } else { _ASSERT(0, "WinMgmt: Database error... Code has not been added to support the opening of this database!!!!!"); ERRORTRACE((LOG_WBEMCORE, "Database error... Code has not been added to support the opening of this database!!!!!\n")); } //Create the file mapping for this page... HANDLE hMapping = CreateFileMapping( m_hFile, // Disk file 0, // No security (m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT, // Extend the file to match the heap size 0, // High-order max size dwSizeToMap, // Low-order max size 0 // No name for the mapping object ); if (hMapping == NULL) { _ASSERT(0, "WinMgmt: Failed to create file mapping"); delete pOffsetItem; return 0; } // Map this into memory... LPBYTE pBindingAddress = (LPBYTE)MapViewOfFile(hMapping, (m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS), 0, 0, dwSizeToMap ); if (pBindingAddress == NULL) { _ASSERT(0, "WinMgmt: Failed to map MMF into memory"); delete pOffsetItem; CloseHandle(hMapping); return 0; } //Record the base address of this because we need easy access to the header... m_pHeapDescriptor = (MMF_ARENA_HEADER*) pBindingAddress; //Create a mapping entry for this... pOffsetItem->m_dwBaseOffset = 0; pOffsetItem->m_dwBlockSize = dwSizeToMap; pOffsetItem->m_hMappingHandle = hMapping; pOffsetItem->m_pBasePointer = pBindingAddress; return pOffsetItem; } //*************************************************************************** // // CMMFArena2::OpenExistingPage // // Opens the specified page from the repostory. // // dwBaseOffset : Offset within the MMF to map in. // // Return value : Pointer to an offset item filled in with the // page information. NULL if we fail to open the // page. // //*************************************************************************** MMFOffsetItem *CMMFArena2::OpenExistingPage(DWORD_PTR dwBaseOffset) { MMFOffsetItem *pOffsetItem = 0; pOffsetItem = new MMFOffsetItem; if (pOffsetItem == 0) throw CX_MemoryException(); //Set the file pointer to the start of this page... if (SetFilePointer(m_hFile, (LONG)dwBaseOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { //We are in trouble! _ASSERT(0, "WinMgmt: Failed to determine the size of the next block to load"); delete pOffsetItem; return 0; } //Read in the page information so we can find out how big the page is... DWORD dwActualRead = 0; MMF_PAGE_HEADER pageHeader; if ((ReadFile(m_hFile, &pageHeader, sizeof(MMF_PAGE_HEADER), &dwActualRead, 0) == 0) || (dwActualRead != sizeof(MMF_PAGE_HEADER))) { _ASSERT(0, "WinMgmt: Failed to read the next page block size"); delete pOffsetItem; return 0; } //Create the file mapping... HANDLE hMapping; hMapping = CreateFileMapping(m_hFile, 0, (m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT, 0, (LONG)dwBaseOffset + pageHeader.m_dwSize, 0); if (hMapping == 0) { _ASSERT(0, "WinMgmt: Failed to map in part of the memory mapped file!"); delete pOffsetItem; return 0; } //Map this into memory... LPBYTE pBindingAddress; pBindingAddress= (LPBYTE)MapViewOfFile(hMapping, (m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS), 0, (LONG)dwBaseOffset, pageHeader.m_dwSize); if (pBindingAddress == 0) { _ASSERT(0, "WinMgmt: Failed to bind part of the memory mapped file into memory!"); delete pOffsetItem; return 0; } //Record the information... pOffsetItem->m_dwBaseOffset = dwBaseOffset; pOffsetItem->m_dwBlockSize = pageHeader.m_dwSize; pOffsetItem->m_hMappingHandle = hMapping; pOffsetItem->m_pBasePointer = pBindingAddress; return pOffsetItem; } //*************************************************************************** // // CMMFArena2::CreateBasePage // // Creates a new base page and initialises it. // // dwInitSize : Initial size of the repotitory, which is also the size // of the first page. // dwGranularity : Size of mimimmum block allocation size... // // Return value : Pointer to an offset item filled in with the // page information. NULL if we fail to open the // page. // //*************************************************************************** MMFOffsetItem *CMMFArena2::CreateBasePage(DWORD dwInitSize, DWORD dwGranularity) { MMFOffsetItem *pOffsetItem = 0; pOffsetItem = new MMFOffsetItem; if (pOffsetItem == 0) throw CX_MemoryException(); // Create a file mapping for this base page... // =========================================== HANDLE hMapping = CreateFileMapping( m_hFile, // Disk file 0, // No security (m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT, // Extend the file to match the heap size 0, // High-order max size dwInitSize, // Low-order max size 0 // No name for the mapping object ); if (hMapping == NULL) { _ASSERT(0, "WinMgmt: Failed to create a new file mapping"); delete pOffsetItem; return 0; } // Map this page into memory... // ============================ LPBYTE pBindingAddress = (LPBYTE)MapViewOfFile(hMapping, (m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS), 0, 0, dwInitSize ); if (pBindingAddress == NULL) { _ASSERT(0, "WinMgmt: Failed to map in new page into memory"); CloseHandle(hMapping); delete pOffsetItem; return 0; } //Record the base pointer which is used throughout to hold important items //and information about the MMF. m_pHeapDescriptor = (MMF_ARENA_HEADER*) pBindingAddress; // Record the final heap address and set up the other related pointers. m_pHeapDescriptor->m_dwVersion = 0; m_pHeapDescriptor->m_dwGranularity = dwGranularity; m_pHeapDescriptor->m_dwCurrentSize = dwInitSize; m_pHeapDescriptor->m_dwMaxSize = 0; m_pHeapDescriptor->m_dwGrowBy = 0; m_pHeapDescriptor->m_dwHeapExtent = sizeof(MMF_ARENA_HEADER); m_pHeapDescriptor->m_dwEndOfHeap = dwInitSize; m_pHeapDescriptor->m_dwFreeList = 0; m_pHeapDescriptor->m_dwRootBlock = 0; m_pHeapDescriptor->m_dwSizeOfFirstPage = dwInitSize; //Now we have to add this information to the offset manager... pOffsetItem->m_dwBaseOffset = 0; pOffsetItem->m_pBasePointer = pBindingAddress; pOffsetItem->m_hMappingHandle = hMapping; pOffsetItem->m_dwBlockSize = dwInitSize; return pOffsetItem; } //*************************************************************************** // // CMMFArena2::CreateNewPage // // Creates a new page and initialises the header, returning a structure // detailing the page. // // dwBaseOffset : Base offset of this new page // dwSize : Size of this page // // Return value : Pointer to an offset item filled in with the // page information. NULL if we fail to open the // page. // //*************************************************************************** MMFOffsetItem *CMMFArena2::CreateNewPage(DWORD_PTR dwBaseOffset, DWORD dwSize) { MMFOffsetItem *pOffsetItem = 0; pOffsetItem = new MMFOffsetItem; if (pOffsetItem == 0) throw CX_MemoryException(); //Create the file mapping, this may grow the file... HANDLE hNewMapping; LPBYTE pNewBindingAddress = NULL; hNewMapping = CreateFileMapping( m_hFile, // Disk file 0, // No security (m_bReadOnlyAccess ? PAGE_READONLY : PAGE_READWRITE) | SEC_COMMIT, // Extend the file to match the heap size 0, // High-order max size (LONG)dwBaseOffset + dwSize, // Low-order max size 0 // No name for the mapping object ); DWORD dwErr = GetLastError(); if (hNewMapping != 0 && dwErr == 0) { //Map this into memory... pNewBindingAddress = (LPBYTE)MapViewOfFile( hNewMapping, (m_bReadOnlyAccess ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS), 0, (LONG)dwBaseOffset, // Old size used here!!! dwSize // Size of new part of mapping ); if (pNewBindingAddress) { //Record the information... pOffsetItem->m_dwBaseOffset = dwBaseOffset; pOffsetItem->m_pBasePointer = pNewBindingAddress; pOffsetItem->m_hMappingHandle = hNewMapping; pOffsetItem->m_dwBlockSize = dwSize; //Fill in the page header... MMF_PAGE_HEADER *pPageHeader = (MMF_PAGE_HEADER *)pNewBindingAddress; pPageHeader->m_dwSize = dwSize; } else { _ASSERT(0, "WinMgmt: Failed to map in page of MMF into memory"); ERRORTRACE((LOG_WBEMCORE, "Failed to map the new file mapping into memory for block size of %lu. Last Error = %lu\n", dwSize, GetLastError())); CloseHandle(hNewMapping); delete pOffsetItem; pOffsetItem = 0; } } else { _ASSERT(0, "WinMgmt: Failed to create a file mapping for the MMF"); if (hNewMapping != 0) CloseHandle(hNewMapping); ERRORTRACE((LOG_WBEMCORE, "Failed to create a new file mapping for %lu bytes block. Last Error = %lu\n", dwSize, dwErr)); delete pOffsetItem; pOffsetItem = 0; } return pOffsetItem; } //*************************************************************************** // // CMMFArena2::ClosePage // // Closes the page specified // // pOffsetItem : Information about the page to close. // // Return value : None // //*************************************************************************** void CMMFArena2::ClosePage(MMFOffsetItem *pOffsetItem) { if (pOffsetItem->m_hMappingHandle) { UnmapViewOfFile(pOffsetItem->m_pBasePointer); CloseHandle(pOffsetItem->m_hMappingHandle); } } //*************************************************************************** // // CMMFArena2::CloseAllPages // // Closes all pages in the offset manager, deleting the pointers of the // objects in there. // // Return value : None // //*************************************************************************** void CMMFArena2::CloseAllPages() { //Close each of the file mappings... for (int i = 0; i != m_OffsetManager.Size(); i++) { MMFOffsetItem *pItem = (MMFOffsetItem*)m_OffsetManager[i]; ClosePage(pItem); delete pItem; } m_OffsetManager.Empty(); } //*************************************************************************** // // CMMFArena2::~CMMFArena2 // // Destructor flushes the heap, unmaps the view and closes handles. // //*************************************************************************** CMMFArena2::~CMMFArena2() { if (m_hFile != INVALID_HANDLE_VALUE) { //Make sure what is in the MMF is flushed... Flush(); //Remember the size of the file.... DWORD dwFileSize = m_pHeapDescriptor->m_dwCurrentSize; //Close each of the file mappings... CloseAllPages(); //Set the size in case we messed it up if we failed to do a grow... SetFilePointer(m_hFile, dwFileSize, NULL, FILE_BEGIN); SetEndOfFile(m_hFile); //Close the file handle CloseHandle(m_hFile); } } //*************************************************************************** // // CMMFArena2::Alloc // // Allocates a new memory block. Uses the free list if a suitable block // can be found; grows the heap if required. // // Parameters: // Number of bytes to allocate. // // Return value: // Offsete of memory, or 0 on failure. // //*************************************************************************** DWORD_PTR CMMFArena2::Alloc(DWORD dwBytes) { //Allocation of 0 bytes returns 0! if (dwBytes == 0) return 0; if (m_dwStatus != 0) throw DATABASE_FULL_EXCEPTION(); //Make sure we are large enough block for any information we store in the //block when it is deleted if (dwBytes < sizeof(MMF_BLOCK_DELETED)) dwBytes = sizeof(MMF_BLOCK_DELETED); //Check the granularity of the size... DWORD dwGran = m_pHeapDescriptor->m_dwGranularity; dwBytes = (dwBytes - 1 + dwGran) / dwGran * dwGran; //Now adjust the size to include the header and trailer blocks... dwBytes += sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER); //Get the first item in the free-list pointer DWORD_PTR dwFreeBlock = m_pHeapDescriptor->m_dwFreeList; MMF_BLOCK_HEADER *pFreeBlock = (MMF_BLOCK_HEADER*)OffsetToPtr(dwFreeBlock); if (!pFreeBlock) return 0; //While free-list pointer is not NULL, and the size of the free-list pointer //block is too small while (dwFreeBlock && (GetSize(pFreeBlock) < dwBytes)) { ValidateBlock(dwFreeBlock); //Get the next block in the free-list pointer MMF_BLOCK_DELETED *pUserBlock = GetUserBlock(pFreeBlock); dwFreeBlock = pUserBlock->m_dwFLnext; pFreeBlock = (MMF_BLOCK_HEADER*)OffsetToPtr(dwFreeBlock); } //If the free-list pointer is NULL, we need to allocate more space in the arena if (dwFreeBlock == NULL) { //If there is not enough space at the end of the heap unused, we need to //grow the heap if (dwBytes > (m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent)) { //Mark the chunk of memory at the end of the arena as deleted as it is too //small to use... if (m_pHeapDescriptor->m_dwEndOfHeap != m_pHeapDescriptor->m_dwHeapExtent) { DecorateUsedBlock(m_pHeapDescriptor->m_dwHeapExtent, LONG(m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent)); Free(DWORD_PTR((LPBYTE)m_pHeapDescriptor->m_dwHeapExtent + sizeof(MMF_BLOCK_HEADER))); m_pHeapDescriptor->m_dwHeapExtent = m_pHeapDescriptor->m_dwEndOfHeap; } //If (Grow the arena by at least dwBytes) fails if (GrowArena(dwBytes) == 0) { //Fail the operation throw DATABASE_FULL_EXCEPTION(); m_dwStatus = 1; return 0; } } //We have a chunk of memory at the end of the arena which is not in the //free-list. We need to extend the size of the working arena and //use this. if ((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent - dwBytes) <= (sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER) + max(sizeof(MMF_BLOCK_DELETED), m_pHeapDescriptor->m_dwGranularity))) { dwBytes += DWORD(m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent) - dwBytes; } DWORD_PTR dwFreeBlock2 = m_pHeapDescriptor->m_dwHeapExtent; m_pHeapDescriptor->m_dwHeapExtent += dwBytes; DecorateUsedBlock(dwFreeBlock2, dwBytes); // _ASSERT((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent) > (sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER) + sizeof(MMF_BLOCK_DELETED)), "Space left at end of arena is too small"); ZeroOutBlock(dwFreeBlock2, 0xCD); return dwFreeBlock2 + sizeof(MMF_BLOCK_HEADER); } ValidateBlock(dwFreeBlock); //If this block is large enough to split if ((GetSize(pFreeBlock) - dwBytes) >= (sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER) + max(sizeof(MMF_BLOCK_DELETED), m_pHeapDescriptor->m_dwGranularity))) { //We need to split this large block into two and put the free-list //information from the start of this block into the newly //slit off bit MMF_BLOCK_DELETED *pFreeBlockUser = GetUserBlock(pFreeBlock); MMF_BLOCK_TRAILER *pFreeBlockTrailer = GetTrailerBlock(pFreeBlock); DecorateAsDeleted(dwFreeBlock + dwBytes, GetSize(pFreeBlock) - dwBytes, pFreeBlockUser->m_dwFLnext, pFreeBlockTrailer->m_dwFLback); //Now need to point the previous blocks and next blocks free-list pointers to point //to this new starting point. MMF_BLOCK_HEADER *pNewFreeBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwFreeBlock + dwBytes); MMF_BLOCK_DELETED *pNewFreeBlockUser = GetUserBlock(pNewFreeBlockHeader); MMF_BLOCK_TRAILER *pNewBlockTrailer = GetTrailerBlock(pNewFreeBlockHeader); //Deal with previous block... if (pNewBlockTrailer->m_dwFLback) { MMF_BLOCK_HEADER *pPrevFreeBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(pNewBlockTrailer->m_dwFLback); MMF_BLOCK_DELETED *pPrevFreeBlockUser = GetUserBlock(pPrevFreeBlockHeader); pPrevFreeBlockUser->m_dwFLnext = dwFreeBlock + dwBytes; } else { m_pHeapDescriptor->m_dwFreeList = dwFreeBlock + dwBytes; } //Deal with next block...if there is one if (pNewFreeBlockUser->m_dwFLnext) { MMF_BLOCK_HEADER *pNextFreeBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(pNewFreeBlockUser->m_dwFLnext); MMF_BLOCK_TRAILER *pNextFreeBlockTrailer = GetTrailerBlock(pNextFreeBlockHeader); pNextFreeBlockTrailer->m_dwFLback = dwFreeBlock + dwBytes; } //Set up the header and footer of this newly slit block DecorateUsedBlock(dwFreeBlock, dwBytes); } else { //Otherwise we need to remove this block from the free-list. RemoveBlockFromFreeList(pFreeBlock); //Populate this block with header and trailer information DecorateUsedBlock(dwFreeBlock, GetSize(pFreeBlock)); } ZeroOutBlock(dwFreeBlock, 0xCD); _ASSERT(!(((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent) != 0) && ((m_pHeapDescriptor->m_dwEndOfHeap - m_pHeapDescriptor->m_dwHeapExtent) < (sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_TRAILER) + sizeof(MMF_BLOCK_DELETED)))), "Space left at end of arena is too small"); //Return the offset to the caller. return dwFreeBlock + sizeof(MMF_BLOCK_HEADER); } //*************************************************************************** // // CMMFArena2::Realloc // // Allocates a new block of the requested size, copies the current // contents to it and frees up the original. If the original is // large enough it returns that. // // Parameters: // The address to reallocate // New requested size // // Return value: // TRUE on success, FALSE if not enough memory of corruption detected // //*************************************************************************** DWORD_PTR CMMFArena2::Realloc(DWORD_PTR dwOriginal, DWORD dwNewSize) { if (m_dwStatus != 0) throw DATABASE_FULL_EXCEPTION(); //Validate original block... ValidateBlock(dwOriginal - sizeof(MMF_BLOCK_HEADER)); MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwOriginal - sizeof(MMF_BLOCK_HEADER)); if (!pHeader) return 0; //If the original block is large enough, return the original if ((GetSize(pHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER)) >= dwNewSize) return dwOriginal; //Allocate a new block DWORD_PTR dwNewBlock = Alloc(dwNewSize); //If the allocation failed return NULL if (dwNewBlock == 0) return 0; //Copy orignial contents into it memcpy(OffsetToPtr(dwNewBlock), OffsetToPtr(dwOriginal), GetSize(pHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER)); //Free original block Free(dwOriginal); //Return newly allocated block return dwNewBlock; } //*************************************************************************** // // CMMFArena2::Free // // Frees a block of memory and places it on the free list. // // Parameters: // The address to 'free'. // // Return value: // TRUE on success, FALSE on erroneous address. // //*************************************************************************** BOOL CMMFArena2::Free(DWORD_PTR dwAddress) { //Freeing of NULL is OK! if (dwAddress == 0) return TRUE; if (m_dwStatus != 0) throw DATABASE_FULL_EXCEPTION(); //Set the address to point to the actual start of the block dwAddress -= sizeof(MMF_BLOCK_HEADER); //Check the block is valid... ValidateBlock(dwAddress); MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwAddress); if (!pBlockHeader) return FALSE; DWORD dwMappedBlockSize = 0; DWORD_PTR dwMappedBlockBaseAddress = GetBlockBaseAddress(dwAddress, dwMappedBlockSize); //If there is a block following this and it is deleted we should remove it from the free-list //chain and merge it with this block... That is if this is not the last block //in the heap! DWORD_PTR dwNextBlockAddress = dwAddress + GetSize(pBlockHeader); if ((dwNextBlockAddress < (dwMappedBlockBaseAddress + dwMappedBlockSize)) && (dwNextBlockAddress < m_pHeapDescriptor->m_dwHeapExtent)) { MMF_BLOCK_HEADER *pNextBlockHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwNextBlockAddress); if (!pNextBlockHeader) return FALSE; if (IsDeleted(pNextBlockHeader)) { //OK, we can now remove this block from the free-list RemoveBlockFromFreeList(pNextBlockHeader); DecorateUsedBlock(dwAddress, GetSize(pBlockHeader) + GetSize(pNextBlockHeader)); } } //If there is a deleted block before we have to merge this block into the previous one //The previous block is deleted if there is something in the FL back-pointer of the //previous blocks trailer. Make sure this is not the first block in the heap though!!! MMF_BLOCK_HEADER *pPrevBlockHeader = 0; if ((dwAddress != (dwMappedBlockBaseAddress + sizeof(MMF_PAGE_HEADER))) && (pPrevBlockHeader = GetPreviousDeletedBlock(pBlockHeader))) { //There is a deleted block prior to this one! We need to merge them MMF_BLOCK_DELETED *pPrevBlockUser = GetUserBlock(pPrevBlockHeader); MMF_BLOCK_TRAILER *pPrevBlockTrailer = GetTrailerBlock(pPrevBlockHeader); DecorateAsDeleted(PtrToOffset(LPBYTE(pPrevBlockHeader)), GetSize(pPrevBlockHeader) + GetSize(pBlockHeader), pPrevBlockUser->m_dwFLnext, pPrevBlockTrailer->m_dwFLback); //This block is now fully set up in the free-list!!! dwAddress = PtrToOffset(LPBYTE(pPrevBlockHeader)); ValidateBlock(dwAddress); } else { //We just add this block as it stands to the free-list chain. DecorateAsDeleted(dwAddress, GetSize(pBlockHeader), m_pHeapDescriptor->m_dwFreeList, NULL); //Point the free-list header pointer at us m_pHeapDescriptor->m_dwFreeList = dwAddress; //If there is a next block, set the next blocks previous FL pointer to us MMF_BLOCK_DELETED *pBlockUser = GetUserBlock(pBlockHeader); if (pBlockUser->m_dwFLnext) { MMF_BLOCK_HEADER *pNextBlockHeader = (MMF_BLOCK_HEADER*)(OffsetToPtr(pBlockUser->m_dwFLnext)); MMF_BLOCK_TRAILER *pNextBlockTrailer = GetTrailerBlock(pNextBlockHeader); pNextBlockTrailer->m_dwFLback = dwAddress; } ValidateBlock(dwAddress); } ZeroOutBlock(dwAddress, 0xDD); return TRUE; } //*************************************************************************** // // CMMFArena2::GrowArena // // Grows the head of the MMF by atleast the amount specified. It does not // add the space to the free-list. // // Parameters: // Number of bytes to allocate. // // Return value: // FALSE if cannot grow the heap (either because it is locked, or due to // a failure). TRUE otherwise. // //*************************************************************************** DWORD_PTR CMMFArena2::GrowArena(DWORD dwGrowBySize) { //Check that the arena is valid before continuing... if (m_dwStatus != 0) return 0; //Validation of heap and no grow passes 0 in! if (dwGrowBySize == 0) return 0; // We check the current size and see if applying // the m_dwGrowBy factor would increase the size beyond // the maximum heap size. // ====================================================== DWORD dwIncrement = 0; while (dwIncrement < dwGrowBySize) { if (((m_pHeapDescriptor->m_dwCurrentSize + dwIncrement) / 10) < m_dwMappingGranularity ) dwIncrement += m_dwMappingGranularity; else dwIncrement += (m_pHeapDescriptor->m_dwCurrentSize + dwIncrement) / 10; } //Cap the size of the page... //If we are a larger block that the max size allowed... cap it... if (dwIncrement > m_dwMaxPageSize) { dwIncrement = m_dwMaxPageSize; } // If here, we can try to increase the heap size by remapping. // =========================================================== MMFOffsetItem *pOffsetItem = 0; bool bLastTry = false; while (pOffsetItem == 0) { //Try and use a smaller increment until we fail to allocate the smallest block size possible... //--------------------------------------------------------------------------------------------- //Make sure this block is big enough for the request plus the page header... if (dwIncrement < (dwGrowBySize + sizeof(MMF_PAGE_HEADER))) { dwIncrement = dwGrowBySize + sizeof(MMF_PAGE_HEADER); bLastTry = true; } if (dwIncrement <= m_dwMappingGranularity) { bLastTry = true; } //Size should be of a specific granularity... if (dwIncrement % m_dwMappingGranularity) dwIncrement += (m_dwMappingGranularity - (dwIncrement % m_dwMappingGranularity)); pOffsetItem = CreateNewPage(m_pHeapDescriptor->m_dwCurrentSize, dwIncrement); if (pOffsetItem == 0) { //We failed! Can we try again... if (bLastTry) { //No! This was the smallest we could make the block return 0; } dwIncrement /= 2; } } if (pOffsetItem == 0) { return 0; } int nStatus = -1; nStatus = m_OffsetManager.InsertAt(m_OffsetManager.Size()-1, pOffsetItem); if (nStatus) { ClosePage(pOffsetItem); delete pOffsetItem; throw CX_MemoryException(); } DWORD dwNewSize = m_pHeapDescriptor->m_dwCurrentSize + dwIncrement; // If here, we succeeded completely // ================================ m_pHeapDescriptor->m_dwHeapExtent = m_pHeapDescriptor->m_dwCurrentSize + sizeof(MMF_PAGE_HEADER); m_pHeapDescriptor->m_dwCurrentSize = dwNewSize; m_pHeapDescriptor->m_dwEndOfHeap = dwNewSize; //get end of file item... and update it with new end offset MMFOffsetItem *pEndOffsetItem = (MMFOffsetItem *)m_OffsetManager[m_OffsetManager.Size() - 1]; pEndOffsetItem->m_dwBaseOffset = dwNewSize; return pOffsetItem->m_dwBaseOffset; } //*************************************************************************** // // CMMFArena2::Flush // // Flushes all uncommited changed to disk. // // Parameters: // // Return value: // TRUE if success. // //*************************************************************************** BOOL CMMFArena2::Flush() { //Need to loop through all mappings and flush them all... //Close each of the file mappings... for (int i = 0; i != m_OffsetManager.Size(); i++) { MMFOffsetItem *pItem = (MMFOffsetItem*)m_OffsetManager[i]; if (pItem->m_hMappingHandle) { FlushViewOfFile(pItem->m_pBasePointer, 0); } } return TRUE; } //*************************************************************************** // // CMMFArena2::Flush // // Flushes the page associated with the specified address // // Parameters: // offset within the MMF to flush // // Return value: // TRUE if success. // //*************************************************************************** BOOL CMMFArena2::Flush(DWORD_PTR dwAddress) { if (dwAddress > sizeof(MMF_BLOCK_HEADER)) { MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwAddress - sizeof(MMF_BLOCK_HEADER)); if (!FlushViewOfFile(pHeader, GetSize(pHeader))) return FALSE; return TRUE; } else return FALSE; } //*************************************************************************** // // CMMFArena2::DecorateUsedBlock // // Populates the header and trailer blocks in the specified block. // // Parameters: // Offset of block to decorate // Size of block // // Return value: // TRUE if success. // //*************************************************************************** BOOL CMMFArena2::DecorateUsedBlock(DWORD_PTR dwBlock, DWORD dwBytes) { MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock); if (!pHeader) return FALSE; pHeader->m_dwSize = (dwBytes & MMF_REMOVE_DELETED_MASK); MMF_BLOCK_TRAILER *pTrailer = GetTrailerBlock(pHeader); pTrailer->m_dwFLback = NULL; if (sizeof(pTrailer->m_dwCheckBlock)) { for (DWORD dwIndex = 0; dwIndex != (sizeof(pTrailer->m_dwCheckBlock) / sizeof(DWORD)); dwIndex++) { pTrailer->m_dwCheckBlock[dwIndex] = MMF_DEBUG_INUSE_TAG; } } return TRUE; } //*************************************************************************** // // CMMFArena2::DecorateAsDeleted // // Populates the header and trailer blocks in the specified block with // everything needed to make it look like a deleted block. Updates // the items associated with the free list within the block // // Parameters: // Offset of block to decorate // Size of block // next item in the free-list // dwPrevFreeListOffset<> previous item in the free list. // // Return value: // TRUE if success. // //*************************************************************************** BOOL CMMFArena2::DecorateAsDeleted(DWORD_PTR dwBlock, DWORD dwBytes, DWORD_PTR dwNextFreeListOffset, DWORD_PTR dwPrevFreeListOffset) { MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock); if (!pHeader) return FALSE; pHeader->m_dwSize = (dwBytes | MMF_DELETED_MASK); MMF_BLOCK_DELETED *pUserBlock = GetUserBlock(pHeader); MMF_BLOCK_TRAILER *pTrailer = GetTrailerBlock(pHeader); pTrailer->m_dwFLback = dwPrevFreeListOffset; pUserBlock->m_dwFLnext = dwNextFreeListOffset; if (sizeof(pTrailer->m_dwCheckBlock)) { for (DWORD dwIndex = 0; dwIndex != (sizeof(pTrailer->m_dwCheckBlock) / sizeof(DWORD)); dwIndex++) { pTrailer->m_dwCheckBlock[dwIndex] = MMF_DEBUG_DELETED_TAG; } } return TRUE; } //*************************************************************************** // // CMMFArena2::ValidateBlock // // Validates the memory block as much as possible and calls a debug break // point if an error is detected. Does this by analysing the size and // the trailer DWORDs // // Parameters: // Offset of block to check // // Return value: // TRUE if success. // //*************************************************************************** #if (defined DEBUG || defined _DEBUG) BOOL CMMFArena2::ValidateBlock(DWORD_PTR dwBlock) { try { MMF_BLOCK_HEADER *pHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock); MMF_BLOCK_TRAILER *pTrailer = GetTrailerBlock(pHeader); if (sizeof(pTrailer->m_dwCheckBlock)) { DWORD dwCheckBit; //Is it deleted? if (pHeader->m_dwSize & MMF_DELETED_MASK) { //Yes it is, so the we check for 0xFFFF dwCheckBit = MMF_DEBUG_DELETED_TAG; } else { dwCheckBit = MMF_DEBUG_INUSE_TAG; } for (DWORD dwIndex = 0; dwIndex != (sizeof(pTrailer->m_dwCheckBlock) / sizeof(DWORD)); dwIndex++) { if (pTrailer->m_dwCheckBlock[dwIndex] != dwCheckBit) { TCHAR string[200]; wsprintf(string, __TEXT("WinMgmt: MMF Arena heap corruption,offset = 0x%p\n"), dwBlock); OutputDebugString(string); MMFDebugBreak(); _ASSERT(0, string); return FALSE; } } } if (!(pHeader->m_dwSize & MMF_DELETED_MASK)) { //We are not deleted, so we should have a trailer back pointer of NULL if (pTrailer->m_dwFLback != 0) { TCHAR string[200]; wsprintf(string, __TEXT("WinMgmt: MMF Arena heap corruption, offset = 0x%p\n"), dwBlock); OutputDebugString(string); MMFDebugBreak(); _ASSERT(0, string); return FALSE; } } } catch (...) { TCHAR string[200]; wsprintf(string, __TEXT("WinMgmt: MMF Arena heap corruption, offset = 0x%p\n"), dwBlock); OutputDebugString(string); MMFDebugBreak(); _ASSERT(0, string); return FALSE; } return TRUE; } #endif //*************************************************************************** // // CMMFArena2::RemoveBlockFromFreeList // // Removes the specified block from the free-list. // // Parameters: // pointer of block to remove // // Return value: // TRUE if success. // //*************************************************************************** BOOL CMMFArena2::RemoveBlockFromFreeList(MMF_BLOCK_HEADER *pBlockHeader) { //Bit of validation... if (!IsDeleted(pBlockHeader)) { TCHAR string[200]; wsprintf(string, __TEXT("WinMgmt: MMF Arena heap corruption: deleting deleted block, offset = 0x%p\n"), Fixdown(pBlockHeader)); OutputDebugString(string); MMFDebugBreak(); _ASSERT(0, string); return FALSE; } MMF_BLOCK_DELETED *pUserBlock = GetUserBlock(pBlockHeader); MMF_BLOCK_TRAILER *pTrailerBlock = GetTrailerBlock(pBlockHeader); //Deal with next block if it exists if (pUserBlock->m_dwFLnext != 0) { //This has a next block. Its back pointer needs to point to our back pointer MMF_BLOCK_HEADER *pNextFreeBlock = (MMF_BLOCK_HEADER *)OffsetToPtr(pUserBlock->m_dwFLnext); if (!pNextFreeBlock) return FALSE; MMF_BLOCK_TRAILER *pNextTrailerBlock = GetTrailerBlock(pNextFreeBlock); pNextTrailerBlock->m_dwFLback = pTrailerBlock->m_dwFLback; } //Deal with previous block if it exists if (pTrailerBlock->m_dwFLback == 0) { //This is the first item in the free-list so the heap descriptor points to it. //Now needs to point to the next item. m_pHeapDescriptor->m_dwFreeList = pUserBlock->m_dwFLnext; } else { //This has a previous block in the list MMF_BLOCK_HEADER *pPrevFreeBlock = (MMF_BLOCK_HEADER *)OffsetToPtr(pTrailerBlock->m_dwFLback); MMF_BLOCK_DELETED *pPrevUserBlock = GetUserBlock(pPrevFreeBlock); pPrevUserBlock->m_dwFLnext = pUserBlock->m_dwFLnext; } return TRUE; } //*************************************************************************** // // CMMFArena2::GetPreviousDeletedBlock // // If the previous block in the MMF is deleted it returns the pointer to // it. If there the previous block is not deleted or there is no // previous block it returns NULL // // Parameters: // pointer to the current block // // Return value: // NULL if no previous deleted block, otherwise pointer to one. // //*************************************************************************** MMF_BLOCK_HEADER *CMMFArena2::GetPreviousDeletedBlock(MMF_BLOCK_HEADER *pBlockHeader) { //If this is the first block there is no previous block... if (PtrToOffset(LPBYTE(pBlockHeader)) == sizeof(MMF_ARENA_HEADER)) return NULL; //Only do anything if there are deleted blocks (we have to do this check for the //special case, so might as well use it around everything!) if (m_pHeapDescriptor->m_dwFreeList) { //Special case of when the previous block is the head block, in which case the trailer of the //previous block is NULL! MMF_BLOCK_HEADER *pHeadFreeBlock = (MMF_BLOCK_HEADER*)OffsetToPtr(m_pHeapDescriptor->m_dwFreeList); if (!pHeadFreeBlock) return NULL; if ((LPBYTE(pHeadFreeBlock) + GetSize(pHeadFreeBlock)) == LPBYTE(pBlockHeader)) { //The head block is the previous deleted block! return pHeadFreeBlock; } //Get the trailer pointer to the previous block... MMF_BLOCK_TRAILER *pPrevBlockTrailer = (MMF_BLOCK_TRAILER *)(LPBYTE(pBlockHeader) - sizeof(MMF_BLOCK_TRAILER)); if (pPrevBlockTrailer->m_dwFLback == NULL) { //The previous block is not deleted. (if this was the head free-list item we would //have returned it in the special case!) return NULL; } //Now we need to get the previous deleted block to this previous block! MMF_BLOCK_HEADER *pPrevPrevBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(pPrevBlockTrailer->m_dwFLback); //Now need this blocks user section so we can get the start of the next free-list item (the one we //want!) MMF_BLOCK_DELETED *pPrevPrevBlockUser = (MMF_BLOCK_DELETED *)(LPBYTE(pPrevPrevBlockHeader) + sizeof(MMF_BLOCK_HEADER)); return (MMF_BLOCK_HEADER *)OffsetToPtr(pPrevPrevBlockUser->m_dwFLnext); } //There are no deleted blocks! return NULL; } #if (defined DEBUG || defined _DEBUG) BOOL CMMFArena2::ZeroOutBlock(DWORD_PTR dwBlock, int cFill) { MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(dwBlock); if (IsDeleted(pBlockHeader)) { memset(LPBYTE(pBlockHeader) + sizeof(MMF_BLOCK_HEADER) + sizeof(MMF_BLOCK_DELETED), cFill, GetSize(pBlockHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER) - sizeof(MMF_BLOCK_DELETED)); } else { memset(LPBYTE(pBlockHeader) + sizeof(MMF_BLOCK_HEADER), cFill, GetSize(pBlockHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER)); } return TRUE; } #endif //Some debugging functions... void CMMFArena2::Reset() { // Move cursor to first block. // =========================== m_dwCursor = sizeof(MMF_ARENA_HEADER); } BOOL CMMFArena2::Next(LPBYTE *ppBlock, DWORD *pdwSize, BOOL *pbActive) { if (m_dwCursor >= m_pHeapDescriptor->m_dwHeapExtent) return FALSE; // Return the block address and block size values to the caller. // We strip out the MS bit of the block size, since it is not // part of the size, but indicates whether or not the block is // part of the free list or part of the active chain. // ============================================================= MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER *)OffsetToPtr(m_dwCursor); if (!pBlockHeader) return FALSE; *ppBlock = LPBYTE(pBlockHeader) + sizeof(MMF_BLOCK_HEADER); // Move past the head block. *pdwSize = GetSize(pBlockHeader) - sizeof(MMF_BLOCK_HEADER) - sizeof(MMF_BLOCK_TRAILER); *pbActive = !IsDeleted(pBlockHeader); // Advance our cursor. // =================== m_dwCursor += GetSize(pBlockHeader); return TRUE; } //*************************************************************************** // // CMMFArena::TextDump // // Dumps a summary of the heap to a text file. // // Parameters: // The text file to which to perform the dump. This // file is appended to. // //*************************************************************************** void CMMFArena2::TextDump(const char *pszFile) { FILE *f = fopen(pszFile, "at"); if (!f) return; LPBYTE pAddress = 0; DWORD dwSize = 0; BOOL bActive = 0; // Primary heap. // ============= fprintf(f, "---Primary Heap Contents---\n"); Reset(); while (Next(&pAddress, &dwSize, &bActive)) { fprintf(f, "BLOCK 0x%X Size=%06d ", PtrToOffset(pAddress), dwSize); if (bActive) fprintf(f, "(active) :"); else fprintf(f, "(deleted) :"); for (int i = 0; i < (int) dwSize && i < 32; i++) if (pAddress[i] < 32) fprintf(f, "."); else fprintf(f, "%c", pAddress[i]); fprintf(f, "\n"); } fclose(f); } //*************************************************************************** // // CMMFArena::GetHeapInfo // // Gets detailed summary info about the heap. Completely walks the // heap to do this. // // Parameters: // Receives the heap size. // Receives the number of allocated blocks. // Receives the total allocated bytes. // Receives the number of 'free' blocks. // Receives the number of 'free' bytes. // //*************************************************************************** void CMMFArena2::GetHeapInfo( DWORD *pdwTotalSize, DWORD *pdwActiveBlocks, DWORD *pdwActiveBytes, DWORD *pdwFreeBlocks, DWORD *pdwFreeBytes ) { LPBYTE pAddress = 0; DWORD dwSize = 0; BOOL bActive = 0; if (pdwTotalSize) *pdwTotalSize = m_pHeapDescriptor->m_dwCurrentSize; if (pdwActiveBlocks) *pdwActiveBlocks = 0; if (pdwActiveBytes) *pdwActiveBytes = 0; if (pdwFreeBlocks) *pdwFreeBlocks = 0; if (pdwFreeBytes) *pdwFreeBytes = 0; Reset(); while (Next(&pAddress, &dwSize, &bActive)) { if (bActive) { if (pdwActiveBytes) (*pdwActiveBytes) += dwSize; if (pdwActiveBlocks) (*pdwActiveBlocks)++; } else { if (pdwFreeBytes) (*pdwFreeBytes) += dwSize; if (pdwFreeBlocks) (*pdwFreeBlocks)++; } } } BOOL CMMFArena2::FileValid(const TCHAR *pszFilename) { BOOL bValid = FALSE; HANDLE hFile = CreateFile( pszFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, // Share mode = exclusive 0, // Security OPEN_EXISTING, // Creation distribution FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, // Attribute 0 // Template file ); if (hFile == INVALID_HANDLE_VALUE) { //This means the file does not exist or something screwy has happened. return FALSE; } MMF_ARENA_HEADER header; DWORD dwBytesRead; if (ReadFile(hFile, &header, sizeof(MMF_ARENA_HEADER), &dwBytesRead, NULL)) { if (sizeof(MMF_ARENA_HEADER) == dwBytesRead) { if (header.m_dwVersion <= 3) { //The header exists, now we need to check to make sure the file length is //correct... //Magic numbers :-) This is how the Version 3 or less size was calculated. //actually it was sizeof the header, which was 568 in those days. if (GetFileSize(hFile, NULL) == (header.m_dwCurrentSize + 568)) { bValid = TRUE; } else { //The sizes are not the same... } } else { //The header exists, now we need to check to make sure the file length is //correct... if (GetFileSize(hFile, NULL) == header.m_dwCurrentSize) { bValid = TRUE; } else { //The sizes are not the same... } } } else { //Implies the file length may have been zeroed out or something } } else { //Implies the file length may have been zeroed out or something } CloseHandle(hFile); _ASSERT(bValid == TRUE, "Repository size is not the same as the size we think it should be."); return bValid; } DWORD_PTR CMMFArena2::GetRootBlock() { if (m_pHeapDescriptor->m_dwVersion <= 3) { DWORD_PTR *pMem = (DWORD_PTR*) m_pHeapDescriptor; return pMem[9]; } else { return (m_pHeapDescriptor ? m_pHeapDescriptor->m_dwRootBlock : 0); } } DWORD CMMFArena2::Size(DWORD_PTR dwBlock) { if (m_dwStatus != 0) throw DATABASE_FULL_EXCEPTION(); //Set the address to point to the actual start of the block dwBlock -= sizeof(MMF_BLOCK_HEADER); //Check the block is valid... ValidateBlock(dwBlock); MMF_BLOCK_HEADER *pBlockHeader = (MMF_BLOCK_HEADER*)OffsetToPtr(dwBlock); if (pBlockHeader) return GetSize(pBlockHeader); else return 0; } //Given an offset, returns a fixed up pointer LPBYTE CMMFArena2::OffsetToPtr(DWORD_PTR dwOffset) { if (dwOffset == 0) return 0; if (m_dwStatus != 0) throw DATABASE_FULL_EXCEPTION(); try { LPBYTE pBlock = 0; int l = 0, u = m_OffsetManager.Size() - 1; while (l <= u) { int m = (l + u) / 2; if (dwOffset < ((MMFOffsetItem *)m_OffsetManager[m])->m_dwBaseOffset) { u = m - 1; } else if (dwOffset >= ((MMFOffsetItem *)m_OffsetManager[m+1])->m_dwBaseOffset) { l = m + 1; } else { return ((MMFOffsetItem *)m_OffsetManager[m])->m_pBasePointer + (dwOffset - ((MMFOffsetItem *)m_OffsetManager[m])->m_dwBaseOffset); } } } catch (...) { } TCHAR string[220]; wsprintf(string, __TEXT("WinMgmt: Could not find the block requested in the repository, offset requested = 0x%p, end of repository = 0x%p\n"), dwOffset, ((MMFOffsetItem *)m_OffsetManager[m_OffsetManager.Size()-1])->m_dwBaseOffset); OutputDebugString(string); _ASSERT(0, string); MMFDebugBreak(); return 0; } //Given a pointer, returns an offset from the start of the MMF DWORD_PTR CMMFArena2::PtrToOffset(LPBYTE pBlock) { if (m_dwStatus != 0) throw DATABASE_FULL_EXCEPTION(); for (int i = 0; i < m_OffsetManager.Size(); i++) { register MMFOffsetItem *pItem = (MMFOffsetItem *)m_OffsetManager[i]; if ((pBlock >= pItem->m_pBasePointer) && (pBlock < (pItem->m_pBasePointer + pItem->m_dwBlockSize))) { return pItem->m_dwBaseOffset + (pBlock - pItem->m_pBasePointer); } } TCHAR string[220]; wsprintf(string, __TEXT("WinMgmt: Could not find the offset requested in the repository, pointer requested = 0x%p\n"), pBlock); OutputDebugString(string); _ASSERT(0, string); MMFDebugBreak(); return 0; } DWORD_PTR CMMFArena2::GetBlockBaseAddress(DWORD_PTR dwOffset, DWORD &dwSize) { LPBYTE pBlock = 0; int l = 0, u = m_OffsetManager.Size() - 1; while (l <= u) { int m = (l + u) / 2; if (dwOffset < ((MMFOffsetItem *)m_OffsetManager[m])->m_dwBaseOffset) { u = m - 1; } else if (dwOffset >= ((MMFOffsetItem *)m_OffsetManager[m+1])->m_dwBaseOffset) { l = m + 1; } else { register MMFOffsetItem *pItem = (MMFOffsetItem *)m_OffsetManager[m]; dwSize = pItem->m_dwBlockSize; return pItem->m_dwBaseOffset; } } TCHAR string[220]; wsprintf(string, __TEXT("WinMgmt: Could not find the block requested in the repository, offset requested = 0x%p, end of repository = 0x%p\n"), dwOffset, ((MMFOffsetItem *)m_OffsetManager[m_OffsetManager.Size()-1])->m_dwBaseOffset); OutputDebugString(string); _ASSERT(0, string); MMFDebugBreak(); return 0; }