|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
mmfile.cxx
Abstract:
Generic shared memory allocator.
Author:
Adriaan Canter (adriaanc) 01-Aug-1998
--*/ #include "include.hxx"
//--------------------------------------------------------------------
// CMMFile::CMMFile()
//--------------------------------------------------------------------
CMMFile::CMMFile(DWORD cbHeap, DWORD cbEntry) : _cbHeap(cbHeap), _cbEntry(cbEntry) { // BUGBUG - assert this.
// Heap size must be multiple of entry size
// and entry size must be multiple of 2.
if ((_cbHeap % _cbEntry) || _cbEntry % 2) { DIGEST_ASSERT(FALSE); _dwStatus = ERROR_INVALID_PARAMETER; goto exit; }
// Max entries and total bytes in bitmap.
_nMaxEntries = _cbHeap / _cbEntry; _cbBitMap = _nMaxEntries / NUM_BITS_IN_BYTE;
// Total DWORDs in bitmap and total
// bytes in shared memory.
_nBitMapDwords = _cbBitMap / sizeof(DWORD); _cbTotal = sizeof(MEMMAP_HEADER) + _cbBitMap + _cbHeap;
_pHeader = NULL; _pBitMap = NULL; _pHeap = NULL;
// BUGBUG - _hFile -> INVALID_HANDLE_VALUE
_hFile = NULL; _dwSig = SIG_CMMF; _dwStatus = ERROR_SUCCESS;
exit: return; }
//--------------------------------------------------------------------
// CMMFile::~CMMFile
//--------------------------------------------------------------------
CMMFile::~CMMFile() { // BUGBUG - protected
DeInit(); }
//--------------------------------------------------------------------
// Init
//--------------------------------------------------------------------
DWORD CMMFile::Init() { BOOL fFirstProc = FALSE; LPVOID pv; HANDLE hHandle = (HANDLE) -1; CHAR szMapName[MAX_PATH]; DWORD cbMapName = MAX_PATH;
// IE5# 89288
// Get map name based on user
if ((_dwStatus = MakeUserObjectName(szMapName, &cbMapName, MAKE_MAP_NAME)) != ERROR_SUCCESS) return _dwStatus;
// BUGBUG - security attributes and -1->INVALID_HANDLE_VALUE
// Create the file mapping handle.
_hFile = CreateFileMapping( hHandle, // 0xffffffff file handle -> backed by paging file.
NULL, // Security attributes.
PAGE_READWRITE // Generic read+write.
| SEC_RESERVE, // Reserve only, don't commit.
0, // dwMaximumSizeHigh
_cbTotal, // dwMaximumSizeLow
szMapName // Map name.
);
// BUGBUG -> COMMENT HERE
_dwStatus = GetLastError();
// Check for success (can already exist) or failure.
if (_dwStatus == ERROR_SUCCESS) fFirstProc = TRUE; else if (_dwStatus != ERROR_ALREADY_EXISTS) { DIGEST_ASSERT(FALSE); goto exit; }
// bugbug - file_map_write logic.
// Create file mapping view.
pv = MapViewOfFileEx( _hFile, // File mapping handle.
FILE_MAP_WRITE, // Read and write access.
0, // High 32 bit offset
0, // Low 32 bit offset
0, // Map entire file.
NULL // System chooses base addr.
);
if(!pv) { _dwStatus = GetLastError(); DIGEST_ASSERT(FALSE); goto exit; }
// Calculate pointers to bitmap and heap.
_pHeader = (LPMEMMAP_HEADER) pv; _pBitMap = (LPDWORD) ((LPBYTE) pv + sizeof(MEMMAP_HEADER)); _pHeap = ((LPBYTE) _pBitMap + _cbBitMap);
// Initialize MM file if first process.
if (fFirstProc) { // Commit header + bitmap.
if (!VirtualAlloc(_pHeader, sizeof(MEMMAP_HEADER) + _cbBitMap, MEM_COMMIT, PAGE_READWRITE)) { _dwStatus = GetLastError(); goto exit; }
//BUGBUG - zero out first.
// Set signature.
memcpy(_pHeader->szSig, MMF_SIG_SZ, MMF_SIG_SIZE);
// Zero out the rest of the header + bitmap.
memset((LPBYTE) _pHeader + MMF_SIG_SIZE, 0, sizeof(MEMMAP_HEADER) - MMF_SIG_SIZE + _cbBitMap); }
_dwStatus = ERROR_SUCCESS;
exit:
return _dwStatus; }
//--------------------------------------------------------------------
// DeInit
//--------------------------------------------------------------------
DWORD CMMFile::DeInit() { // BUGBUG - should always close _hFile
if (!UnmapViewOfFile((LPVOID) _pHeader)) { DIGEST_ASSERT(FALSE); _dwStatus = GetLastError(); goto exit; }
if (!CloseHandle(_hFile)) { DIGEST_ASSERT(FALSE); _dwStatus = GetLastError(); goto exit; }
_dwStatus = ERROR_SUCCESS;
exit:
return _dwStatus; }
//--------------------------------------------------------------------
// CMMFile::CheckNextNBits
//
// Determines if the next N bits are unset.
//
// Arguments:
//
// [IN/OUT]
// DWORD &nArrayIndex, DWORD &dwMask
//
// [IN]
// DWORD nBitsRequired
//
// [OUT]
// DWORD &nBitsFound
//
// Return Value:
//
// TRUE if the next N bits were found unset.
// FALSE otherwise.
//
// Notes:
// This function assumes that the range of bits to be checked lie
// within a valid area of the bit map.
//--------------------------------------------------------------------
BOOL CMMFile::CheckNextNBits(DWORD& nArrayIndex, DWORD& dwStartMask, DWORD nBitsRequired, DWORD& nBitsFound) { DWORD i; DWORD nIdx = nArrayIndex; DWORD dwMask = dwStartMask; BOOL fFound = FALSE; LPDWORD BitMap = &_pBitMap[nIdx];
nBitsFound = 0;
// Check if the next nBitsRequired bits are unset
for (i = 0; i < nBitsRequired; i++) { // Is this bit unset?
if ((*BitMap & dwMask) == 0) { // Have sufficient unset bits been found?
if (++nBitsFound == nBitsRequired) { // Found sufficient bits. Success.
fFound = TRUE; goto exit; } }
// Ran into a set bit. Fail.
else { // Indicate the array and bit index
// of the set bit encountered.
nArrayIndex = nIdx; dwStartMask = dwMask; goto exit; }
// Left rotate the bit mask.
dwMask <<= 1; if (dwMask == 0x0) { dwMask = 0x1; BitMap = &_pBitMap[++nIdx]; }
} // Loop nBitsRequired times.
exit: return fFound; }
//--------------------------------------------------------------------
// CMMFile::SetNextNBits
//
// Given an array index and bit mask, sets the next N bits.
//
// Arguments:
// [IN]
// DWORD nIdx, DWORD dwMask, DWORD nBitsRequired
//
// Return Value:
//
// TRUE if the next N bits were found unset, and successfully set.
// FALSE if unable to set all the required bits.
//
// Notes:
// This function assumes that the range of bits to be set lie
// within a valid area of the bit map. If the function returns
// false, no bits are set.
//--------------------------------------------------------------------
BOOL CMMFile::SetNextNBits(DWORD nIdx, DWORD dwMask, DWORD nBitsRequired) { DWORD i, j, nBitsSet = 0; LPDWORD BitMap = &_pBitMap[nIdx];
for (i = 0; i < nBitsRequired; i++) { // Check that this bit is not already set.
if (*BitMap & dwMask) { // Fail. Unset the bits we just set and exit.
for (j = nBitsSet; j > 0; j--) { DIGEST_ASSERT((*BitMap & dwMask) == 0);
// Right rotate the bit mask.
dwMask >>= 1; if (dwMask == 0x0) { dwMask = 0x80000000; BitMap = &_pBitMap[--nIdx]; } *BitMap &= ~dwMask; } return FALSE; }
*BitMap |= dwMask; nBitsSet++;
// Left rotate the bit mask.
dwMask <<= 1; if (dwMask == 0x0) { dwMask = 0x1; BitMap = &_pBitMap[++nIdx]; }
}
// Success.
return TRUE; }
//--------------------------------------------------------------------
// CMMFile::GetAndSetNextFreeEntry
//
// Computes the first available free entry index.
//
// Arguments:
//
// DWORD nBitsRequired
//
// Return Value:
//
// Next available free entry Index.
//--------------------------------------------------------------------
DWORD CMMFile::GetAndSetNextFreeEntry(DWORD nBitsRequired) { DWORD nReturnBit = 0xFFFFFFFF;
// Align if 4k or greater
BOOL fAlign = (nBitsRequired >= NUM_BITS_IN_DWORD ? TRUE : FALSE);
// Scan DWORDS from the beginning of the byte array.
DWORD nArrayIndex = 0; while (nArrayIndex < _nBitMapDwords) { // Process starting from this DWORD if alignment is not required
// and there are free bits, or alignment is required and all bits
// are free.
if (_pBitMap[nArrayIndex] != 0xFFFFFFFF && (!fAlign || (fAlign && _pBitMap[nArrayIndex] == 0))) { DWORD nBitIndex = 0; DWORD dwMask = 0x1; LPDWORD BitMap = &_pBitMap[nArrayIndex];
// Find a candidate slot.
while (nBitIndex < NUM_BITS_IN_DWORD) { // Found first bit of a candidate slot.
if ((*BitMap & dwMask) == 0) { // Calculate leading bit value.
DWORD nLeadingBit = NUM_BITS_IN_DWORD * nArrayIndex + nBitIndex;
// Don't exceed max number of entries.
if (nLeadingBit + nBitsRequired > _nMaxEntries) { // Overstepped last internal entry
goto exit; }
// If we just need one bit, then we're done.
if (nBitsRequired == 1) { *BitMap |= dwMask; nReturnBit = nLeadingBit; _pHeader->nEntries += 1; goto exit; }
// Additional bits required.
DWORD nBitsFound; DWORD nIdx = nArrayIndex;
// Check the next nBitsRequired bits. Set them if free.
if (CheckNextNBits(nIdx, dwMask, nBitsRequired, nBitsFound)) { if (SetNextNBits(nIdx, dwMask, nBitsRequired)) { // Return the offset of the leading bit.
_pHeader->nEntries += nBitsRequired; nReturnBit = nLeadingBit; goto exit; } // Bad news.
else { // The bits are free, but we couldn't set them. Fail.
DIGEST_ASSERT(FALSE); goto exit; } } else { // This slot has insufficient contiguous free bits.
// Update the array index. We break back to looping
// over the bits in the DWORD where the interrupting
// bit was found.
nArrayIndex = nIdx; nBitIndex = (nBitIndex + nBitsFound) % NUM_BITS_IN_DWORD; break; }
} // Found a free leading bit.
else { // Continue looking at bits in this DWORD.
nBitIndex++; dwMask <<= 1; }
} // Loop over bits in DWORD.
} // If we found a candidate DWORD.
nArrayIndex++;
} // Loop through all DWORDS.
exit: return nReturnBit; }
//--------------------------------------------------------------------
// CMMFile::AllocateEntry
//
// Routine Description:
//
// Member function that returns an free entry from the cache list. If
// none is available free, it grows the map file, makes more free
// entries.
//
// Arguments:
//
// DWORD cbBytes : Number of bytes requested
// DWORD cbOffset: Offset from beginning of bit map where allocation is
// requested.
//
// Return Value:
//
//
//--------------------------------------------------------------------
LPMAP_ENTRY CMMFile::AllocateEntry(DWORD cbBytes) { LPMAP_ENTRY NewEntry;
// BUGBUG - ASSERT THIS.
// Validate cbBytes
if (cbBytes > MAX_ENTRY_SIZE) { return NULL; }
// Find and mark off a set of contiguous bits
// spanning the requested number of bytes.
DWORD nBlocksRequired = NUMBLOCKS(ROUNDUPBLOCKS(cbBytes), _cbEntry); DWORD FreeEntryIndex = GetAndSetNextFreeEntry(nBlocksRequired);
// Failed to find space.
if( FreeEntryIndex == 0xFFFFFFFF ) { DIGEST_ASSERT(FALSE); return NULL; }
// Cast the memory.
NewEntry = (LPMAP_ENTRY) (_pHeap + _cbEntry * FreeEntryIndex);
if (!VirtualAlloc(NewEntry, nBlocksRequired * _cbEntry, MEM_COMMIT, PAGE_READWRITE)) { DIGEST_ASSERT(FALSE); _dwStatus = GetLastError(); return NULL; }
// Mark the allocated space.
NewEntry->dwSig = SIG_ALLOC;
// Set the number of blocks in the entry.
NewEntry->nBlocks = nBlocksRequired;
return NewEntry; }
//--------------------------------------------------------------------
// Routine Description:
//
// Attempts to reallocate an entry at the location given.
//
// Arguments:
//
// LPMAP_ENTRY pEntry: Pointer to location in file map.
// DWORD cbBytes : Number of bytes requested
//
// Return Value:
//
// Original value of pEntry if successful. pEntry->nBlocks is set to
// the new value, but all other fields in the entry are unmodified.
// If insufficient contiguous bits are found at the end of the original
// entry, NULL is returned, indicating failure. In this case the entry
// remains unmodified.
//
//--------------------------------------------------------------------
// BUGBUG -> remove ?
BOOL CMMFile::ReAllocateEntry(LPMAP_ENTRY pEntry, DWORD cbBytes) { // Validate cbBytes
if (cbBytes > MAX_ENTRY_SIZE) { DIGEST_ASSERT(FALSE); return FALSE; }
// Validate pEntry.
DWORD cbEntryOffset = (DWORD)((DWORD_PTR) pEntry - (DWORD_PTR) _pBitMap); if ((cbEntryOffset == 0) || (cbEntryOffset & (_cbEntry-1)) || (cbEntryOffset >= _cbTotal)) { DIGEST_ASSERT(FALSE); return FALSE; }
// Calculate number of blocks required for this entry.
DWORD nBlocksRequired = NUMBLOCKS(ROUNDUPBLOCKS(cbBytes), _cbEntry);
// Sufficient space in current slot?
if (nBlocksRequired <= pEntry->nBlocks) { // We're done.
return TRUE; } else { // Determine if additional free bits are
// available at the end of this entry.
// If not, return NULL.
// Determine the array and bit indicese of the first
// free bit immediately following the last set bit of
// the entry.
DWORD nTrailingIndex = cbEntryOffset / _cbEntry + pEntry->nBlocks; DWORD nArrayIndex = nTrailingIndex / NUM_BITS_IN_DWORD; DWORD nBitIndex = nTrailingIndex % NUM_BITS_IN_DWORD; DWORD dwMask = 0x1 << nBitIndex; DWORD nAdditionalBlocksRequired = nBlocksRequired - pEntry->nBlocks; DWORD nBlocksFound;
// Don't exceed the number of internal entries.
if (nTrailingIndex + nAdditionalBlocksRequired > _nMaxEntries) { // Overstepped last internal entry. Here we should fail
// by returning NULL. Note - DO NOT attempt to grow the
// map file at this point. The caller does not expect this.
return FALSE; }
if (CheckNextNBits(nArrayIndex, dwMask, nAdditionalBlocksRequired, nBlocksFound)) { // We were able to grow the entry.
SetNextNBits(nArrayIndex, dwMask, nAdditionalBlocksRequired); pEntry->nBlocks = nBlocksRequired; _pHeader->nEntries += nAdditionalBlocksRequired; return TRUE; } else // Couldn't grow the entry.
return FALSE; } }
//--------------------------------------------------------------------
// CMMFile::FreeEntry
// This public member function frees up a file cache entry.
//
// Arguments:
//
// UrlEntry : pointer to the entry that being freed.
//
// Return Value:
//
// TRUE - if the entry is successfully removed from the cache.
// FALSE - otherwise.
//
//--------------------------------------------------------------------
BOOL CMMFile::FreeEntry(LPMAP_ENTRY Entry) { DWORD nIndex, nArrayIndex, nOffset, nBlocks, BitMask;
LPDWORD BitMap;
// Validate the pointer passed in.
if(((LPBYTE) Entry < _pHeap) || ((LPBYTE) Entry >= (_pHeap + _cbEntry * _nMaxEntries))) { DIGEST_ASSERT(FALSE); return FALSE; }
// Compute and check offset (number of bytes from start).
nOffset = (DWORD)((DWORD_PTR)Entry - (DWORD_PTR)_pHeap); if( nOffset % _cbEntry ) { // Pointer does not point to a valid entry.
DIGEST_ASSERT(FALSE); return FALSE; }
nBlocks = Entry->nBlocks;
if (nBlocks > (MAX_ENTRY_SIZE / _cbEntry)) { DIGEST_ASSERT(FALSE); return FALSE; }
// Compute indicese
nIndex = nOffset / _cbEntry; nArrayIndex = nIndex / NUM_BITS_IN_DWORD;
// Unmark the index bits in the map.
BitMap = &_pBitMap[nArrayIndex]; BitMask = 0x1 << (nIndex % NUM_BITS_IN_DWORD); for (DWORD i = 0; i < nBlocks; i++) { // Check we don't free unset bits
if (!(*BitMap & BitMask)) { DIGEST_ASSERT(FALSE); return FALSE; }
*BitMap &= ~BitMask; BitMask <<= 1; if (BitMask == 0x0) { BitMask = 0x1; BitMap = &_pBitMap[++nArrayIndex]; } }
// Mark the freed space.
ResetEntryData(Entry, SIG_FREE, nBlocks);
// Reduce the count of allocated entries.
// INET_ASSERT (_HeaderInfo->NumUrlEntriesAlloced > 0);
_pHeader->nEntries -= nBlocks;
return TRUE; }
//--------------------------------------------------------------------
// CMMFile::SetHeaderData
//--------------------------------------------------------------------
VOID CMMFile::SetHeaderData(DWORD dwHeaderIndex, DWORD dwHeaderValue) { _pHeader->dwHeaderData[dwHeaderIndex] = dwHeaderValue; }
//--------------------------------------------------------------------
// CMMFile::GetHeaderData
//--------------------------------------------------------------------
LPDWORD CMMFile::GetHeaderData(DWORD dwHeaderIndex) { return &_pHeader->dwHeaderData[dwHeaderIndex]; }
//--------------------------------------------------------------------
// CMMFile::ResetEntryData
//--------------------------------------------------------------------
VOID CMMFile::ResetEntryData(LPMAP_ENTRY Entry, DWORD dwResetValue, DWORD nBlocks) { for (DWORD i = 0; i < (_cbEntry * nBlocks) / sizeof(DWORD); i++) { *((DWORD*) Entry + i) = dwResetValue; } }
//--------------------------------------------------------------------
// CMMFile::GetMapPtr
//--------------------------------------------------------------------
DWORD_PTR CMMFile::GetMapPtr() { return (DWORD_PTR) _pHeader; }
//--------------------------------------------------------------------
// CMMFile::GetStatus
//--------------------------------------------------------------------
DWORD CMMFile::GetStatus() { return _dwStatus; }
//--------------------------------------------------------------------
// CMMFile::MakeUserObjectName
// Added in fix for IE5# 89288
// (inet\digest\init.cxx:MakeFullAccessSA: NULL DACL is no protection)
//
// Generates either of the names
// <Local\>SSPIDIGESTMMMAP:username
// <Local\>SSPIDIGESTMUTEX:username
// where Local\ is prepended on NT, username from GetUserName
//--------------------------------------------------------------------
DWORD CMMFile::MakeUserObjectName(LPSTR szName, LPDWORD pcbName, DWORD dwFlags) { DWORD cbLocal, cbRequired, cbUserName = MAX_PATH, dwError = ERROR_SUCCESS; CHAR szUserName[MAX_PATH]; DWORD cbNameFixed = (DWORD)((dwFlags == MAKE_MUTEX_NAME ? sizeof(SZ_MUTEXNAME) - 1 : sizeof(SZ_MAPNAME) - 1));
// Get platform info.
OSVERSIONINFO osInfo; osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&osInfo)) { dwError = GetLastError(); goto exit; }
// Prepend "Local\" if on >= NT5
if ((osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osInfo.dwMajorVersion >= 5)) { // We will prepend "Local\"
cbLocal = sizeof("Local\\") - 1; } else { // We will not prepend "Local\"
cbLocal = 0; }
// Get username. Default to "anyuser" if not present.
if (!GetUserName(szUserName, &cbUserName)) { cbUserName = sizeof("anyuser"); memcpy(szUserName, "anyuser", cbUserName); } // Required size for name
// eg <Local\>SSPIDIGESTMUTEX:username
cbRequired = cbLocal + cbNameFixed + cbUserName; if (cbRequired > *pcbName) { *pcbName = cbRequired; dwError = ERROR_INSUFFICIENT_BUFFER; goto exit; } // <Local\>
// (cbLocal may be 0)
memcpy(szName, "Local\\", cbLocal); // <Local\>SSPIDIGESTMUTEX:
memcpy(szName + cbLocal, (dwFlags == MAKE_MUTEX_NAME ? SZ_MUTEXNAME : SZ_MAPNAME), cbNameFixed);
// <Local\> SSPIDIGESTMUTEX:username
// cbUsername includes null terminator.
memcpy(szName + cbLocal + cbNameFixed, szUserName, cbUserName);
*pcbName = cbRequired;
exit: return dwError; }
|