//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1991 - 1999 // // File: filidmap.cxx // // Contents: FileId to wid mapping using hashed access // // History: 07-May-97 SitaramR Created // // Notes: Only ntfs 5.0 wids are added to the file id map because all // non-ntfs 5.0 wids map to fileIdInvalid, which means that there // will be too many collisions on fileIdInvalid, which will degrade // the performance of the hashed access. One drawback of mapping // only ntfs 5.0 wids is that the count of wids in fileid map and // property store cannot be checked for equality to detect // corruption as is done for the strings table. However, there is a // correlation between a count mismatch in strings table and in // file id map table because the file id map table is flushed before // the strings table. As a result, when a corruption is detected // in strings table, a corruption error is thrown and the processing // for that corruption error reinitializes both strings table and the // file id map table. // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include //+--------------------------------------------------------------------------- // // Member: CFileIdMap::CFileIdMap // // Synopsis: Constructor // // Arguments: [propStoreMgr] -- Property store // [cicat] -- CiCat // // History: 07-May-97 SitaramR Created // //---------------------------------------------------------------------------- CFileIdMap::CFileIdMap( CPropStoreManager &propStoreMgr ) : CPersHash( propStoreMgr, FALSE ), _cCacheEntries( 0 ) #if CIDBG == 1 , _cCacheHits( 0 ), _cCacheNegativeHits( 0 ), _cCacheMisses( 0 ), _cCacheNegativeMisses( 0 ) #endif { } //+--------------------------------------------------------------------------- // // Member: CFileIdMap::FastInit // // Synopsis: Fast initialization // // Arguments: [pStorage] -- Ci storage // [pwcsCatDir] -- Catalog directory // [pwcsFile] -- File id file name // [version] -- Content index version // // History: 28-Jul-97 SitaramR Created // 13-Mar-98 KitmanH Passed in True to // CPerHash::FastInit to specify // that a FileIdMap is wanted // //---------------------------------------------------------------------------- BOOL CFileIdMap::FastInit( CiStorage * pStorage, ULONG version ) { return CPersHash::FastInit(pStorage, version, TRUE); } //+--------------------------------------------------------------------------- // // Member: CFileIdMap::LongInit // // Synopsis: Initialization that may take a long time // // Arguments: [version] -- CI version // [fDirtyShutdown] -- Set to TRUE if the previous shutdown // was dirty. // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- void CFileIdMap::LongInit ( ULONG version, BOOL fDirtyShutdown ) { CPersHash::LongInit( version, fDirtyShutdown ); } //+--------------------------------------------------------------------------- // // Member: CFileIdMap::LokFlush // // Synopsis: Flushes fileid map // // History: 28-Jul-97 SitaramR Created // 27-Feb-98 KitmanH Don't flush if catalog is // read-only // //---------------------------------------------------------------------------- void CFileIdMap::LokFlush() { if ( !_fIsReadOnly ) CPersHash::LokFlush(); } //LokFlush //+------------------------------------------------------------------------- // // Member: CFileIdMap::AddToCache, private // // Synopsis: Adds an entry to the cache, or overwrites wid if the // entry currently exists. The oldest entry is bumped out. // Caching an entry with widInvalid is valid. // // Arguments: [fileId] -- FileId to add // [wid] -- VolumeId to add // [volumeId] -- VolumeId to add // // History: 22-Jul-98 dlee Created // //-------------------------------------------------------------------------- void CFileIdMap::AddToCache( FILEID & fileId, WORKID wid, VOLUMEID volumeId ) { // First look for the fileid/volumeid currently in the cache for ( unsigned iCache = 0; iCache < _cCacheEntries; iCache++ ) { if ( _aCache[ iCache ].fileId == fileId && _aCache[ iCache ].volumeId == volumeId ) { _aCache[ iCache ].wid = wid; return; } } Win4Assert( _cCacheEntries <= cFileIdMapCacheEntries ); // Bump out the oldest item and put the new item first in the array unsigned cToMove; if ( _cCacheEntries == cFileIdMapCacheEntries ) { cToMove = cFileIdMapCacheEntries - 1; } else { cToMove = _cCacheEntries; _cCacheEntries++; } RtlMoveMemory( & _aCache[ 1 ], & _aCache[ 0 ], sizeof SFileIdMapEntry * cToMove ); _aCache[0].fileId = fileId; _aCache[0].volumeId = volumeId; _aCache[0].wid = wid; } //AddToCache //+------------------------------------------------------------------------- // // Member: CFileIdMap::FindInCache, private // // Synopsis: Locates a workid in the cache given a fileid and volumeid // // Arguments: [fileId] -- FileId of entry to find // [volumeId] -- VolumeId of entry to find // [wid] -- Returns the workid if found // // Returns: TRUE if found or FALSE if not found // // History: 22-Jul-98 dlee Created // //-------------------------------------------------------------------------- BOOL CFileIdMap::FindInCache( FILEID & fileId, VOLUMEID volumeId, WORKID & wid ) { for ( unsigned iCache = 0; iCache < _cCacheEntries; iCache++ ) { if ( _aCache[ iCache ].fileId == fileId && _aCache[ iCache ].volumeId == volumeId ) { wid = _aCache[ iCache ].wid; #if CIDBG == 1 ciDebugOut(( DEB_USN, "fim::find cache worked %#I64x %#x, vol %wc\n", fileId, wid, volumeId )); _cCacheHits++; if ( widInvalid == wid ) _cCacheNegativeHits++; #endif return TRUE; } } return FALSE; } //FindInCache //+------------------------------------------------------------------------- // // Member: CFileIdMap::RemoveFromCache, private // // Synopsis: Removes the item from the cache if it exists // // Arguments: [fileId] -- FileId of entry to remove (only used for assert) // [wid] -- Workid of entry to remove // // History: 22-Jul-98 dlee Created // //-------------------------------------------------------------------------- void CFileIdMap::RemoveFromCache( FILEID & fileId, WORKID wid ) { for ( unsigned iCache = 0; iCache < _cCacheEntries; iCache++ ) { if ( _aCache[ iCache ].wid == wid ) { Win4Assert( _aCache[ iCache ].fileId == fileId ); RtlCopyMemory( & _aCache[ iCache ], & _aCache[ iCache + 1 ], sizeof SFileIdMapEntry * ( _cCacheEntries - iCache - 1 ) ); _cCacheEntries--; break; } } } //RemoveFromCache //+------------------------------------------------------------------------- // // Member: CFileIdMap::LokFind, private // // Synopsis: Given fileId, find workid. // // Arguments: [fileId] - FileId to locate // [volumeId] - Volume id of fileid // // Returns: Workid of FileId // // History: 07-May-97 SitaramR Created // // Notes: File ids are not unique across different volumes, hence the // need for volume ids. // //-------------------------------------------------------------------------- WORKID CFileIdMap::LokFind( FILEID fileId, VOLUMEID volumeId ) { Win4Assert( _fFullInit ); WORKID wid; if ( FindInCache( fileId, volumeId, wid ) ) return wid; #if CIDBG == 1 _cCacheMisses++; #endif #ifdef DO_STATS unsigned cSearchLen = 0; #endif CShortWidList list( HashFun(fileId), _hTable ); for ( wid = list.WorkId(); wid != widInvalid; wid = list.NextWorkId() ) { #ifdef DO_STATS cSearchLen++; #endif XPrimaryRecord rec( _PropStoreMgr, wid ); PROPVARIANT propVar; BOOL fFound = _PropStoreMgr.ReadProperty( rec.GetReference(), pidFileIndex, propVar ); ciDebugOut(( DEB_ITRACE, "trying wid %#x, fFound %d, fid %#I64x\n", wid, fFound, propVar.uhVal.QuadPart )); if ( fFound && propVar.uhVal.QuadPart == fileId ) { Win4Assert( propVar.vt == VT_UI8 ); BOOL fFound = _PropStoreMgr.ReadProperty( rec.GetReference(), pidVolumeId, propVar ); if ( fFound && propVar.ulVal == volumeId ) { Win4Assert( propVar.vt == VT_UI4 ); #ifdef DO_STATS _hTable.UpdateStats( cSearchLen ); #endif ciDebugOut(( DEB_USN, "fim::find worked %#I64x %#x, vol %wc\n", fileId, wid, volumeId )); AddToCache( fileId, wid, volumeId ); return wid; } else { ciDebugOut(( DEB_USN, "fim::find fileid match, but not volid: %#x, %#x\n", propVar.ulVal, volumeId )); } } } ciDebugOut(( DEB_USN, "fim::find failed %#I64x, vol %wc\n", fileId, volumeId )); #if CIDBG == 1 _cCacheNegativeMisses++; #endif // // Often, a new file is looked up multiple times before it is added, so // add the widInvalid entry to the cache. Negative lookups are slow. // AddToCache( fileId, widInvalid, volumeId ); return widInvalid; } //LokFind //+------------------------------------------------------------------------- // // Member: CFileIdMap::LokAdd // // Synopsis: Add a new fileid to wid mapping // // Arguments: [fileId] -- File Id // [wid] -- Workid // // History: 07-May-97 SitaramR Created // //-------------------------------------------------------------------------- void CFileIdMap::LokAdd( FILEID fileId, WORKID wid, VOLUMEID volumeId ) { ciDebugOut(( DEB_USN, "Adding map fileid %#I64x -> wid %x, vol %wc\n", fileId, wid, volumeId )); Win4Assert( fileIdInvalid != fileId ); Win4Assert( _fFullInit ); // // Must grow hash table first, as it grovels through property store. // if ( _hTable.IsFull() ) GrowHashTable(); AddToCache( fileId, wid, volumeId ); _hTable.Add ( HashFun(fileId), wid ); } //LokAdd //+--------------------------------------------------------------------------- // // Member: CFileIdMap::LokDelete // // Synopsis: Delete file id from table // // Arguments: [fileId] -- FileId to remove // [wid] -- Workid to remove // // History: 07-May-97 SitaramR Created. // //---------------------------------------------------------------------------- void CFileIdMap::LokDelete( FILEID fileId, WORKID wid ) { ciDebugOut(( DEB_USN, "Removing map fileid %#I64x, %#x\n", fileId, wid )); RemoveFromCache( fileId, wid ); _hTable.Remove( HashFun( fileId ), wid, TRUE ); } //LokDelete //+--------------------------------------------------------------------------- // // Member: CFileIdMap::HashAll // // Synopsis: Re-hash all fileId's (after hash table growth) // // History: 07-May-97 May Created // //---------------------------------------------------------------------------- void CFileIdMap::HashAll() { // Count the number of hash table entries and grow the hash table { CPropertyStoreWids iter( _PropStoreMgr ); unsigned cWids = 0; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _fAbort ) { ciDebugOut(( DEB_WARN, "Stopping FileIdMap::HashAll because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); } PROPVARIANT propVar; BOOL fFound = _PropStoreMgr.ReadPrimaryProperty( wid, pidFileIndex, propVar ); // Only ntfs 5.0 wids have valid fileid's if ( fFound && ( VT_UI8 == propVar.vt ) && ( fileIdInvalid != propVar.uhVal.QuadPart ) ) cWids++; } GrowToSize( cWids ); } // Add the wids to the hash table CPropertyStoreWids iter( _PropStoreMgr ); for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _fAbort ) { ciDebugOut(( DEB_WARN, "Stopping FileIdMap::HashAll because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); } PROPVARIANT propVar; BOOL fFound = _PropStoreMgr.ReadPrimaryProperty( wid, pidFileIndex, propVar ); // // Only ntfs 5.0 wids have valid fileid's // if ( fFound && ( VT_UI8 == propVar.vt ) && ( fileIdInvalid != propVar.uhVal.QuadPart ) ) _hTable.Add ( HashFun( propVar.uhVal.QuadPart ), wid ); } #if 0 // // Look-up every file to see what the hashing looks like. // { CPropertyStoreWids iter( _PropStoreMgr ); unsigned cWids = 0; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { XPrimaryRecord rec( _PropStoreMgr, wid ); PROPVARIANT propVar; BOOL fFound = _PropStoreMgr.ReadProperty( rec.GetReference(), pidFileIndex, propVar ); // Only ntfs 5.0 wids have valid fileid's if ( fFound && ( VT_UI8 == propVar.vt ) && ( fileIdInvalid != propVar.uhVal.QuadPart ) ) { FILEID fid = propVar.uhVal.QuadPart; _PropStoreMgr.ReadProperty( rec.GetReference(), pidVolumeId, propVar ); LokFind( fid, propVar.ulVal ); } } ciDebugOut(( DEB_FORCE, "hash: size %d count %d maxchain %d searches %d length %d\n", _hTable.Size(), _hTable.Count(), _hTable._cMaxChainLen, _hTable._cTotalSearches, _hTable._cTotalLength )); } #endif } //HashAll //+------------------------------------------------------------------------- // // Member: CFileIdMap::HashFun // // Synopsis: Hash function for fileid's // // Arguments: [fileId] - FileId to hash // // History: 07-May-97 SitaramR Created // //-------------------------------------------------------------------------- unsigned CFileIdMap::HashFun( FILEID fileId ) { // // The lower 32 bits of the fileid are unique at any given time. The // upper 32 bits of the fileid represent a sequence number that is // incremented every time one of the lower 32 bits number is reused. // So, a good hash function is to simply use the lower 32 bits. // return (unsigned) fileId; } //HashFun