windows-nt/Source/XPSP1/NT/inetcore/digest/mmfile.cxx
2020-09-26 16:20:57 +08:00

816 lines
22 KiB
C++

/*++
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;
}