windows-nt/Source/XPSP1/NT/inetsrv/query/ntciutil/secstore.cxx
2020-09-26 16:20:57 +08:00

874 lines
24 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 2000.
//
// File: secstore.cxx
//
// Contents: SDID to security descriptor mapping table
//
// History: 29 Jan 1996 AlanW Created
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <cistore.hxx>
#include <rcstxact.hxx>
#include <rcstrmit.hxx>
#include <catalog.hxx>
#include <secstore.hxx>
//+-------------------------------------------------------------------------
//
// Method: CSdidLookupTable::CSdidLookupTable, public
//
// Synopsis: Constructor of a CSdidLookupTable
//
// Arguments: -NONE-
//
// Returns: Nothing
//
//--------------------------------------------------------------------------
CSdidLookupTable::CSdidLookupTable( )
: _pTable( 0 ),
_xrsoSdidTable( 0 ),
_mutex(),
_cache()
{
}
void CSdidLookupTable::Empty()
{
CLock lock ( _mutex );
delete [] _pTable; _pTable = 0;
_xrsoSdidTable.Free();
_cache.Empty();
}
CSdidLookupTable::~CSdidLookupTable( )
{
Empty();
}
//+---------------------------------------------------------------------------
//
// Member: CSdidLookupTable::Init, public
//
// Synopsis: Loads metadata from persistent location into memory.
//
// Arguments: [pobj] -- Stream(s) in which metadata is stored.
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
BOOL CSdidLookupTable::Init( CiStorage * pobj )
{
CLock lock ( _mutex );
_xrsoSdidTable.Set( pobj->QuerySdidLookupTable( eSecStoreWid ) );
//
// Load header
//
CRcovStorageHdr & hdr = _xrsoSdidTable->GetHeader();
struct CRcovUserHdr data;
hdr.GetUserHdr( hdr.GetPrimary(), data );
RtlCopyMemory( &_Header, &data._abHdr, sizeof(_Header) );
ciDebugOut(( DEB_SECSTORE, "SECSTORE: Record size = %d bytes\n", _Header.cbRecord ));
ciDebugOut(( DEB_SECSTORE, "SECSTORE: %d file records\n", _Header.cRecords ));
ciDebugOut(( DEB_SECSTORE, "SECSTORE: Hash size = %u\n", _Header.cHash ));
if ( _Header.cHash == 0 )
{
Win4Assert( 0 == Records() && _Header.cbRecord == 0 );
RtlCopyMemory( _Header.Signature, "SECSTORE", sizeof _Header.Signature );
Win4Assert( (sizeof (SSdHeaderRecord) + SECURITY_DESCRIPTOR_MIN_LENGTH)
< SECSTORE_REC_SIZE );
_Header.cbRecord = SECSTORE_REC_SIZE;
_Header.cHash = SECSTORE_HASH_SIZE;
_Header.cRecords = 0;
}
else
{
Win4Assert( RtlEqualMemory( _Header.Signature, "SECSTORE",
sizeof _Header.Signature) &&
_Header.cbRecord == SECSTORE_REC_SIZE &&
_Header.cHash == SECSTORE_HASH_SIZE );
if ( ! RtlEqualMemory( _Header.Signature, "SECSTORE",
sizeof _Header.Signature) ||
_Header.cbRecord != SECSTORE_REC_SIZE ||
_Header.cHash != SECSTORE_HASH_SIZE )
return FALSE;
}
//
// Load hash table
//
ULONG cRecordsFromFile = Records();
ULONG iRecord = 1;
_pTable = new SDID [ _Header.cHash ];
RtlZeroMemory( _pTable, _Header.cHash * sizeof (SDID) );
_Header.cRecords = 0;
#if (DBG == 1)
_cMaxChainLen = 0;
_cTotalSearches = 0;
_cTotalLength = 0;
#endif // (DBG == 1)
CRcovStrmReadTrans xact( _xrsoSdidTable.GetReference() );
CRcovStrmReadIter iter( xact, SECSTORE_REC_SIZE );
BYTE temp[ SECSTORE_REC_SIZE ];
while ( iter.GetRec( &temp, iRecord-1 ) )
{
SSdHeaderRecord SdHdr = *(SSdHeaderRecord *)temp;
Win4Assert( SdHdr.cbSD >= SECURITY_DESCRIPTOR_MIN_LENGTH &&
SdHdr.cbSD < 256 * 1024 &&
_pTable[ SdHdr.ulHash % SECSTORE_HASH_SIZE ] ==
SdHdr.iHashChain );
if ( SdHdr.cbSD < SECURITY_DESCRIPTOR_MIN_LENGTH ||
SdHdr.cbSD >= 256 * 1024 ||
_pTable[ SdHdr.ulHash % SECSTORE_HASH_SIZE ] != SdHdr.iHashChain )
return FALSE;
_pTable[ SdHdr.ulHash % SECSTORE_HASH_SIZE ] = iRecord;
ciDebugOut(( DEB_SECSTORE,
"SECSTORE: SD record\tSDID = %d, cb = %d, hash = %08x, chain = %d\n",
iRecord, SdHdr.cbSD, SdHdr.ulHash, SdHdr.iHashChain ));
#ifdef UNIT_TEST
// much below is debug code; don't need to allocate
// the SD here; just seek to the start of each record
// and read the record header.
XArray<BYTE> pbSD ( SdHdr.cbSD );
BYTE * pbDst = pbSD.GetPointer();
BYTE * pbSrc = &temp[0] + sizeof (SSdHeaderRecord);
ULONG cb = SdHdr.cbSD;
ULONG cbPart = SECSTORE_REC_SIZE - sizeof (SSdHeaderRecord);
if (cb < cbPart)
cbPart = cb;
RtlCopyMemory( pbDst, pbSrc, cbPart );
pbDst += cbPart;
cb -= cbPart;
pbSrc = &temp[0];
while( 0 != cb )
{
iter.GetRec( temp );
cbPart = (cb > SECSTORE_REC_SIZE) ? SECSTORE_REC_SIZE : cb;
RtlCopyMemory( pbDst, pbSrc, cbPart );
pbDst += cbPart;
cb -= cbPart;
pbSrc = &temp[0];
iRecord++;
}
PSECURITY_DESCRIPTOR pSD = pbSD.GetPointer();
Win4Assert( SdHdr.cbSD == GetSecurityDescriptorLength( pSD ) &&
SdHdr.ulHash == Hash( pSD, SdHdr.cbSD ) );
iRecord++;
#else
iRecord += (SdHdr.cbSD + (sizeof SdHdr) + SECSTORE_REC_SIZE - 1) /
SECSTORE_REC_SIZE;
#endif
_Header.cRecords = iRecord - 1;
}
Win4Assert( Records() == cRecordsFromFile );
return TRUE;
}
//+-------------------------------------------------------------------------
//
// Method: CSdidLookupTable::Hash, public
//
// Synopsis: Generate a hash value for the passed SECURITY_DESCRIPTOR
//
// Arguments: [pSD] -- pointer to SECURITY_DESCRIPTOR
// [cb] -- length of SECURITY_DESCRIPTOR in bytes
//
// Returns: ULONG - Hash value for the input SECURITY_DESCRIPTOR
//
//--------------------------------------------------------------------------
ULONG CSdidLookupTable::Hash(const PSECURITY_DESCRIPTOR pSD, unsigned cb)
{
ULONG ulHash = 0;
BYTE * pb = (BYTE *) pSD;
while (cb-- != 0)
{
if (ulHash & 0x80000000)
{
ulHash = (ulHash << 1) | 1;
}
else
{
ulHash <<= 1;
}
ulHash ^= *pb++;
}
return(ulHash);
}
//+---------------------------------------------------------------------------
//
// Method: CSdidLookupTable::Lookup, private
//
// Synopsis: Looks up a security descriptor in the table.
//
// Arguments: [sdid] - SDID to look up.
//
// Returns: CSdidLookupEntry* - pointer to entry for SDID
//
// History: 29 Jan 1996 Alanw Created
//
// Notes: The security descriptor entry will be owned by
// the caller after the call.
//
//----------------------------------------------------------------------------
CSdidLookupEntry * CSdidLookupTable::Lookup( SDID sdid )
{
Win4Assert( sdid <= Records() );
Win4Assert( !_xrsoSdidTable.IsNull() );
if ( sdid > Records() )
{
return 0;
}
CSdidLookupEntry * pEntry = 0;
CLock lock ( _mutex );
//
// First see if the desired item is in the cache
//
for ( CSdidCacheIter listiter( _cache );
!_cache.AtEnd( listiter );
_cache.Advance( listiter ) )
{
if ( listiter.GetEntry()->Sdid() == sdid )
{
pEntry = listiter.GetEntry();
_cache.RemoveFromList( pEntry );
return pEntry;
}
}
XPtr<CSdidLookupEntry> xEntry;
//
// The entry was not in the cache. Read it from storage.
//
TRY
{
// Corrupt?
if (_xrsoSdidTable.IsNull())
THROW(CException(CI_CORRUPT_DATABASE));
xEntry.Set( new CSdidLookupEntry(sdid) );
CRcovStrmReadTrans xact( _xrsoSdidTable.GetReference() );
CRcovStrmReadIter iter( xact, SECSTORE_REC_SIZE );
LoadTableEntry( iter, xEntry.GetReference(), sdid );
}
CATCH(CException, e)
{
ciDebugOut(( DEB_WARN, "CSdidLookupTable::Lookup - exception %x\n",
e.GetErrorCode() ));
if (e.GetErrorCode() != STATUS_ACCESS_VIOLATION)
RETHROW();
}
END_CATCH
return xEntry.Acquire();
}
//+---------------------------------------------------------------------------
//
// Method: CSdidLookupTable::LookUpSDID, public
//
// Synopsis: Looks up a security descriptor's ID in the table.
// Add the SD to the table if not found.
//
// Arguments: [pSD] - SD to look up.
// [cbSD] - size of security descriptor
//
// Returns: SDID - ID of security descriptor input
//
// History: 29 Jan 1996 Alanw Created
//
// Notes:
//
//----------------------------------------------------------------------------
SDID CSdidLookupTable::LookupSDID( PSECURITY_DESCRIPTOR pSD, ULONG cbSD )
{
Win4Assert( (((SECURITY_DESCRIPTOR *)pSD)->Control & SE_SELF_RELATIVE) &&
GetSecurityDescriptorLength( pSD ) == cbSD );
Win4Assert( !_xrsoSdidTable.IsNull() );
SDID iSdid = 0;
BOOL fFound = FALSE;
#if (DBG == 1)
ULONG cSearchLen = 0;
#endif // (DBG == 1)
ULONG ulHash = Hash( pSD, cbSD );
CLock lock ( _mutex );
//
// First see if a matching item is in the cache
//
for ( CSdidCacheIter listiter( _cache );
!_cache.AtEnd( listiter );
_cache.Advance( listiter ) )
{
if ( listiter.GetEntry()->IsEqual( pSD, cbSD, ulHash ) )
{
Win4Assert( listiter.GetEntry()->Sdid() > 0 );
return listiter.GetEntry()->Sdid();
}
}
//
// The SD was not found in the cache. Try looking in storage.
//
TRY
{
SDID iNext = _pTable[ ulHash % HashSize() ];
if (iNext != 0)
{
CRcovStrmReadTrans xact( _xrsoSdidTable.GetReference() );
CRcovStrmReadIter iter( xact, SECSTORE_REC_SIZE );
BYTE temp[ SECSTORE_REC_SIZE ];
while (iNext != 0)
{
#if (DBG == 1)
cSearchLen++;
#endif // (DBG == 1)
iter.GetRec( &temp, iNext-1 );
SSdHeaderRecord * pSdHdr = (SSdHeaderRecord *)temp;
Win4Assert( pSdHdr->cbSD >= SECURITY_DESCRIPTOR_MIN_LENGTH &&
pSdHdr->iHashChain < iNext );
if (pSdHdr->cbSD == cbSD && pSdHdr->ulHash == ulHash)
{
// The byte count and hash value match. Fetch the rest of
// the SD to compare it byte-for-byte.
XPtr<CSdidLookupEntry> xEntry( new CSdidLookupEntry( iNext ) );
LoadTableEntry( iter, xEntry.GetReference(), iNext );
if (RtlEqualMemory( pSD, xEntry->GetSD(), cbSD))
{
fFound = TRUE;
iSdid = iNext;
_cache.Add( xEntry.Acquire() );
}
}
iNext = pSdHdr->iHashChain;
}
}
if (! fFound)
{
//
// The SD was not found.
// Write new mapping to the recoverable storage.
//
iSdid = Records() + 1;
CRcovStorageHdr & hdr = _xrsoSdidTable->GetHeader();
CRcovStrmAppendTrans xact( _xrsoSdidTable.GetReference() );
CRcovStrmAppendIter iter( xact, SECSTORE_REC_SIZE );
BYTE temp[ SECSTORE_REC_SIZE ];
SSdHeaderRecord * pSdHdr = (SSdHeaderRecord *)temp;
pSdHdr->cbSD = cbSD;
pSdHdr->ulHash = ulHash;
pSdHdr->iHashChain = _pTable[ ulHash % HashSize() ];
BYTE * pbDst = &temp[0] + sizeof (SSdHeaderRecord);
BYTE * pbSrc = (BYTE *)pSD;
ULONG cb = cbSD;
ULONG cbPart = SECSTORE_REC_SIZE - sizeof (SSdHeaderRecord);
if (cb < cbPart)
cbPart = cb;
RtlCopyMemory( pbDst, pbSrc, cbPart );
pbSrc += cbPart;
cb -= cbPart;
iter.AppendRec( temp );
ULONG cRecordsWritten = 1;
while( 0 != cb )
{
if (cb >= SECSTORE_REC_SIZE)
{
iter.AppendRec( pbSrc );
cbPart = SECSTORE_REC_SIZE;
}
else
{
cbPart = (cb > SECSTORE_REC_SIZE) ? SECSTORE_REC_SIZE : cb;
RtlCopyMemory( temp, pbSrc, cbPart );
RtlZeroMemory( &temp[cbPart], SECSTORE_REC_SIZE - cbPart );
iter.AppendRec( temp );
}
pbSrc += cbPart;
cb -= cbPart;
cRecordsWritten++;
}
ciDebugOut(( DEB_SECSTORE,
"SECSTORE: new SD record\tSDID = %d, cb = %d, hash = %08x, chain = %d\n",
iSdid, cbSD, ulHash, _pTable[ ulHash % SECSTORE_HASH_SIZE ] ));
_pTable[ ulHash % SECSTORE_HASH_SIZE ] = iSdid;
_Header.cRecords += cRecordsWritten;
struct CRcovUserHdr data;
RtlCopyMemory( &data._abHdr, &_Header, sizeof(_Header) );
Win4Assert( hdr.GetCount(hdr.GetBackup()) == hdr.GetCount(hdr.GetPrimary()) + cRecordsWritten);
hdr.SetUserHdr( hdr.GetBackup(), data );
xact.Commit();
}
}
CATCH(CException, e)
{
ciDebugOut(( DEB_WARN, "CSdidLookupTable::LookupSDID - exception %x\n",
e.GetErrorCode() ));
if (e.GetErrorCode() == STATUS_ACCESS_VIOLATION)
{
Win4Assert( !"Access violation in CSdidLookupTable::LookupSDID - "
"Are you running two queries on the same downlevel catalog?" );
}
RETHROW();
}
END_CATCH
#if (DBG == 1)
// Update search statistics
_cTotalSearches++;
if (fFound)
{
_cTotalLength += cSearchLen;
}
else
{
if (cSearchLen >= _cMaxChainLen)
_cMaxChainLen = cSearchLen + 1;
}
#endif // (DBG == 1)
return iSdid;
}
//+---------------------------------------------------------------------------
//
// Method: CSdidLookupTable::LoadTableEntry, private
//
// Synopsis: Loads a table entry for some SDID from the table.
//
// Arguments: [Iter] - CRcovStrmReadIter for access to the stream
// [Entry] - CSdidTableEntry to be filled in
// [iSdid] - SDID to be looked up.
//
// Returns: Nothing
//
// History: 29 Jan 1996 Alanw Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CSdidLookupTable::LoadTableEntry(
CRcovStrmReadIter & iter,
CSdidLookupEntry & Entry,
SDID iSdid )
{
Win4Assert( iSdid <= Records() );
BYTE temp[ SECSTORE_REC_SIZE ];
iter.GetRec( &temp, iSdid-1 );
Entry._hdr = *(SSdHeaderRecord *)temp;
Win4Assert( Entry._hdr.cbSD >= SECURITY_DESCRIPTOR_MIN_LENGTH &&
Entry._hdr.cbSD < 256 * 1024 );
ciDebugOut(( DEB_SECSTORE,
"SECSTORE: SD record\tSDID = %d, cb = %d, hash = %08x, chain = %d\n",
iSdid, Entry._hdr.cbSD, Entry._hdr.ulHash, Entry._hdr.iHashChain ));
XArray<BYTE> pbSD ( Entry._hdr.cbSD );
BYTE * pbDst = pbSD.GetPointer();
BYTE * pbSrc = &temp[0] + sizeof (SSdHeaderRecord);
ULONG cb = Entry._hdr.cbSD;
ULONG cbPart = SECSTORE_REC_SIZE - sizeof (SSdHeaderRecord);
if (cb < cbPart)
cbPart = cb;
RtlCopyMemory( pbDst, pbSrc, cbPart );
pbDst += cbPart;
cb -= cbPart;
pbSrc = &temp[0];
while( 0 != cb )
{
if (cb >= SECSTORE_REC_SIZE)
{
iter.GetRec( pbDst );
cbPart = SECSTORE_REC_SIZE;
}
else
{
iter.GetRec( temp );
cbPart = (cb > SECSTORE_REC_SIZE) ? SECSTORE_REC_SIZE : cb;
RtlCopyMemory( pbDst, pbSrc, cbPart );
}
pbDst += cbPart;
cb -= cbPart;
pbSrc = &temp[0];
}
Win4Assert( Entry._hdr.cbSD == GetSecurityDescriptorLength( pbSD.GetPointer() ) &&
Entry._hdr.ulHash == Hash( pbSD.GetPointer(), Entry._hdr.cbSD ) );
Entry._pSD = pbSD.Acquire();
}
//+---------------------------------------------------------------------------
//
// Method: CSdidLookupTable::AccessCheck, public
//
// Synopsis: Performs an access check for some SDID, access mask combination
//
// Arguments: [sdid] - SDID of file to be checked
// [hToken] - security token to be checked against
// [am] - access mode to be checked against
// [fGranted] - TRUE is access is granted, FALSE otherwise
//
// Returns: BOOL - TRUE if access check was successful
//
// History: 05 Feb 1996 Alanw Created
//
// Notes:
//
//----------------------------------------------------------------------------
GENERIC_MAPPING gmFile = {
FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE,
FILE_ALL_ACCESS
};
BOOL CSdidLookupTable::AccessCheck(
SDID sdid,
HANDLE hToken,
ACCESS_MASK am,
BOOL & fGranted )
{
Win4Assert( sdidInvalid != sdid && sdidNull != sdid );
CSdidLookupEntry * pSD = Lookup( sdid );
fGranted = FALSE;
if ( 0 == pSD )
return FALSE;
PRIVILEGE_SET ps;
ULONG ulPrivSize = sizeof ps;
ACCESS_MASK GrantedAccess;
BOOL fResult = ::AccessCheck( pSD->GetSD(),
hToken,
am,
&gmFile,
&ps,
&ulPrivSize,
&GrantedAccess,
&fGranted);
{
CLock lock ( _mutex );
_cache.Add( pSD );
}
return fResult;
}
//+---------------------------------------------------------------------------
//
// Member: CSdidLookupTable::GetSecurityDescriptor
//
// Synopsis: Retrieves the security descriptor for the given SDID.
//
// Arguments: [sdid] - SDID to lookup
// [pbData] - Pointer to the buffer to write the desc.
// [cbIn] - Size of the pSD buffer
// [cbOut] - Size of the security descriptor; if cbIn < cbOut,
// then the buffer is not big enough to copy the data.
//
// Returns: S_OK if successfully returned.
// S_FALSE if the buffer is not big enough to hold
// the data. In this case cbOut will have the actual buffer
// needed.
// CI_E_NOT_FOUND the sdid is not valid.
//
// History: 7-18-97 srikants Created
//
//----------------------------------------------------------------------------
HRESULT
CSdidLookupTable::GetSecurityDescriptor(
SDID sdid,
PSECURITY_DESCRIPTOR pbData,
ULONG cbIn,
ULONG & cbOut )
{
Win4Assert( sdidInvalid != sdid && sdidNull != sdid );
CSdidLookupEntry * pSD = Lookup( sdid );
if ( 0 == pSD )
return CI_E_NOT_FOUND;
cbOut = pSD->Size();
if ( cbOut > cbIn )
return S_FALSE;
RtlCopyMemory( pbData, pSD->GetSD(), cbOut );
return S_OK;
}
//+---------------------------------------------------------------------------
//
// Member: CSdidLookupTable::Save
//
// Synopsis: Makes a copy of the current security table using the
// destination storage object.
//
// Arguments: [pIProgressNotify] - Progress notification.
// [fAbort] - Flag set to TRUE if the copy must
// be aborted in the middle.
// [dstStorage] - Destination storage object to use
// for creating the bakcup.
// [ppFileList] - List of files that constitute the
// the security store.
//
// History: 7-14-97 srikants Created
//
//----------------------------------------------------------------------------
void CSdidLookupTable::Save( IProgressNotify * pIProgressNotify,
BOOL & fAbort,
CiStorage & dstStorage,
IEnumString **ppFileList )
{
dstStorage.RemoveSecStore( eSecStoreWid );
XPtr<PRcovStorageObj> xObj( dstStorage.QuerySdidLookupTable( eSecStoreWid ) );
// ===============================================================
CLock lock ( _mutex );
//
// Make a copy of the security table.
//
Win4Assert( !_xrsoSdidTable.IsNull() );
CCopyRcovObject copyRcov( xObj.GetReference(),
_xrsoSdidTable.GetReference() );
copyRcov.DoIt();
//
// Retrive the names of the files that constitute the security store.
//
CEnumString * pEnumString = new CEnumString();
XInterface<IEnumString> xEnumStr(pEnumString);
dstStorage.ListSecStoreFileNames( *pEnumString, 0 );
*ppFileList = xEnumStr.Acquire();
// ===============================================================
}
//+---------------------------------------------------------------------------
//
// Member: CSdidLookupTable::Load
//
// Synopsis: Loads the security store from a saved location into the
// target directory.
//
// Arguments: [pwszDestDir] - Destination directory to load to
// [pFileList] - List of the files.
// [pProgressNotify] - Progress notification.
// [fCallerOwnsFiles] - If the caller owns files.
// [pfAbort] - Set to TRUE if must be aborted.
//
// History: 7-18-97 srikants Created
//
//----------------------------------------------------------------------------
void CSdidLookupTable::Load( CiStorage * pobj,
IEnumString * pFileList,
IProgressNotify * pProgressNotify,
BOOL fCallerOwnsFiles,
BOOL * pfAbort )
{
// ===============================================================
CLock lock ( _mutex );
Win4Assert(pobj);
Win4Assert(pFileList);
Win4Assert(pfAbort);
ULONG ulFetched;
WCHAR * pwszFilePath;
while ( !(*pfAbort) && (S_OK == pFileList->Next(1, &pwszFilePath, &ulFetched)) )
{
pobj->CopyGivenFile( pwszFilePath, !fCallerOwnsFiles );
}
}
//+---------------------------------------------------------------------------
//
// Method: CSdidCache::Add, public
//
// Synopsis: Add an SDID record to the lookaside cache
//
// Arguments: [pEntry] - the item to be added to the cache
//
// Returns: Nothing
//
// History: 18 Apr 1996 Alanw Created
//
// Notes: The cache must be locked when this method is called.
//
//----------------------------------------------------------------------------
void CSdidCache::Add( CSdidLookupEntry * pEntry )
{
//
// If the cache is full, check to see if the item is in the cache. Another
// copy may have been added while we were using this one.
// If there is space in the cache, allow multiple copies of the same SDID.
//
if ( Count() >= _maxEntries )
{
for ( CSdidCacheIter iter( *this );
!AtEnd( iter );
Advance( iter ) )
{
if ( iter.GetEntry()->Sdid() == pEntry->Sdid() )
{
MoveToFront( iter.GetEntry() );
delete pEntry;
return;
}
}
}
//
// Add the entry to the front of the list. If there are too many
// items in the cache, delete the last entry.
//
Push( pEntry );
if ( Count() > _maxEntries )
{
delete RemoveLast();
}
}
//+---------------------------------------------------------------------------
//
// Method: CSdidCache::Empty, public
//
// Synopsis: Clean out the lookaside cache
//
// Arguments: NONE
//
// Returns: Nothing
//
// History: 18 Apr 1996 Alanw Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CSdidCache::Empty( )
{
CSdidLookupEntry * pEntry = 0;
while ( pEntry = Pop() )
delete pEntry;
}