//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1991 - 2000. // // File: Strings.cxx // // Contents: Strings and hash table used by cicat // // History: 17-May-1993 BartoszM Created // 03-Jan-96 KyleP Integrate with property cache // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include #include #include #include "cicat.hxx" #include "usntree.hxx" #include "strings.hxx" //+--------------------------------------------------------------------------- // // Member: CStrings::CStrings, public // // Synopsis: Simple half of 2-phase construction // // Arguments: [PropStore] -- Property store. // [cicat] -- Catalog // // History: 27-Dec-95 KyleP Created. // //---------------------------------------------------------------------------- CStrings::CStrings( CPropStoreManager & PropStoreMgr, CiCat & cicat ) : CPersHash( PropStoreMgr, TRUE ), _cicat(cicat) { } //+--------------------------------------------------------------------------- // // Member: CStrings::FastInit // // Synopsis: Opens persistent hash. // // Arguments: [wcsCatDir] -- Catalog directory // [version] -- Content index version // // Returns: TRUE if table was successfully opened. // // History: 27-Dec-95 KyleP Created. // 13-Mar-98 KitmanH Passed in False to CPerHash::FastInit // to specify that a PersHash is wanted // //---------------------------------------------------------------------------- BOOL CStrings::FastInit( CiStorage * pStorage, ULONG version ) { CPersHash::FastInit( pStorage, version, FALSE ); // // Intialize virtual/physical map. // _vmap.Init( pStorage->QueryVirtualScopeList( 0 ) ); return TRUE; } //+--------------------------------------------------------------------------- // // Member: CStrings::Empty // // Synopsis: Method to empty out any of the initialized members. This is // called if corruption is detected and so all resources must // be released. // // History: 3-17-96 srikants Created // //---------------------------------------------------------------------------- void CStrings::Empty() { CPersHash::Empty(); _vmap.Empty(); } //+--------------------------------------------------------------------------- // // Member: CStrings::LongInit // // Synopsis: Initialization that may take a long time // // Arguments: [version] - The version of the compiled code. // [fDirtyShutdown] - Set to TRUE if the previous shutdown was // dirty. // // History: 3-06-96 srikants Created // //---------------------------------------------------------------------------- void CStrings::LongInit( ULONG version, BOOL fDirtyShutdown ) { CPersHash::LongInit( version, fDirtyShutdown ); // // On a dirty shutdown, we should revirtualize because the property store // may have lost some of the changes made prior to shutdown. // if ( !_vmap.IsClean() || fDirtyShutdown ) { ciDebugOut(( DEB_WARN, "Virtual mapping suspect. ReVirtualizing...\n" )); ReVirtualize(); _vmap.MarkClean(); } } //LongInit //+--------------------------------------------------------------------------- // // Member: CStrings::ReInit, public // // Synopsis: Clears out hash table // // Arguments: [version] -- Content index version // // Returns: TRUE if table was successfully opened. // // History: 27-Dec-95 KyleP Created. // //---------------------------------------------------------------------------- BOOL CStrings::ReInit ( ULONG version ) { CPersHash::ReInit( version ); return TRUE; } //ReInit //+------------------------------------------------------------------------- // // Member: CStrings::LokAdd, public // // Synopsis: Add a new path without updating the hash table // or the persistent count // // Arguments: [pwcPath] -- File path // [fileId] -- file id for the file if available. // [fUsnVolume] -- TRUE if the file is from a USN volume // [widParent] -- widInvalid or the parent wid // [ulAttrib] -- file attributes // [pftLastSeenTime] -- file last seen time // // History: 19-May-93 BartoszM Created // //-------------------------------------------------------------------------- WORKID CStrings::LokAdd( WCHAR const * pwcPath, FILEID fileId, BOOL fUsnVolume, WORKID widParent, ULONG ulAttrib, FILETIME const * pftLastSeenTime ) { Win4Assert( _fFullInit ); Win4Assert( 0 != pwcPath && wcslen(pwcPath) > 0 ); Win4Assert( L':' == pwcPath[1] || (L'\\' == pwcPath[0] && L'\\' == pwcPath[1]) ); // // Must grow hash table first, as it grovels through property store. // if ( _hTable.IsFull() ) GrowHashTable(); // // Store path in new record. // PROPVARIANT var; var.vt = VT_LPWSTR; var.pwszVal = (WCHAR *)pwcPath; WORKID wid = _PropStoreMgr.WritePropertyInNewRecord( pidPath, *(CStorageVariant const *)(ULONG_PTR)&var ); VOLUMEID volumeId = fUsnVolume ? _cicat.MapPathToVolumeId( pwcPath ) : CI_VOLID_USN_NOT_ENABLED; ciDebugOut(( DEB_ITRACE, "adding volume %#x %s file wid %#x, '%ws'\n", volumeId, fUsnVolume ? "usn" : "fat", wid, pwcPath )); if ( !fUsnVolume ) _hTable.Add ( HashFun(pwcPath), wid ); ULONG ulVPathId = _vmap.PhysicalPathToId( pwcPath ); ULONG ulParentWid; if ( widInvalid == widParent) { // // If the parent is deleted by now, store the parent as widUnused // instead of widInvalid, in an attempt to avoid confusion. // ulParentWid = LokParentWorkId( pwcPath, fUsnVolume ); if ( widInvalid == ulParentWid ) ulParentWid = widUnused; } else ulParentWid = widParent; // // Open a composite property record for doing all the writes below // XWriteCompositeRecord rec( _PropStoreMgr, wid ); // // Initialize the SDID to avoid showing the file before it is // filtered. // var.vt = VT_UI4; var.ulVal = sdidInvalid; SCODE sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidSecurity, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); // // Write the fileindex, and other default ntfs properties for non-ntfs 5.0 wids // var.vt = VT_UI8; var.uhVal.QuadPart = fileId; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidFileIndex, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); var.vt = VT_UI4; var.ulVal = volumeId; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidVolumeId, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); // // Write parent wid to prop store // var.ulVal = ulParentWid; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidParentWorkId, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); // // Determine the virtual path. // var.ulVal = ulVPathId; var.vt = VT_UI4; sc = _PropStoreMgr.WriteProperty( rec.GetReference(), pidVirtualPath, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); //ciDebugOut(( DEB_ITRACE, "%ws --> VPath %d\n", pwcPath, var.ulVal )); // // Init the last seen time // if ( 0 == pftLastSeenTime ) RtlZeroMemory( &var.filetime, sizeof var.filetime ); else var.filetime = *pftLastSeenTime; var.vt = VT_FILETIME; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidLastSeenTime, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); // // Init the attributes // var.vt = VT_UI4; var.ulVal = ulAttrib; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); rec.Free(); // Update both seen arrays so the new file isn't deleted at the end // of the scan. if ( _afSeenScans.Size() > 0 ) SET_SEEN( &_afSeenScans, wid, SEEN_NEW ); if ( _afSeenUsns.Size() > 0 ) SET_SEEN( &_afSeenUsns, wid, SEEN_NEW ); ciDebugOut(( DEB_ITRACE, "lokadd 0x%x, usn volume: %d\n", wid, fUsnVolume )); return wid; } //LokAdd //+--------------------------------------------------------------------------- // // Member: CStrings::LokDelete, public // // Synopsis: Delete string from table // // Arguments: [pwcPath] -- Path of the document, in lower case // [wid] -- Workid to remove // [fDisableDeletionCheck] -- Should we assert that the deleted // entry must be found ? // [fUsnVolume] -- TRUE if the file is on a USN volume // // History: 27-Dec-95 KyleP Created. // //---------------------------------------------------------------------------- void CStrings::LokDelete( WCHAR const * pwcPath, WORKID wid, BOOL fDisableDeletionCheck, BOOL fUsnVolume ) { XGrowable awc; WCHAR const * pwcName = awc.Get(); BOOL fFound = TRUE; if ( !fUsnVolume ) { if ( 0 != pwcPath ) { AssertLowerCase( pwcPath, 0 ); pwcName = pwcPath; } else { fFound = (Find( wid, awc) > 0); pwcName = awc.Get(); } } if ( fFound ) { _PropStoreMgr.DeleteRecord( wid ); ciDebugOut(( DEB_ITRACE, "LokDelete 0x%x, usn %d\n", wid, fUsnVolume )); if ( !fUsnVolume ) { Win4Assert( 0 != pwcName ); _hTable.Remove( HashFun( pwcName ), wid, fDisableDeletionCheck ); } } } //LokDelete //+--------------------------------------------------------------------------- // // Member: CStrings::LokRenameFile // // Synopsis: Rename old file to new file // // Arguments: [pwcsOldFileName] -- Old file name as a funny path or remote path // [pwcsNewFileName] -- New file name as a funny path or remote path // [ulFileAttib] -- File attributes of new file // [volumeId] -- Volume id // // History: 20-Mar-96 SitaramR Created // //---------------------------------------------------------------------------- void CStrings::LokRenameFile( const WCHAR * pwcsOldFileName, const WCHAR * pwcsNewFileName, WORKID wid, ULONG ulFileAttrib, VOLUMEID volumeId, WORKID widParent ) { Win4Assert( L':' == pwcsNewFileName[1] || (L'\\' == pwcsNewFileName[0] && L'\\' == pwcsNewFileName[1]) ); ciDebugOut(( DEB_FSNOTIFY, "CStrings: Renaming file (%ws) to (%ws)\n", pwcsOldFileName, pwcsNewFileName )); BOOL fUsnVolume = ( CI_VOLID_USN_NOT_ENABLED != volumeId ); PROPVARIANT propVar; if ( widInvalid == wid ) { ciDebugOut(( DEB_ITRACE, "adding '%ws' via the rename path\n", pwcsNewFileName )); wid = LokAdd( pwcsNewFileName, fileIdInvalid, fUsnVolume, widParent ); propVar.vt = VT_UI4; propVar.ulVal = ulFileAttrib; SCODE sc = _PropStoreMgr.WritePrimaryProperty( wid, pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); return; } // Files from USN volumes aren't in this hash table if ( !fUsnVolume ) { _hTable.Remove( HashFun(pwcsOldFileName), wid, FALSE ); _hTable.Add( HashFun(pwcsNewFileName), wid ); } if ( widInvalid == widParent ) widParent = LokParentWorkId( pwcsNewFileName, fUsnVolume ); XWriteCompositeRecord rec( _PropStoreMgr, wid ); propVar.vt = VT_LPWSTR; propVar.pwszVal = (WCHAR*)pwcsNewFileName; SCODE sc = _PropStoreMgr.WriteProperty( rec.GetReference(), pidPath, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); propVar.vt = VT_UI4; propVar.ulVal = _vmap.PhysicalPathToId( pwcsNewFileName ); sc = _PropStoreMgr.WriteProperty( rec.GetReference(), pidVirtualPath, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); propVar.ulVal = ulFileAttrib; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); propVar.ulVal = widParent; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidParentWorkId, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); } //LokRenameFile //+--------------------------------------------------------------------------- // // Member: CStrings::HashAll, public // // Synopsis: Re-hash all strings (after hash table growth) // // History: 27-Dec-95 KyleP Created. // //---------------------------------------------------------------------------- void CStrings::HashAll() { XGrowable awc; // 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 HashAll because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); } // // Files from USN volumes aren't in the strings table // PROPVARIANT var; if ( _PropStoreMgr.ReadPrimaryProperty( wid, pidFileIndex, var ) && ( VT_UI8 == var.vt ) && ( fileIdInvalid != var.uhVal.QuadPart ) ) continue; // // It is possible to have a top-level wid in the propstore without // a pidPath. This can happen if the system crashes in the middle of // WritePropertyInNewRecord, when a new top-level wid has been created // but pidPath hasn't yet been written. // if ( Find( wid, awc ) > 0 ) cWids++; else _PropStoreMgr.DeleteRecord( wid ); } 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 HashAll because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); } // // Files from USN volumes aren't in the strings table // PROPVARIANT var; if ( _PropStoreMgr.ReadPrimaryProperty( wid, pidFileIndex, var ) && ( VT_UI8 == var.vt ) && ( fileIdInvalid != var.uhVal.QuadPart ) ) continue; // // It is possible to have a top-level wid in the propstore without // a pidPath. This can happen if the system crashes in the middle of // WritePropertyInNewRecord, when a new top-level wid has been created // but pidPath hasn't yet been written. // if ( Find( wid, awc ) > 0 ) _hTable.Add ( HashFun( awc.Get() ), wid ); else _PropStoreMgr.DeleteRecord( wid ); } } //HashAll //+--------------------------------------------------------------------------- // // Member: CStrings::AddVirtualScope, public // // Synopsis: Add new virtual/physical path mapping // // Arguments: [vroot] -- Virtual path // [root] -- Physical path // [fAutomatic] -- TRUE for root tied to Gibraltar // [eType] -- root type // [fVRoot] -- TRUE if a vroot, not a vdir // [fIsIndexed] -- TRUE if should be indexed, FALSE otherwise // // Returns: TRUE if a change was made. // // History: 05-Feb-96 KyleP Created. // //---------------------------------------------------------------------------- BOOL CStrings::AddVirtualScope( WCHAR const * vroot, WCHAR const * root, BOOL fAutomatic, CiVRootTypeEnum eType, BOOL fVRoot, BOOL fIsIndexed ) { ULONG idNew; if ( _vmap.Add( vroot, root, fAutomatic, idNew, eType, fVRoot, fIsIndexed ) ) { // // Log event // if ( fVRoot && fIsIndexed ) { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_VROOT_ADDED, 2 ); item.AddArg( vroot ); item.AddArg( root ); eventLog.ReportEvent( item ); } // // Add new virtual root to all appropriate objects. // #if CIDBG == 1 if ( ciInfoLevel & DEB_ITRACE ) { // only directories should have virtual root identifiers // of 0xffffffff CPropertyStoreWids iter( _PropStoreMgr ); PROPVARIANT var; unsigned cb = 0; unsigned cb2 = 0; XGrowable wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && VT_EMPTY != var.vt && 0xffffffff == var.ulVal && _PropStoreMgr.ReadProperty( wid, pidAttrib, var, 0, &cb2 ) && VT_EMPTY != var.vt && ( 0 == ( var.ulVal & FILE_ATTRIBUTE_DIRECTORY ) ) ) { Find( wid, wcPhysPath ); ciDebugOut(( DEB_ITRACE, "vid 0xffffffff wid 0x%x, path '%ws'\n", wid, wcPhysPath.Get() )); } } } #endif // CIDBG == 1 ULONG idParent = _vmap.Parent( idNew ); ciDebugOut(( DEB_ITRACE, "idParent 0x%x, idnew 0x%x\n", idParent, idNew )); CStorageVariant varNew; varNew.SetUI4( idNew ); Win4Assert( 0xffffffff != idNew ); CPropertyStoreWids iter( _PropStoreMgr ); XGrowable wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { PROPVARIANT var; unsigned cb = 0; // // Check if the vdir should be changed to the new one // if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && ( VT_EMPTY == var.vt || var.ulVal == idParent || ( _vmap.IsNonIndexedVDir( var.ulVal ) && idNew != _vmap.GetExcludeParent( var.ulVal ) ) ) ) { unsigned cc = Find( wid, wcPhysPath ); Win4Assert( cc > 0 ); if ( cc > 0 ) { if ( _vmap.IsInPhysicalScope( idNew, wcPhysPath.Get(), cc ) ) { ciDebugOut(( DEB_ITRACE, "add, %.*ws 0x%x --> VPath 0x%x\n", cc, wcPhysPath, var.ulVal, idNew )); SCODE sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew ); if (FAILED(sc)) THROW(CException(sc)); } } } else { // ciDebugOut(( DEB_ITRACE, "ignoring id %d, isnivd %d\n", // var.ulVal, _vmap.IsNonIndexedVDir( var.ulVal ) )); } } _vmap.MarkClean(); return TRUE; } else return FALSE; } //AddVirtualScope //+--------------------------------------------------------------------------- // // Member: CStrings::RemoveVirtualScope, public // // Synopsis: Remove virtual/physical path mapping // // Arguments: [vroot] -- Virtual path // [fOnlyIfAutomatic] -- If TRUE, then a manual root will not // be removed // [eType] -- type of root // [fVRoot] -- TRUE if a vroot, FALSE if a vdir // [fForceVPathFixing] -- forces fixups of removed vpaths // // History: 05-Feb-96 KyleP Created. // //---------------------------------------------------------------------------- BOOL CStrings::RemoveVirtualScope( WCHAR const * vroot, BOOL fOnlyIfAutomatic, CiVRootTypeEnum eType, BOOL fVRoot, BOOL fForceVPathFixing ) { ULONG idOld, idNew; if ( _vmap.Remove( vroot, fOnlyIfAutomatic, idOld, idNew, eType, fVRoot ) || fForceVPathFixing ) { // // Log event // if ( fVRoot ) { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_VROOT_REMOVED, 1 ); item.AddArg( vroot ); eventLog.ReportEvent( item ); } // // Swap virtual root to all appropriate objects. // #if CIDBG == 1 if ( ciInfoLevel & DEB_ITRACE ) { // only directories should have virtual root identifiers // of 0xffffffff CPropertyStoreWids iter( _PropStoreMgr ); PROPVARIANT var; unsigned cb = 0; unsigned cb2 = 0; XGrowable wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && VT_EMPTY != var.vt && 0xffffffff == var.ulVal && _PropStoreMgr.ReadProperty( wid, pidAttrib, var, 0, &cb2 ) && VT_EMPTY != var.vt && ( 0 == ( var.ulVal & FILE_ATTRIBUTE_DIRECTORY ) ) ) { Find( wid, wcPhysPath ); ciDebugOut(( DEB_ITRACE, "vid 0xffffffff wid 0x%x, path '%ws'\n", wid, wcPhysPath.Get() )); } } } #endif // CIDBG == 1 CStorageVariant varNew; varNew.SetUI4( idNew ); CPropertyStoreWids iter( _PropStoreMgr ); BOOL fIsNewExcludeParent = _vmap.IsAnExcludeParent( idNew ); SCODE sc; XGrowable wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { PROPVARIANT var; unsigned cb = 0; // // No existing virtual root, or possibility of replacement // if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && var.vt == VT_UI4 && var.ulVal == idOld ) { // If the new vpath has child non-indexed vdirs, the vdir // needs to be recomputed from scratch, otherwise, just use // the new id. if ( fIsNewExcludeParent ) { BOOL fFound = (Find( wid, wcPhysPath ) > 0); Win4Assert( fFound ); if ( fFound ) { ULONG idBest = _vmap.PhysicalPathToId( wcPhysPath.Get() ); ciDebugOut(( DEB_ITRACE, "removeA: %ws 0x%x --> 0x%x\n", wcPhysPath, idOld, idBest )); CStorageVariant varNew; varNew.SetUI4( idBest ); sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew ); if (FAILED(sc)) THROW(CException(sc)); } } else { #if CIDBG == 1 if ( ciInfoLevel & DEB_ITRACE ) { XGrowable wcPhysPath; unsigned cc = Find( wid, wcPhysPath ); ciDebugOut(( DEB_ITRACE, "removeB %.*ws 0x%x --> VPath 0x%x\n", cc, wcPhysPath.Get(), idOld, idNew )); } #endif // CIDBG == 1 sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew ); if (FAILED(sc)) THROW(CException(sc)); } } } _vmap.MarkClean(); return TRUE; } else return FALSE; } //RemoveVirtualScope //+------------------------------------------------------------------------- // // Member: CStrings::FindVirtual, private // // Synopsis: Given workid, find virtual path. Version which uses pre-opened // property record. // // Arguments: [PropRec] -- Pre-opened property record. // [cSkip] -- Count of paths to skip. // [xBuf] -- String returned here // // Returns: 0 if string not found ELSE // Count of chars in xBuf // // Note: xBuf is returned as a NULL terminated string // // History: 29-Apr-96 AlanW Created. // //-------------------------------------------------------------------------- unsigned CStrings::FindVirtual( CCompositePropRecord & PropRec, unsigned cSkip, XGrowable & xBuf ) { PROPVARIANT var; unsigned cb = 0; unsigned cc = 0; if ( _PropStoreMgr.ReadProperty( PropRec, pidVirtualPath, var, 0, &cb ) && VT_UI4 == var.vt ) { // // Skip the specified number of virtual paths. // ULONG id = _vmap.FindNthRemoved( var.ulVal, cSkip ); // // Were there that many possible paths? // if ( 0xFFFFFFFF != id ) { // // There are two cases for building the path. // // \short\vpath // c:\physical\path // \vpath\longer\than\ppath // // If the virtual path is *longer* than the physical path, then we can just // copy the full physical path starting at such an offset that the virtual // path can be blasted on top. // // If the virtual path is *shorter* than the physical path, then we need // to fetch the physical path and shift it left. // CVMapDesc const & VDesc = _vmap.GetDesc( id ); // This is either a non-indexed virtual directory // or the vdesc has just been marked as not in use // though it was in use at the time of FindNthRemoved above. // Either way, there is no vpath mapping. // if ( !VDesc.IsInUse() ) { return 0; } // NTRAID#DB-NTBUG9-83796-2000/07/31-dlee handling changed vroots can AV if more changes come later. // VDesc can go stale or be reused at any time, since // it's being used with no lock held. We may AV in // some cases here. unsigned ccVPath = VDesc.VirtualLength(); unsigned ccPPath = VDesc.PhysicalLength(); if ( ccVPath >= ccPPath ) { unsigned delta = ccVPath - ccPPath; XGrowable xTemp; unsigned ccIn = Find( PropRec, xTemp ); if ( ccIn > 0 ) { xBuf.SetSize( delta + ccIn + 1 ); RtlCopyMemory( xBuf.Get() + delta, xTemp.Get(), ccIn * sizeof( WCHAR ) ); Win4Assert( ccIn >= ccPPath ); Win4Assert( RtlEqualMemory( xBuf.Get() + delta, VDesc.PhysicalPath(), ccPPath * sizeof(WCHAR) ) ); RtlCopyMemory( xBuf.Get(), VDesc.VirtualPath(), ccVPath * sizeof(WCHAR) ); // Null-terminate // Win4Assert( xBuf.Count() > ccIn + delta ); xBuf[cc = ccIn + delta] = 0; } } else { unsigned delta = ccPPath - ccVPath; unsigned ccIn = Find( PropRec, xBuf ); if ( ccIn >= ccPPath ) { RtlMoveMemory( xBuf.Get() + ccVPath, xBuf.Get() + ccPPath, (ccIn - ccPPath) * sizeof(WCHAR) ); RtlCopyMemory( xBuf.Get(), VDesc.VirtualPath(), ccVPath * sizeof(WCHAR) ); // // Null-terminate // Win4Assert( xBuf.Count() > ccIn - delta ); xBuf[cc = ccIn - delta] = 0; } } } } return cc; } //FindVirtual //+--------------------------------------------------------------------------- // // Member: CStrings::ReVirtualize, private // // Synopsis: Verify or correct all virtual mappings. // // History: 14-Feb-96 KyleP Created. // //---------------------------------------------------------------------------- void CStrings::ReVirtualize() { // // Loop through property cache and verify virtual mapping. // CPropertyStoreWids iter( _PropStoreMgr ); XGrowable wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { // // Get path. // unsigned cc = Find( wid, wcPhysPath ); // // Get existing virtual root. // PROPVARIANT var; unsigned cb = 0; if ( cc > 0 && _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) ) { // // Do we need to change? // wcPhysPath[cc] = 0; ULONG idNew = _vmap.PhysicalPathToId( wcPhysPath.Get() ); if ( var.vt == VT_EMPTY || var.ulVal != idNew ) { ciDebugOut(( DEB_ITRACE, "ReVirtualize: %ws --> %u\n", wcPhysPath.Get(), idNew )); CStorageVariant varNew; varNew.SetUI4( idNew ); SCODE sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew ); if (FAILED(sc)) THROW(CException(sc)); } } } } //ReVirtualize //+------------------------------------------------------------------------- // // Member: CStrings::HashFun, private // // Synopsis: The hash function used to find strings in _strings[] // // Arguments: [str] - the string to perform the hash function on // // History: 10-Mar-92 BartoszM Created // //-------------------------------------------------------------------------- unsigned CStrings::HashFun( WCHAR const * pc ) { unsigned ulG; for ( unsigned ulH=0; *pc; pc++) { ulH = (ulH << 4) + (*pc); if (ulG = (ulH & 0xf0000000)) ulH ^= ulG >> 24; ulH &= ~ulG; } return ulH; } //+------------------------------------------------------------------------- // // Member: CStrings::LokFind // // Synopsis: Given string, find workid. This works only for FAT volumes. // // Arguments: [buf] - String to locate // // Returns: Workid of [buf] // // History: 27-Dec-95 KyleP Created. // //-------------------------------------------------------------------------- WORKID CStrings::LokFind( WCHAR const * buf ) { Win4Assert( _fFullInit ); Win4Assert( 0 != buf ); Win4Assert( FALSE == _cicat.IsOnUsnVolume( buf ) ); #ifdef DO_STATS unsigned cSearchLen = 0; #endif // DO_STATUS CShortWidList list( HashFun(buf), _hTable ); unsigned ccIn = wcslen(buf); //Win4Assert( ccIn < MAX_PATH ); for ( WORKID wid = list.WorkId(); wid != widInvalid; wid = list.NextWorkId() ) { PROPVARIANT var; XGrowable wcTemp; //unsigned cc = sizeof(wcTemp) / sizeof(WCHAR); // don't even try to find paths that are longer than ccIn // account for quad-word align and null-termination by adding 4 unsigned cc = Find( wid, wcTemp ); #ifdef DO_STATS cSearchLen++; #endif // DO_STATS // Win4Assert( cc <= sizeof(wcTemp) / sizeof(WCHAR) ); if ( ccIn == cc && RtlEqualMemory( buf, wcTemp.Get(), cc * sizeof(WCHAR) ) ) { #ifdef DO_STATS _hTable.UpdateStats( cSearchLen ); #endif // DO_STATS return wid; } } return widInvalid; } //LokFind //+------------------------------------------------------------------------- // // Member: CStrings::LokFind // // Synopsis: Given string, find workid. This works for both FAT and NTFS. // // Arguments: [lcaseFunnyPath] - Path to locate // [fUsnVolume] - Flag to tell whether it's a USN volume or not // // Returns: Workid of [lcaseFunnyPath] // // History: 18-Aug-98 VikasMan Created. // //-------------------------------------------------------------------------- // inline WORKID CStrings::LokFind( const CLowerFunnyPath & lcaseFunnyPath, BOOL fUsnVolume ) { return ( fUsnVolume ? _cicat.PathToWorkId ( lcaseFunnyPath, FALSE ) : LokFind( lcaseFunnyPath.GetActualPath() ) ); } //+------------------------------------------------------------------------- // // Member: CStrings::Find // // Synopsis: Given workid, find the path string. // // Arguments: [wid] -- Workid to locate // [xBuf] -- String returned here // // Returns: 0 if string not found ELSE // Count of chars in xBuf // // Note: xBuf is returned as a NULL terminated string // // History: 27-Dec-95 KyleP Created. // //-------------------------------------------------------------------------- unsigned CStrings::Find( WORKID wid, XGrowable & xBuf ) { CCompositePropRecord PropRec( wid, _PropStoreMgr ); return Find( PropRec, xBuf ); } //Find //+------------------------------------------------------------------------- // // Member: CStrings::Find // // Synopsis: Given PropRec, find the path string. // // Arguments: [PropRec] -- Property Record // [xBuf] -- String returned here // // Returns: 0 if string not found ELSE // Count of chars in xBuf // // Note: xBuf is returned as a NULL terminated string // // History: 27-Dec-95 KyleP Created. // //-------------------------------------------------------------------------- unsigned CStrings::Find( CCompositePropRecord & PropRec, XGrowable & xBuf ) { unsigned cc = xBuf.Count(); _Find( PropRec, xBuf.Get(), cc ); if ( cc > xBuf.Count() ) { // Need more space xBuf.SetSize( cc ); _Find( PropRec, xBuf.Get(), cc ); // Can't go on asking for more space forever ! Win4Assert( cc < xBuf.Count() ); } // Either we didn't find or if we did, then it is null terminated Win4Assert( 0 == cc || 0 == xBuf[cc] ); return cc; } //+------------------------------------------------------------------------- // // Member: CStrings::Find // // Synopsis: Given workid, find the path string. // // Arguments: [wid] -- Workid to locate // [funnyPath] -- String returned here, as funnyPath // // Returns: 0 if string not found ELSE // Count of actual chars in funnyPath // // History: 21-May-98 VikasMan Created. // //-------------------------------------------------------------------------- unsigned CStrings::Find( WORKID wid, CFunnyPath & funnyPath ) { CCompositePropRecord PropRec( wid, _PropStoreMgr ); return Find( PropRec, funnyPath ); } //Find //+------------------------------------------------------------------------- // // Member: CStrings::Find // // Synopsis: Given workid, find the path string. // // Arguments: [wid] -- Workid to locate // [lcaseFunnyPath] -- String returned here, as lcase funnyPath // // Returns: 0 if string not found ELSE // Count of actual chars in funnyPath // // History: 21-May-98 VikasMan Created. // //-------------------------------------------------------------------------- unsigned CStrings::Find( WORKID wid, CLowerFunnyPath & lcaseFunnyPath ) { CCompositePropRecord PropRec( wid, _PropStoreMgr ); return Find( PropRec, lcaseFunnyPath ); } //Find //+------------------------------------------------------------------------- // // Member: CStrings::Find // // Synopsis: Given PropRec, find the path string. // // Arguments: [PropRec] -- Property Record // [funnyPath] -- String returned here, as funnyPath // // Returns: 0 if string not found ELSE // Count of actual chars in funnyPath // // History: 21-May-98 VikasMan Created. // //-------------------------------------------------------------------------- unsigned CStrings::Find( CCompositePropRecord & PropRec, CFunnyPath & funnyPath ) { XGrowable xBuf; unsigned cc = Find( PropRec, xBuf ); if ( cc > 0 ) { funnyPath.SetPath( xBuf.Get(), cc ); } return cc; } //+------------------------------------------------------------------------- // // Member: CStrings::Find // // Synopsis: Given PropRec, find the path string. // // Arguments: [PropRec] -- Property Record // [lcaseFunnyPath] -- String returned here, as lcase funnyPath // // Returns: 0 if string not found ELSE // Count of actual chars in funnyPath // // History: 21-May-98 VikasMan Created. // //-------------------------------------------------------------------------- unsigned CStrings::Find( CCompositePropRecord & PropRec, CLowerFunnyPath & lcaseFunnyPath ) { XGrowable xBuf; unsigned cc = Find( PropRec, xBuf ); if ( cc > 0 ) { lcaseFunnyPath.SetPath( xBuf.Get(), cc, TRUE ); } return cc; } //+------------------------------------------------------------------------- // // Member: CStrings::_Find, private // // Synopsis: Given workid, find string. Version which uses pre-opened // property record. // // Arguments: [PropRec] -- Pre-opened property record. // [buf] -- String returned here // [cc] -- On input: size in WCHARs of [buf]. On output, // size required or 0 if string not found. // // History: 03-Apr-96 KyleP Created. // //-------------------------------------------------------------------------- void CStrings::_Find( CCompositePropRecord & PropRec, WCHAR * buf, unsigned & cc ) { #if 0 // // First try to get the path based on file id and volume id. // This only works for files on USN volumes. Paths from files on USN // volumes aren't in the property cache. // Sure, this makes FAT lookups slower, but that's the way it goes. // VOLUMEID volumeId; FILEID fileId; if ( _cicat.PropertyRecordToFileId( PropRec, fileId, volumeId ) ) { // PropertyRecordToFileId doesn't return a fileid without a volumeid Win4Assert( CI_VOLID_USN_NOT_ENABLED != volumeId ); _cicat.FileIdToPath( fileId, volumeId, buf, cc ); return; } #endif PROPVARIANT var; const unsigned cbIn = cc * sizeof(WCHAR); unsigned cb = cbIn; if ( !_PropStoreMgr.ReadProperty( PropRec, pidPath, var, (BYTE *)buf, &cb ) ) cc = 0; else { if ( cb <= cbIn ) { Win4Assert( (buf == var.pwszVal && VT_LPWSTR == var.vt) || VT_EMPTY == var.vt); // // Length returned from ReadProperty may be the QWORD-ALIGNED length. // Must adjust to the real length, which will be sans terminating // null, and maybe a few bytes more. // if ( VT_LPWSTR == var.vt ) { //Win4Assert( 0 == (cb & 7) ); //Win4Assert( cb >= sizeof(LONGLONG) ); if ( cb < sizeof(LONGLONG) ) { cc = cb / sizeof(WCHAR) - 1; // -1 for null } else { cc = cb / sizeof(WCHAR); cc -= sizeof(LONGLONG)/sizeof(WCHAR); while ( 0 != buf[cc] ) cc++; } Win4Assert( 0 == buf[cc] ); Win4Assert( 0 != buf[cc-1] ); } else { buf[0] = 0; cc = 0; } } else { // // The buffer is not big enough. // cc = cb/sizeof(WCHAR); } } } //Find //+--------------------------------------------------------------------------- // // Member: CStrings::Find // // Synopsis: Get the last seen time for the given wid. // // Arguments: [wid] - // [ftLastSeen] - // // History: 3-19-96 srikants Created // //---------------------------------------------------------------------------- BOOL CStrings::Find( WORKID wid, FILETIME & ftLastSeen ) { PROPVARIANT var; unsigned cb; BOOL fFound = _PropStoreMgr.ReadPrimaryProperty( wid, pidLastSeenTime, var ); if ( fFound && ( VT_FILETIME == var.vt ) ) ftLastSeen = var.filetime; else RtlZeroMemory( &ftLastSeen, sizeof(ftLastSeen) ); return fFound; } //Find //+------------------------------------------------------------------------- // // Member: CStrings::BeginSeen, public // // Synopsis: Begin 'seen' processing. // // Arguments: [pwcRoot] -- Root path of the seen processing // [mutex] -- Cicat mutex // [eType] -- Seen array type // // History: 27-Dec-95 KyleP Created // //-------------------------------------------------------------------------- void CStrings::BeginSeen( WCHAR const * pwcsRoot, CMutexSem & mutex, ESeenArrayType eType ) { ciDebugOut(( DEB_ITRACE, "BeginSeen 0x%x\n", eType )); // // Return time of last *seen* and update to current time. // CDynArrayInPlace *pafSeen; if ( eType == eScansArray ) pafSeen = &_afSeenScans; else pafSeen = &_afSeenUsns; // // Set array. // CPropertyStoreWids iter( _PropStoreMgr ); WORKID widIgnore = 0; // // For doing scope testing. // ULONG cwcScope = 0 != pwcsRoot ? wcslen(pwcsRoot) : 0; CScopeMatch scopeTest( pwcsRoot, cwcScope ); XGrowable wcsPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { CLock lock(mutex); // Protect "seen" array. if ( _fAbort ) { ciDebugOut(( DEB_WARN, "Stopping BeginSeen because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); } // // Ignore any missing entries. // for ( ; widIgnore < wid; widIgnore++ ) SET_SEEN( pafSeen, widIgnore, SEEN_IGNORE ); widIgnore++; unsigned cc = Find( wid, wcsPath ); if ( 0 == cc || scopeTest.IsInScope( wcsPath.Get(), cc ) ) SET_SEEN( pafSeen, wid, SEEN_NOT ); else SET_SEEN( pafSeen, wid, SEEN_YES ); } CLock lock( mutex ); // Protect "seen" array for ( ; widIgnore < _PropStoreMgr.MaxWorkId(); widIgnore++ ) SET_SEEN( pafSeen, widIgnore, SEEN_IGNORE ); // At the point EndSeen is called, the size of the seen array must // be non-zero. if it is not non-zero, set it to be so. if (0 == pafSeen->Size()) pafSeen->SetSize( 10 ); Win4Assert( pafSeen->Size() > 0 ); } //BeginSeen //+--------------------------------------------------------------------------- // // Member: CStrings::LokParentWorkId // // Synopsis: Returns parent workid of given file // // Arguments: [pwcsFileName] -- File name // // History: 23-Jun-97 SitaramR Created // 01-Sep-97 EmilyB Physical drive roots (c:\) and // UNC roots (\\emilyb\d) have // no parent. // //---------------------------------------------------------------------------- WORKID CStrings::LokParentWorkId( WCHAR const * pwcsFileName, BOOL fUsnVolume ) { // // return widInvalid if we're at root of physical drive // unsigned cwcEnd = wcslen( pwcsFileName ) - 1 ; if (cwcEnd < 3) return widInvalid; // physical drive root has no parent // // backup from end of filename to last \ to get parent // while ( pwcsFileName[cwcEnd] != L'\\' && cwcEnd > 1) cwcEnd--; CLowerFunnyPath lcaseFunnyParent; lcaseFunnyParent.SetPath( pwcsFileName, cwcEnd ); // // Find parent's wid // WORKID widParent = LokFind( lcaseFunnyParent, fUsnVolume ); if (widInvalid == widParent) // if parent didn't have wid, then create one { // // check if we're at root of UNC path - return widInvalid if so // const WCHAR * pwszParent = lcaseFunnyParent.GetActualPath(); if (cwcEnd > 2 && pwszParent[0] == L'\\' && pwszParent[1] == L'\\') { // see if last \ is the 2nd \\ in UNC name while ( pwszParent[cwcEnd] != L'\\') cwcEnd--; if ( 1 == cwcEnd ) return widInvalid; } // // Now we traverse the path from the left and create all the wids // that are needed. We avoid recursion by starting from left. // unsigned cwcParentLength = lcaseFunnyParent.GetActualLength(); BOOL fAllPathsInvalidFromNow = FALSE; cwcEnd = 2; // In case it is a remote path, start traversal after the machine name if ( L'\\' == pwcsFileName[0] && L'\\' == pwcsFileName[1] ) { while ( pwcsFileName[cwcEnd] != L'\\' ) cwcEnd++; cwcEnd++; } for (; cwcEnd < cwcParentLength; cwcEnd++) { if ( L'\\' == pwcsFileName[cwcEnd] ) { lcaseFunnyParent.SetPath( pwcsFileName, cwcEnd ); if ( fAllPathsInvalidFromNow || widInvalid == LokFind( lcaseFunnyParent, fUsnVolume ) ) { // Since we done't have a wid for this path, it should be // be a valid assumption that all its children also do // not have valid wids fAllPathsInvalidFromNow = TRUE; _cicat.PathToWorkId ( lcaseFunnyParent, TRUE ); } } } // Now create the final parent wid lcaseFunnyParent.SetPath( pwcsFileName, cwcEnd ); widParent = _cicat.PathToWorkId ( lcaseFunnyParent, TRUE ); } // // wid can be widInvalid if the scope is no longer indexed or has // been deleted. // // Win4Assert( widInvalid != widParent ); // return widParent; } //LokParentWorkId