//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000. // // File: CICAT.CXX // // Contents: Content index temporary catalog // // Classes: // // History: 09-Mar-1992 BartoszM Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "propiter.hxx" #include "propobj.hxx" #include "cicat.hxx" #include "cinulcat.hxx" #include "catreg.hxx" #include #include #include static const GUID guidCharacterization = PSGUID_CHARACTERIZATION; static const unsigned propidCharacterization = 2; static const GUID guidDocSummary = DocPropSetGuid; static const PROPID propidTitle = PIDSI_TITLE; // // Given a registry string like \Registry\Machine\System\CurrentControlSet\... // you skip the first 18 characters if using w/ HKEY_LOCAL_MACHINE. // unsigned const SKIP_TO_LM = 18; // ------------------------------------------------------------------------- // DO NOT VIOLATE THE FOLLOWING LOCK HIERARCHY // // 1. DocStore Lock // 2. CiCat Admin Lock // 3. CiCat Lock // 4. Resman Lock // 5. ScopeTable Lock // 6. NotifyMgr Lock // 7. ScanMgr Lock // 8. Propstore write lock // 9. Propstore lock record // // It is okay for a thread to acquire lock at level X and then acquire a lock // at a level >= X but not locks < X. This will avoid deadlock situations. // // SrikantS - April 25, 1996 // SrikantS _ Dec 31, 1996 - Added DocStore as the highest level lock // // ------------------------------------------------------------------------- //+------------------------------------------------------------------------- // // Member: CiCat::CiCat, public // // Synopsis: Creates a new 'content index catalog' // // Arguments: [docStore] - Doc store // [workMan] - Work queue manager // [wcsCatPath] - root path of catalog // [fVersionChange] - Set to true if there is a format version // change. // [pwcName] - name of the catalog from the registry // // History: 10-Mar-92 BartoszM Created // 14-mar-92 KyleP Added Content index object. // 03-Mar-98 KitmanH Added code to deal with read-only catalogs // 02-Apr-98 KitmanH Start the CiCat in r/o mode if // fOpenForReadOnly is TRUE // 01-Nov-98 KLam Pass DiskSpaceToLeave to CiStorage // //-------------------------------------------------------------------------- CiCat::CiCat ( CClientDocStore & docStore, CWorkManager & workMan, WCHAR const * wcsCatPath, BOOL &fVersionChange, BOOL fOpenForReadOnly, CDrvNotifArray & DrvNotifArray, WCHAR const * pwcName, BOOL fLeaveCorruptCatalog ) : _regParams( pwcName ), _ulSignature( LONGSIG( 'c', 'c', 'a', 't' ) ), _fIsReadOnly( _regParams.IsReadOnly() ), _statusMonitor(_wcsCatDir), _pStorage(0), _state(eStarting), _docStore(docStore), _PartId(1), _fInitialized(FALSE), _workMan( workMan ), #pragma warning( disable : 4355 ) // this used in base initialization _strings( _propstoremgr, *this ), _fileIdMap( _propstoremgr ), _scanMgr(*this), _usnMgr(*this), _notify(*this, _scanMgr), _scopeTable(*this, _notify, _scanMgr, _usnMgr), #pragma warning( default : 4355 ) _fRecovering( FALSE ), _fRecoveryCompleted( FALSE ), _propstoremgr( _regParams.GetMinDiskSpaceToLeave() ), _fAutoAlias( TRUE ), _fIndexW3Roots( FALSE ), _fIndexNNTPRoots( FALSE ), _fIndexIMAPRoots( FALSE ), _W3SvcInstance( 1 ), _NNTPSvcInstance( 1 ), _IMAPSvcInstance( 1 ), _fIsIISAdminAlive( FALSE ), _impersonationTokenCache( pwcName ), _evtRescanTC( 0 ), _cIISSynchThreads( 0 ), _cUsnVolumes(0), _DrvNotifArray(DrvNotifArray) { Win4Assert( 0 != pwcName ); ciDebugOut(( DEB_WARN, "CiCat::CiCat: %ws\n", pwcName )); CImpersonateSystem impersonate; // // Configure the property store // _propstoremgr.SetBackupSize(_regParams.GetPrimaryStoreBackupSize(), PRIMARY_STORE); _propstoremgr.SetMappedCacheSize(_regParams.GetPrimaryStoreMappedCache(), PRIMARY_STORE); _propstoremgr.SetBackupSize(_regParams.GetSecondaryStoreBackupSize(), SECONDARY_STORE); _propstoremgr.SetMappedCacheSize(_regParams.GetSecondaryStoreMappedCache(), SECONDARY_STORE); fVersionChange = FALSE; RtlZeroMemory( &_ftLastCLFlush, sizeof(_ftLastCLFlush) ); // // Get path to hives. Needed to avoid indexing them (oplock problems). // // // Use _wcsCatDir as scratch space. // WCHAR const wcsConfigDir[] = L"\\config"; if ( 0 != GetSystemDirectory( _wcsCatDir, (sizeof(_wcsCatDir) - sizeof(wcsConfigDir))/sizeof(WCHAR) ) ) { wcscat( _wcsCatDir, wcsConfigDir ); } // // Set up catalog paths // CLowcaseBuf PathBuf( wcsCatPath ); if ( PathBuf.Length() >= MAX_CAT_PATH ) { ciDebugOut(( DEB_ERROR, "Path for catalog (%ws) is too long\n", wcsCatPath )); CCiStatusMonitor::ReportPathTooLong( wcsCatPath ); THROW( CException( STATUS_INVALID_PARAMETER ) ); } _xwcsDriveName.SetBuf( PathBuf.Get(), wcslen( PathBuf.Get() ) + 1 ); wcscpy ( _wcsCatDir, PathBuf.Get() ); wcscat ( _wcsCatDir, CAT_DIR ); _CatDir.Init( _wcsCatDir, wcslen( _wcsCatDir ) ); SetName( pwcName ); BuildRegistryScopesKey( _xScopesKey, pwcName ); // Note: indexsrv uses the full path with catalog.wci while query // uses just the cat path. CSharedNameGen nameGen( wcsCatPath ); _evtRescanTC.Set( new CEventSem( nameGen.GetRescanTCEventName() ) ); _evtRescanTC->Reset(); _fAutoAlias = _regParams.IsAutoAlias(); _fIndexW3Roots = _regParams.IsIndexingW3Roots(); _fIndexNNTPRoots = _regParams.IsIndexingNNTPRoots(); _fIndexIMAPRoots = _regParams.IsIndexingIMAPRoots(); _W3SvcInstance = _regParams.GetW3SvcInstance(); _NNTPSvcInstance = _regParams.GetNNTPSvcInstance(); _IMAPSvcInstance = _regParams.GetIMAPSvcInstance(); // CImpersonateSystem::_fRunningAsSystem will already be set as TRUE // if running in CiSvc if ( _fIndexW3Roots || _fIndexNNTPRoots || _fIndexIMAPRoots ) { CImpersonateSystem::SetRunningAsSystem(); BOOL fInstalled; _fIsIISAdminAlive = CMetaDataMgr::IsIISAdminUp( fInstalled ); if ( !_fIsIISAdminAlive ) { // if IIS isn't installed, don't bother at all if ( !fInstalled ) { _fIndexW3Roots = FALSE; _fIndexNNTPRoots = FALSE; _fIndexIMAPRoots = FALSE; ciDebugOut(( DEB_WARN, "IIS isn't installed!!!\n" )); } EvtLogIISAdminNotAvailable(); } } _impersonationTokenCache.Initialize( CI_ACTIVEX_NAME, _fIndexW3Roots, _fIndexNNTPRoots, _fIndexIMAPRoots, _W3SvcInstance, _NNTPSvcInstance, _IMAPSvcInstance ); #if CIDBG == 1 if ( _fIndexW3Roots ) ciDebugOut(( DEB_ITRACE, "Indexing W3 roots in %d\n", _W3SvcInstance )); if ( _fIndexNNTPRoots ) ciDebugOut(( DEB_ITRACE, "Indexing NNTP roots in %d\n", _NNTPSvcInstance )); if ( _fIndexIMAPRoots ) ciDebugOut(( DEB_ITRACE, "Indexing IMAP roots in %d\n", _IMAPSvcInstance )); ciDebugOut(( DEB_ITRACE, "_fIsIISAdminAlive: %d\n", _fIsIISAdminAlive )); #endif // CIDBG == 1 // obtain an ICiCAdviseStatus interface pointer to use ICiCAdviseStatus *pAdviseStatus = 0; SCODE sc = _docStore.QueryInterface( IID_ICiCAdviseStatus, (void **) &pAdviseStatus); if ( S_OK != sc ) { Win4Assert( 0 == pAdviseStatus ); THROW( CException(sc) ); } _xAdviseStatus.Set(pAdviseStatus); XPtr xStorage; _pStorage = new CiStorage ( _wcsCatDir, _xAdviseStatus.GetReference(), _regParams.GetMinDiskSpaceToLeave(), FSCI_VERSION_STAMP, _fIsReadOnly ); xStorage.Set( _pStorage ); // in case the catalog allows only read-only access, but it was not marked // as read-only in the registry, refresh _fIsReadOnly value and setreadonly() // for _scanMgr, if _pStorage is found as IsReadOnly() after its construction // Also if we are in "net paused" state, we're restarting the CiCat in r/o // mode if ( _pStorage->IsReadOnly() || fOpenForReadOnly ) { _fIsReadOnly = TRUE; _scanMgr.SetReadOnly(); _pStorage->SetReadOnly(); //refresh it if we're in "net pause" } // // The scope table is initialized to check the corruption status // and format version. // NTSTATUS status = STATUS_SUCCESS; TRY { _scopeTable.FastInit(); } CATCH( CException, e ) { status = e.GetErrorCode(); ciDebugOut(( DEB_ERROR, "Error 0x%x while initializing scopetable\n", status )); } END_CATCH if ( status != STATUS_SUCCESS ) { if ( IsCiCorruptStatus(status) || status == CI_INCORRECT_VERSION ) { // if catalog is corrupted and the catalog is readonly, We cannot recover if ( IsReadOnly() ) { Win4Assert( !"Catalog is read-only, cannot be recovered!" ); _statusMonitor.LogEvent( CCiStatusMonitor::eInitFailed, status ); THROW( CException( status ) ); } #if CIDBG==1 //if ( IsCiCorruptStatus(status) ) // Win4Assert( !"FSCI(Catalog) Data Corruption" ); #endif if ( status == CI_INCORRECT_VERSION ) fVersionChange = TRUE; // If instructed to leave corrupt catalogs, throw now. // Don't leave around catalogs that need to change due to a // version change. Note that some corruptions may just look // like a version change, but that's the way it goes. if ( fLeaveCorruptCatalog && !fVersionChange ) { _statusMonitor.LogEvent( CCiStatusMonitor::eInitFailed, status ); Win4Assert( !"leaving corrupt catalog" ); THROW( CException( status ) ); } // // Log an event that we are doing automatic recovery. // _statusMonitor.LogEvent( CCiStatusMonitor::eCiRemoved ); _pStorage->DeleteAllFsCiFiles(); _statusMonitor.Reset(); _scopeTable.FastInit(); } else { if ( status == HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) ) _statusMonitor.LogEvent( CCiStatusMonitor::eInitFailed, status ); THROW( CException( status ) ); } } xStorage.Acquire(); // // Start all the threads up, after we can THROW. // _notify.Resume(); _usnMgr.Resume(); _scanMgr.Resume(); } //CiCat //+--------------------------------------------------------------------------- // // Member: CiCat::EvtLogIISAdminNotAvailable(), private // // Synopsis: Logs an event that iisadmin was needed but not available // // History: 2-19-97 dlee Created // //---------------------------------------------------------------------------- void CiCat::EvtLogIISAdminNotAvailable() { TRY { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_WARNING_TYPE, CI_SERVICE_CATEGORY, MSG_CI_IISADMIN_NOT_AVAILABLE, 0 ); eventLog.ReportEvent( item ); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "Exception 0x%X while writing to event log\n", e.GetErrorCode() )); } END_CATCH } //EvtLogIISAdminNotAvailable //+------------------------------------------------------------------------- // // Method: CiCat::PathToFileId, private // // Synopsis: Looks up a fileid based on a path // // Arguments: [funnyPath] -- Full funny path of the file. // // Returns: The fileid of the file or fileIdInvalid if not found. // // History: 3-3-98 dlee Created // //-------------------------------------------------------------------------- FILEID CiCat::PathToFileId( const CFunnyPath & funnyPath ) { FILEID fileId = fileIdInvalid; BOOL fAppendBackSlash = FALSE; // For volume \\?\D:, NtQueryInformationFile with the following // error: 0xC0000010L - STATUS_INVALID_DEVICE_REQUEST. Need to append a \, // to make it work. We need to append the '\'only in case of volume path. if ( 2 == funnyPath.GetActualLength() && !funnyPath.IsRemote() ) { Win4Assert( L':' == (funnyPath.GetActualPath())[1] ); ((CFunnyPath&)funnyPath).AppendBackSlash(); // override const fAppendBackSlash = TRUE; } HANDLE h; NTSTATUS status = CiNtOpenNoThrow( h, funnyPath.GetPath(), FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0 ); if ( fAppendBackSlash ) { ((CFunnyPath&)funnyPath).RemoveBackSlash(); // override const } if ( NT_SUCCESS( status ) ) { SHandle xFile( h ); FILE_INTERNAL_INFORMATION fii; IO_STATUS_BLOCK IoStatus; status = NtQueryInformationFile( h, &IoStatus, &fii, sizeof fii, FileInternalInformation ); if ( NT_SUCCESS( status ) ) status = IoStatus.Status; if ( NT_SUCCESS( status ) ) { fileId = fii.IndexNumber.QuadPart; Win4Assert( fileIdInvalid != fileId ); } } if ( NT_ERROR( status ) ) { // ignore and return fileIdInvalid if the file doesn't exist. if ( ! ( IsSharingViolation( status ) || STATUS_ACCESS_DENIED == status || STATUS_DELETE_PENDING == status || STATUS_OBJECT_PATH_NOT_FOUND == status || STATUS_OBJECT_NAME_NOT_FOUND == status || STATUS_OBJECT_NAME_INVALID == status || STATUS_NO_MEDIA_IN_DEVICE == status || STATUS_NONEXISTENT_SECTOR == status || STATUS_IO_REPARSE_TAG_NOT_HANDLED == status ) ) { #if CIDBG == 1 if ( STATUS_WRONG_VOLUME != status && STATUS_VOLUME_DISMOUNTED != status && STATUS_INSUFFICIENT_RESOURCES != status && STATUS_UNRECOGNIZED_VOLUME != status ) { ciDebugOut(( DEB_WARN, "error 0x%x, can't get fileid for '%ws'\n", status, funnyPath.GetPath() )); char acTemp[ 200 ]; sprintf( acTemp, "New error %#x from PathToFileId. Call DLee", status ); Win4AssertEx(__FILE__, __LINE__, acTemp); } #endif THROW( CException( status ) ); } else { ciDebugOut(( DEB_ITRACE, "(ok) pathtofileid failed %#x\n", status )); } } return fileId; } //PathToFileId //+------------------------------------------------------------------------- // // Method: CiCat::LokLookupWid, private // // Synopsis: Looks up a workid based on a fileid or path // // Arguments: [lcaseFunnyPath] -- Full path of the file. // [fileId] -- The fileid of the file or fileIdInvalid if // the fileid should be found based on pwcPath. // Returns the fileId if it was looked up. // // Returns: The workid of the file or widInvalid if not found. // // History: 3-3-98 dlee Created // //-------------------------------------------------------------------------- WORKID CiCat::LokLookupWid( const CLowerFunnyPath & lcaseFunnyPath, FILEID & fileId ) { Win4Assert( _mutex.IsHeld() ); if ( fileIdInvalid == fileId ) fileId = PathToFileId( lcaseFunnyPath ); if ( fileIdInvalid == fileId ) return widInvalid; return _fileIdMap.LokFind( fileId, MapPathToVolumeId( lcaseFunnyPath.GetActualPath() ) ); } //LokLookupWid //+------------------------------------------------------------------------- // // Method: CiCat::PropertyToPropId // // Synopsis: Locate pid for property // // Arguments: [ps] -- Property specification (name) // [fCreate] -- TRUE if non-existent mapping should be created // // Returns: The pid of [ps]. // // History: 09 Jan 1996 AlanW Created // //-------------------------------------------------------------------------- PROPID CiCat::PropertyToPropId( CFullPropSpec const & ps, BOOL fCreate ) { FULLPROPSPEC const * fps = ps.CastToStruct(); if ( IsStarted() ) return _docStore._PropertyToPropid( fps, fCreate ); return _propMapper.PropertyToPropId( ps, fCreate); } //+------------------------------------------------------------------------- // // Member: CiCat::GetStorage, public // // Returns: Storage object for catalog // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- PStorage& CiCat::GetStorage () { return *_pStorage; } //+------------------------------------------------------------------------- // // Member: CiCat::GetDriveName, public // // Returns: Drive name // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- WCHAR * CiCat::GetDriveName() { return( _xwcsDriveName.Get() ); } //+------------------------------------------------------------------------- // // Member: CiCat::LokExists, private // // Returns: TRUE if a catalog directory exists. // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- BOOL CiCat::LokExists() { Win4Assert( _mutex.IsHeld() ); if ( IsStarted() ) return TRUE; DWORD dwAttr = GetFileAttributesW( _wcsCatDir ); if ( dwAttr == 0xFFFFFFFF ) { ciDebugOut((DEB_ITRACE, "Directory %ws does not exist\n", _wcsCatDir)); return(FALSE); } return(TRUE); } //+------------------------------------------------------------------------- // // Member: CiCat::LokCreate, private // // Synopsis: Creates content index. // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- void CiCat::LokCreate() { Win4Assert( _mutex.IsHeld() ); CImpersonateSystem impersonate; CreateDirectory ( _wcsCatDir, 0 ); ciDebugOut ((DEB_ITRACE, "Catalog Directory %ws created\n", _wcsCatDir )); } //+--------------------------------------------------------------------------- // // Member: CiCat::DoRecovery // // Synopsis: Does the "long" initialization process. If any recovery // needs to be done, it will be done here. This is done in // the scan threads context and will not allow any other // writer to come in. // // History: 3-06-96 srikants Created // 9-Nov-98 KLam Throws CI_E_CONFIG_DISK_FULL if there is no disk space // //---------------------------------------------------------------------------- void CiCat::DoRecovery() { ciDebugOut(( DEB_ITRACE, "Doing long initialization step\n" )); NTSTATUS status = STATUS_SUCCESS; TRY { if ( !IsStarted() || _statusMonitor.IsCorrupt() ) { // Win4Assert( !"Corrupt catalog" ); _pStorage->ReportCorruptComponent( L"CiCatalog1" ); THROW( CException( CI_CORRUPT_CATALOG ) ); } // // Check for disk full situation and process it accordingly. // BOOL fLow = _docStore.VerifyIfLowOnDiskSpace(); if ( fLow ) { THROW( CException( CI_E_CONFIG_DISK_FULL ) ); } else if ( IsLowOnDisk() ) { // // A disk-full situation existed before. Clear it up. // NoLokClearDiskFull(); } if ( _propstoremgr.IsDirty() ) _statusMonitor.ReportPropStoreRecoveryStart(); ULONG cInconsistencies; _propstoremgr.LongInit( _fRecovering, cInconsistencies, UpdateDuringRecovery, this ); if ( _fRecovering ) { if (cInconsistencies) { _statusMonitor.ReportPropStoreRecoveryError( cInconsistencies ); THROW( CException( CI_CORRUPT_CATALOG ) ); } else _statusMonitor.ReportPropStoreRecoveryEnd(); } // // Add properties to primary store. Will only happen on first call. // ULONG_PTR ulToken = _propstoremgr.BeginTransaction(); _propstoremgr.Setup( pidLastSeenTime, VT_FILETIME, sizeof (FILETIME), ulToken, FALSE, PRIMARY_STORE ); _propstoremgr.Setup( pidParentWorkId, VT_UI4, sizeof( WORKID ), ulToken, FALSE, PRIMARY_STORE ); _propstoremgr.Setup( pidAttrib, VT_UI4, sizeof (ULONG), ulToken, FALSE, PRIMARY_STORE ); // // Usn/Ntfs 5.0 properties // _propstoremgr.Setup( pidFileIndex, VT_UI8, sizeof( FILEID ), ulToken, FALSE, PRIMARY_STORE ); _propstoremgr.Setup( pidVolumeId, VT_UI4, sizeof( VOLUMEID ), ulToken, FALSE, PRIMARY_STORE ); // // User properties destined to the primary store // RecoverUserProperties( ulToken, PRIMARY_STORE ); _propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath ); // // Add properties to secondary store. Will only happen on first call. // ulToken = _propstoremgr.BeginTransaction(); // // 'Standard' properties. // CFullPropSpec psTitle( guidDocSummary, propidTitle ); PROPID pidTitle = PropertyToPropId( psTitle, TRUE ); _propstoremgr.Setup( pidSize, VT_I8, sizeof (LONGLONG), ulToken, FALSE, SECONDARY_STORE ); _propstoremgr.Setup( pidWriteTime, VT_FILETIME, sizeof (LONGLONG), ulToken, FALSE, SECONDARY_STORE ); _propstoremgr.Setup( pidTitle, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); // // 'Characterization' // CFullPropSpec psCharacterization( guidCharacterization, propidCharacterization ); PROPID pidCharacterization = PropertyToPropId( psCharacterization, TRUE ); BOOL fCanStoreChar = _propstoremgr.CanStore( pidCharacterization ); if ( _regParams.GetGenerateCharacterization() ) { if ( ! fCanStoreChar ) _propstoremgr.Setup( pidCharacterization, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); } else { // size of 0 means remove the property if ( fCanStoreChar ) _propstoremgr.Setup( pidCharacterization, VT_LPWSTR, 0, ulToken, TRUE, SECONDARY_STORE ); } // // IMAP properties // if ( _fIndexIMAPRoots ) { CFullPropSpec psNewsReceivedDate( guidNNTP, propidNewsReceivedDate ); PROPID pidNewsReceivedDate = PropertyToPropId( psNewsReceivedDate, TRUE ); if ( !_propstoremgr.CanStore( pidNewsReceivedDate ) ) _propstoremgr.Setup( pidNewsReceivedDate, VT_FILETIME, 8, ulToken, TRUE, SECONDARY_STORE ); } // // IMAP/NNTP properties // if ( _fIndexNNTPRoots || _fIndexIMAPRoots ) { CFullPropSpec psNewsDate( guidNNTP, propidNewsDate ); CFullPropSpec psNewsArticleid( guidNNTP, propidNewsArticleid ); PROPID pidNewsDate = PropertyToPropId( psNewsDate, TRUE ); PROPID pidNewsArticleid = PropertyToPropId( psNewsArticleid, TRUE ); if ( !_propstoremgr.CanStore( pidNewsDate ) ) _propstoremgr.Setup( pidNewsDate, VT_FILETIME, 8, ulToken, TRUE, SECONDARY_STORE ); if ( !_propstoremgr.CanStore( pidNewsArticleid ) ) _propstoremgr.Setup( pidNewsArticleid, VT_UI4, 4, ulToken, TRUE, SECONDARY_STORE ); } // // NNTP properties // if ( _fIndexNNTPRoots ) { CFullPropSpec psNewsSubject( guidNNTP, propidNewsSubject ); CFullPropSpec psNewsFrom( guidNNTP, propidNewsFrom ); CFullPropSpec psNewsGroup( guidNNTP, propidNewsGroup ); CFullPropSpec psNewsGroups( guidNNTP, propidNewsGroups ); CFullPropSpec psNewsReferences( guidNNTP, propidNewsReferences ); CFullPropSpec psNewsMsgid( guidNNTP, propidNewsMsgid ); PROPID pidNewsSubject = PropertyToPropId( psNewsSubject, TRUE ); PROPID pidNewsFrom = PropertyToPropId( psNewsFrom, TRUE ); PROPID pidNewsGroup = PropertyToPropId( psNewsGroup, TRUE ); PROPID pidNewsGroups = PropertyToPropId( psNewsGroups, TRUE ); PROPID pidNewsReferences = PropertyToPropId( psNewsReferences, TRUE ); PROPID pidNewsMsgid = PropertyToPropId( psNewsMsgid, TRUE ); if ( !_propstoremgr.CanStore( pidNewsSubject ) ) _propstoremgr.Setup( pidNewsSubject, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); if ( !_propstoremgr.CanStore( pidNewsFrom ) ) _propstoremgr.Setup( pidNewsFrom, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); if ( !_propstoremgr.CanStore( pidNewsGroup ) ) _propstoremgr.Setup( pidNewsGroup, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); if ( !_propstoremgr.CanStore( pidNewsGroups ) ) _propstoremgr.Setup( pidNewsGroups, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); if ( !_propstoremgr.CanStore( pidNewsReferences ) ) _propstoremgr.Setup( pidNewsReferences, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); if ( !_propstoremgr.CanStore( pidNewsMsgid ) ) _propstoremgr.Setup( pidNewsMsgid, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE ); } // // User properties destined to the secondary store // RecoverUserProperties( ulToken, SECONDARY_STORE ); _propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath); // // The strings/fileidmap version numbers are 1 greater than the index // version, since we needed to change the on-disk format of the hash // tables but don't want to force a reindex of the catalog. Hash tables // are rebuilt automatically from the property store if the version is // incorrect. // _strings.LongInit( CURRENT_VERSION_STAMP + 1, _fRecovering ); _fileIdMap.LongInit( CURRENT_VERSION_STAMP + 1, _fRecovering ); ciDebugOut(( DEB_ITRACE, "Long Initialization procedure complete\n" )); _evtInitialized.Set(); _fInitialized = TRUE; #ifdef CI_FAILTEST NTSTATUS failStatus = CI_CORRUPT_CATALOG; ciFAILTEST( failStatus ); #endif // CI_FAILTEST } CATCH( CException, e ) { // // Set that the initialization failed and CI is not mounted. // status = e.GetErrorCode(); } END_CATCH if ( STATUS_SUCCESS != status ) { HandleError( status ); if ( !CiCat::IsDiskLowError(status) ) { // // Report any error other than the disk full error. // CLock lock(_mutex); if ( _statusMonitor.GetStatus() != status ) { _statusMonitor.SetStatus( status ); _statusMonitor.ReportInitFailure(); } } THROW( CException( status ) ); } _fRecovering = FALSE; } //DoRecovery //+--------------------------------------------------------------------------- // // Member: CiCat::StartScansAndNotifies // // Synopsis: Enables scanning and tracking notifications. // // History: 12-09-96 srikants Created // // Notes: Must be called only after recovery is complete. // //---------------------------------------------------------------------------- void CiCat::StartScansAndNotifies() { Win4Assert( !_fRecovering ); NTSTATUS status = STATUS_SUCCESS; TRY { _scopeTable.StartUp( _docStore, GetPartition() ); _evtPh2Init.Set(); #ifdef CI_FAILTEST NTSTATUS failStatus = CI_CORRUPT_CATALOG; ciFAILTEST( failStatus ); #endif // CI_FAILTEST // // Just to make sure the shadow virtual root registry entries // didn't get messed up... // AddShadowScopes(); if ( _fIndexW3Roots || _fIndexNNTPRoots || _fIndexIMAPRoots ) { // // DO NOT OBTAIN LOCK WHILE DOING SYNC WITH IIS // // setup tracking even if iisadmin is hosed -- _notify // will poll until iisadmin is up. _notify.TrackIISVRoots( _fIndexW3Roots, _W3SvcInstance, _fIndexNNTPRoots, _NNTPSvcInstance, _fIndexIMAPRoots, _IMAPSvcInstance ); } // Always synch with IIS. Indexing of IIS may have been on // on the previous run of this catalog but not for this run, // so we need to remove those vroots SynchWithIIS( TRUE, FALSE ); if ( 0 != GetName() ) { SynchWithRegistryScopes( FALSE ); _notify.TrackScopesInRegistry(); } _notify.TrackCiRegistry(); } CATCH( CException, e ) { // // Set that the initialization failed and CI is not mounted. // status = e.GetErrorCode(); } END_CATCH if ( STATUS_SUCCESS != status ) { HandleError( status ); if ( !CiCat::IsDiskLowError(status) ) { // // Report any error other than the disk full error. // CLock lock(_mutex); if ( _statusMonitor.GetStatus() != status ) { _statusMonitor.SetStatus( status ); _statusMonitor.ReportInitFailure(); } } THROW( CException( status ) ); } // ++++++++++++++++++++++++++++++++++++++++++++++++++++ { CLock lock(_mutex); if( IsStarting() ) _state = eStarted; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++ } //StartScansAndNotifies //+--------------------------------------------------------------------------- // // Member: CCiCat::SetRecoveryCompleted // // Synopsis: Marks that recovery is complete on persistent data // structures. It informs the docstore about the completed // recovery which can then enable filtering. // // History: 12-09-96 srikants Created // // Notes: MUST BE CALLED ONLY BY A WORKER THREAD. // Should NOT throw. // //---------------------------------------------------------------------------- void CiCat::SetRecoveryCompleted() { Win4Assert( !CImpersonateSystem::IsImpersonated() ); Win4Assert( !_fRecovering ); TRY { _docStore._SetCiCatRecovered(); _fRecoveryCompleted = TRUE; } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "SetRecoveryCompleted error (0x%X)\n", e.GetErrorCode() )); } END_CATCH } //SetRecoveryCompleted //+--------------------------------------------------------------------------- // // Member: CiCat::EnableUpdateNotifies // // Synopsis: Called by the doc store to start scanning and filtering // when CI is ready to receive notification changes. // // History: 12-09-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::EnableUpdateNotifies() { Win4Assert( !_fRecovering ); Win4Assert( _fInitialized ); _scanMgr.StartScansAndNotifies(); } //+------------------------------------------------------------------------- // // Member: CiCat::LokInit, private // // Synopsis: Opens content index. Index corruption failures during // LokInit do not require a restart of the process to blow // away the corrupt index // // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- void CiCat::LokInit() { Win4Assert( _mutex.IsHeld() ); ciDebugOut ((DEB_ITRACE, "CiCat::LokInit.. Catalog Directory is %ws\n", _wcsCatDir )); if ( 0 == _xCiManager.GetPointer() ) THROW( CException( CI_E_NOT_INITIALIZED ) ); #ifdef CI_FAILTEST NTSTATUS failStatus = CI_CORRUPT_CATALOG; ciFAILTEST( failStatus ); #endif // CI_FAILTEST TRY { ciDebugOut ((DEB_ITRACE, "Opening Catalog %ws\n", _wcsCatDir )); if ( !_scopeTable.IsInit() ) _scopeTable.FastInit(); // Win4Assert( !_scopeTable.IsCiDataCorrupt() ); // Commented out because it's hit by version upgrade. if ( _scopeTable.IsFsCiDataCorrupt() ) { ciDebugOut(( DEB_ERROR, "Persistent corruption detected in ci %ws\n", _wcsCatDir )); // Win4Assert( !"Corrupt scope table" ); // Commented out because it's hit by version upgrade. _pStorage->ReportCorruptComponent( L"CiCatalog2" ); THROW( CException( CI_CORRUPT_CATALOG ) ); } _propstoremgr.FastInit( _pStorage ); // // If we went down dirty during an initial scan, blow away the // catalog and start again. // BOOL fBackedUpMode = _propstoremgr.IsBackedUpMode(); ciDebugOut(( DEB_ITRACE, "startup: is backed up mode: %d\n", fBackedUpMode )); if ( !fBackedUpMode ) { _pStorage->ReportCorruptComponent( L"Crash during scan" ); THROW( CException( CI_CORRUPT_CATALOG ) ); } #ifdef CI_FAILTEST ciFAILTEST( failStatus ); #endif // CI_FAILTEST PRcovStorageObj * pObj = _pStorage->QueryPidLookupTable( 0 ); // Init takes ownership of the object regardless of whether it // succeeds. if ( !_PidTable.Init( pObj ) ) { ciDebugOut ((DEB_ERROR, "Failed init of PidTable\n")); // Win4Assert( !"Corrupt pid table" ); _pStorage->ReportCorruptComponent( L"CiCatalog3" ); THROW (CException(CI_CORRUPT_CATALOG)); } #ifdef CI_FAILTEST ciFAILTEST( failStatus ); #endif // CI_FAILTEST if ( !_SecStore.Init( _pStorage ) ) { ciDebugOut ((DEB_ERROR, "Failed init of SecStore\n")); // Win4Assert( !"Corrupt security store" ); _pStorage->ReportCorruptComponent( L"CiCatalog4" ); THROW (CException(CI_CORRUPT_CATALOG)); } #ifdef CI_FAILTEST ciFAILTEST( failStatus ); #endif // CI_FAILTEST // // On initial creation, this will happen. // A transaction can only operate on one store at a time, // so separate Setup calls into two transactions. // ULONG_PTR ulToken = _propstoremgr.BeginTransaction(); _propstoremgr.Setup( pidSecurity, VT_UI4, sizeof (ULONG), ulToken, FALSE, PRIMARY_STORE ); // // Create a pointer to secondary store top-level wid, which will be the // counter part of the top-level in the primary // _propstoremgr.Setup( pidSecondaryStorage, VT_UI4, sizeof (ULONG), ulToken, FALSE, PRIMARY_STORE ); _propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath ); ulToken = _propstoremgr.BeginTransaction(); _propstoremgr.Setup( pidPath, VT_LPWSTR, MAX_PATH / 3, ulToken, FALSE, SECONDARY_STORE ); _propstoremgr.Setup( pidVirtualPath, VT_UI4, 4, ulToken, FALSE, SECONDARY_STORE ); _propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath ); // // The strings/fileidmap version numbers are 1 greater than the index // version, since we needed to change the on-disk format of the hash // tables but don't want to force a reindex of the catalog. Hash tables // are rebuilt automatically from the property store if the version is // incorrect. // _strings.FastInit( _pStorage, CURRENT_VERSION_STAMP + 1 ); _fileIdMap.FastInit( _pStorage, CURRENT_VERSION_STAMP + 1 ); #ifdef CI_FAILTEST ciFAILTEST( failStatus ); #endif // CI_FAILTEST ciDebugOut ((DEB_ITRACE, "Opening Content Index %ws\n", _wcsCatDir )); #ifdef CI_FAILTEST ciFAILTEST( failStatus ); #endif // CI_FAILTEST _state = eQueryOnly; // make fixups available so we can start to process queries asap SetupScopeFixups(); _statusMonitor.ReportCIStarted(); if ( !_fIsReadOnly ) DoRecovery(); } CATCH(CException, e) { ciDebugOut (( DEB_ERROR, "Catalog initialization failed, CI exception = %08x\n", e.GetErrorCode() )); _statusMonitor.SetStatus( e.GetErrorCode() ); _strings.Empty(); _fileIdMap.Empty(); _propstoremgr.Empty(); _SecStore.Empty(); _PidTable.Empty(); _scopeTable.Empty(); } END_CATCH if ( !_statusMonitor.IsOk() ) { SCODE sc = _statusMonitor.GetStatus(); // // If the status is corrupt, we'll blow away the catalog and start // again. Note that a version change error at this point is due to // a corrupt file, since normal version changes are detected earlier. // if ( ! ( IsCiCorruptStatus( sc ) || ( CI_INCORRECT_VERSION == sc ) ) ) _statusMonitor.ReportInitFailure(); THROW( CException( sc ) ); } } //LokInit //+------------------------------------------------------------------------- // // Member: CiCat::ForceMerge, public // // Synopsis: Forces a merge in the content index // // Arguments: [partid] -- Partition id // // History: 10-Jan-96 KyleP Added header // 03-11-98 kitmanh Don't merge if catalog is // opened for read-only. Also raise // assertion fail to warn // //-------------------------------------------------------------------------- NTSTATUS CiCat::ForceMerge( PARTITIONID partid ) { if ( _pStorage->IsReadOnly() ) { ciDebugOut(( DEB_WARN, "Cannot merge. Catalog is opened for read-only.\n" )); return STATUS_ACCESS_DENIED; } XInterface xCiManager; // ++++++++++++++++++++++++++++++++++++++++++++++++++++ { CLock lock(_mutex); if ( IsShuttingDown() || 0 == _xCiManager.GetPointer() ) { return STATUS_TOO_LATE; } else { _xCiManager->AddRef(); xCiManager.Set( _xCiManager.GetPointer() ); } } // ----------------------------------------------------- return xCiManager->ForceMerge( CI_MASTER_MERGE ); } //ForceMerge //+------------------------------------------------------------------------- // // Member: CiCat::AbortMerge, public // // Synopsis: Stops a merge in the content index // // Arguments: [partid] -- Partition id // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- NTSTATUS CiCat::AbortMerge( PARTITIONID partid ) { XInterface xCiManager; // ++++++++++++++++++++++++++++++++++++++++++++++++++++ { CLock lock(_mutex); if ( IsShuttingDown() || 0 == _xCiManager.GetPointer() ) { return STATUS_TOO_LATE; } else { _xCiManager->AddRef(); xCiManager.Set( _xCiManager.GetPointer() ); } } // ----------------------------------------------------- return xCiManager->AbortMerge(); } //AbortMerge //+------------------------------------------------------------------------- // // Member: CiCat::DumpWorkId, public // // Synopsis: Debug method to dump CI data // // Arguments: [wid] -- Workid // [iid] -- Index id (0 if any) // [pb] -- Return buffer // [cb] -- Size of return buffer // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- void CiCat::DumpWorkId( WORKID wid, ULONG iid, BYTE * pb, ULONG cb ) { Win4Assert( !"Backdoor not supported in framework" ); #if 0 if ( 0 != _pCci ) { ULONG UNALIGNED * pul = (ULONG UNALIGNED *)pb; *pul = CiDumpWorkId; pul++; *pul = wid; pul++; *pul = iid; _pCci->BackDoor( cb, pb ); } #endif // 0 } //+------------------------------------------------------------------------- // // Member: CiCat::CreateContentIndex, public // // Synopsis: Creates a virgin content index. // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- SCODE CiCat::CreateContentIndex() { Win4Assert( !"Must Not Be Called" ); InitIf(); return IsStarted(); } //+------------------------------------------------------------------------- // // Member: CiCat::EmptyContentIndex, public // // Synopsis: Deletes content index (including storage) // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- void CiCat::EmptyContentIndex() { XInterface xCiManager; // ++++++++++++++++++++++++++++++++++++++++++++++++++++ { CLock lock(_mutex); if ( IsShuttingDown() || 0 == _xCiManager.GetPointer() ) { return; } else { _xCiManager->AddRef(); xCiManager.Set( _xCiManager.GetPointer() ); } } // ----------------------------------------------------- SCODE sc = xCiManager->Empty(); if ( S_OK != sc ) { ciDebugOut(( DEB_ERROR, "ICiManager::Empty failed with error (0x%X)\n", sc )); THROW( CException( sc ) ); } } //EmptyContentIndex //+------------------------------------------------------------------------- // // Member: CiCat::_IsEligibleForFiltering, private // // Synopsis: Determine if a directory should be filtered // // Arguments: [pwcsPath] -- directory path name to be tested // [ccPath] -- length of pwcsPath // // Returns: BOOL - TRUE if directory and contents should be filtered, // FALSE otherwise // // History: 23 Jun 98 VikasMan Created // // Notes: This is the function which does the work and gets called from // various versions of IsEligbleForFiltering. The path pwcsPath // has to be all lowercase. // //-------------------------------------------------------------------------- BOOL CiCat::_IsEligibleForFiltering( const WCHAR * pwcsPath, unsigned ccPath ) { AssertLowerCase( pwcsPath, ccPath ); // // These files can't be indexed or we'll either deadlock or never filter // the files. // struct SUnfilterable { WCHAR const * pwcFile; unsigned cwcFile; }; static const SUnfilterable aUnfilterable[] = { { L"\\classes.dat", 12 }, // classes hive { L"\\classes.dat.log", 16 }, // classes hive log { L"\\hiberfil.sys", 13 }, // the hibernation file { L"\\ntuser.dat", 11 }, // user hive { L"\\ntuser.dat.log", 15 }, // user hive log file { L"\\pagefile.sys", 13 }, // the pagefile { L"\\usrclass.dat", 13 }, // user classes hive file { L"\\usrclass.dat.log", 17 }, // user classes hive log file }; const cUnfilterable = sizeof aUnfilterable / sizeof aUnfilterable[0]; // // IMPORTANT OBSERVATION: // // All the entries above end in either 't', 'g', or 's' // static const WCHAR aLastLetter[] = L"tgs"; const cLastLetter = sizeof(aLastLetter)/sizeof(aLastLetter[0]) - 1; for ( unsigned i = 0; i < cLastLetter; i++ ) if ( pwcsPath[ccPath-1] == aLastLetter[i] ) { for ( unsigned j = 0; j < cUnfilterable; j++ ) { SUnfilterable const & entry = aUnfilterable[ j ]; if ( ( ccPath > entry.cwcFile ) && ( RtlEqualMemory( pwcsPath + ccPath - entry.cwcFile, entry.pwcFile, entry.cwcFile * sizeof WCHAR ) ) ) { ciDebugOut(( DEB_IWARN, "File %ws ineligible for filtering (unfilterable).\n", pwcsPath )); return FALSE; } } break; } // // Nothing in a CATALOG.WCI directory can be indexed. That is metadata. // const unsigned CAT_DIR_LEN = sizeof CAT_DIR / sizeof CAT_DIR[0]; const WCHAR * wcsComponent = pwcsPath; while ( wcsComponent = wcschr( wcsComponent + 1, L'\\' ) ) { switch ( wcsComponent[1] ) { case 'c': Win4Assert( CAT_DIR[1] == L'c' ); if ( RtlEqualMemory( wcsComponent, CAT_DIR, sizeof CAT_DIR - sizeof (WCHAR)) && ( wcsComponent[ CAT_DIR_LEN-1 ] == L'\\' || wcsComponent[ CAT_DIR_LEN-1 ] == L'\0' )) { ciDebugOut(( DEB_IWARN, "File %ws ineligible for filtering (ci metadata).\n", pwcsPath )); return FALSE; } break; case L'$': Win4Assert( SETUP_DIR_PREFIX[1] == L'$' ); if ( RtlEqualMemory( wcsComponent, SETUP_DIR_PREFIX, sizeof SETUP_DIR_PREFIX - sizeof (WCHAR) ) ) { ciDebugOut(( DEB_IWARN, "File %ws ineligible for filtering (nt setup directory).\n", pwcsPath )); return FALSE; } break; } //switch } //while // // skip indexing catalog directory. // if ( _CatDir.IsInScope( pwcsPath, ccPath ) ) return FALSE; // // Nothing the admin told us not to index can be indexed. Duh! // BOOL fFound = _scopesIgnored.RegExFind( pwcsPath ); ciDebugOut(( DEB_ITRACE, "%ws %ws\n", pwcsPath, fFound? L"not indexed" : L" indexed" )); return !fFound; } //_IsEligibleForFiltering //+------------------------------------------------------------------------- // // Member: CiCat::IsEligibleForFiltering, public // // Synopsis: Determine if a directory should be filtered // // Arguments: [wcsDirPath] -- directory path name to be tested // // Returns: BOOL - TRUE if directory and contents should be filtered, // FALSE otherwise // // History: 09 Feb 96 AlanW Created // //-------------------------------------------------------------------------- BOOL CiCat::IsEligibleForFiltering( WCHAR const* wcsDirPath ) { CLowcaseBuf Buf( wcsDirPath ); return _IsEligibleForFiltering( Buf.Get(), Buf.Length() ); } //+------------------------------------------------------------------------- // // Member: CiCat::IsEligibleForFiltering, public // // Synopsis: Determine if a directory should be filtered // // Arguments: [lcPath] -- directory path name to be tested // // Returns: BOOL - TRUE if directory and contents should be filtered, // FALSE otherwise // // History: 17 Jul 96 AlanW Created // //-------------------------------------------------------------------------- BOOL CiCat::IsEligibleForFiltering( const CLowcaseBuf & lcPath ) { return _IsEligibleForFiltering( lcPath.Get(), lcPath.Length() ); } //IsEligibleForFiltering //+------------------------------------------------------------------------- // // Member: CiCat::IsEligibleForFiltering, public // // Synopsis: Determine if a directory should be filtered // // Arguments: [lowerFunnyPath] -- directory path name to be tested // // Returns: BOOL - TRUE if directory and contents should be filtered, // FALSE otherwise // // History: 23 Jun 98 VikasMan Created // //-------------------------------------------------------------------------- BOOL CiCat::IsEligibleForFiltering( const CLowerFunnyPath & lowerFunnyPath ) { return _IsEligibleForFiltering( lowerFunnyPath.GetActualPath(), lowerFunnyPath.GetActualLength() ); } //IsEligibleForFiltering //+------------------------------------------------------------------------- // // Member: CiCat::Update, public // // Synopsis: Updates an individual workid // // Arguments: [wid] -- Workid // [partid] -- Partition id // [volumeId] -- Volume id // [usn] -- USN // [action] -- Update or delete // // History: 10-Jan-96 KyleP Added header // 07-May-97 SitaramR Usns // //-------------------------------------------------------------------------- SCODE CiCat::Update( WORKID wid, PARTITIONID partid, VOLUMEID volumeId, USN usn, ULONG action ) { InitOrCreate(); if (wid != widInvalid) { Win4Assert( 0 != _xCiManager.GetPointer() ); BOOL fDelete = CI_DELETE_OBJ == action; CDocumentUpdateInfo info( wid, volumeId, usn, fDelete ); return _xCiManager->UpdateDocument( &info ); } return S_OK; } //Update //+------------------------------------------------------------------------- // // Member: CiCat::Update // // Synopsis: Process usn notification // // Arguments: [lcaseFunnyPath] -- File path // [fileId] -- File id // [widParent] -- Parent workid // [usn] -- Usn // [pUsnVolume] -- Usn volume // [fDeleted] -- Deleted file ? // [pLock] -- If non-zero, this is the cicat lock // and we have already verified the file // isn't in the catalog yet. // // History: 07-May-97 SitaramR Created // //-------------------------------------------------------------------------- void CiCat::Update( const CLowerFunnyPath & lcaseFunnyPath, FILEID fileId, WORKID widParent, USN usn, CUsnVolume * pUsnVolume, BOOL fDeleted, CReleasableLock * pLock ) { // NOTE: We do not need cimpersonatesystem here because this is a USN // notification, so it is always local! // // Get rid of any remaining backslash // // override const here - unethical, but saves us a copy BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash(); TRY { WORKID wid; ULONG action = CI_UPDATE_OBJ; BOOL fNew = FALSE; { CLock lock( _mutex ); if ( IsShuttingDown() ) THROW( CException( STATUS_TOO_LATE ) ); Win4Assert( 0 != _xCiManager.GetPointer() ); Win4Assert( fileIdInvalid != fileId ); // The caller knows for sure that the file doesn't exist in // the fileid map, and called this under cicat lock. if ( 0 != pLock ) { wid = widInvalid; Win4Assert( widInvalid == _fileIdMap.LokFind( fileId, pUsnVolume->VolumeId() ) ); } else { wid = _fileIdMap.LokFind( fileId, pUsnVolume->VolumeId() ); } if ( fDeleted && widInvalid == wid ) { // // This file is not yet known to us. Nothing to do. // return; } Win4Assert( IsOnUsnVolume( lcaseFunnyPath.GetActualPath() ) ); if ( fDeleted ) { ciDebugOut(( DEB_ITRACE, "delete %#I64x %ws\n", fileId, lcaseFunnyPath.GetActualPath() )); action = CI_DELETE_OBJ; _fileIdMap.LokDelete( fileId, wid ); _strings.LokDelete( 0, wid, TRUE, TRUE ); } else if ( widInvalid == wid ) { fNew = TRUE; wid = _strings.LokAdd( lcaseFunnyPath.GetActualPath(), fileId, TRUE, widParent ); #if CIDBG == 1 ciDebugOut(( DEB_ITRACE, "lokadded %#I64x %#x, '%ws'\n", fileId, wid, lcaseFunnyPath.GetActualPath() )); // // Just a few lines above in LokLookupWid, we couldn't // lookup the wid using the fileid map. If we can look // it up now, something is broken. // WORKID widExisting = _fileIdMap.LokFind( fileId, pUsnVolume->VolumeId() ); if ( widInvalid != widExisting ) { Win4Assert( widInvalid != widExisting ); ciDebugOut(( DEB_ERROR, "wid %#x, widExisting %#x\n", wid, widExisting )); ciDebugOut(( DEB_ERROR, "Wid info:" )); DebugPrintWidInfo( wid ); ciDebugOut(( DEB_ERROR, "WidExisting info:" )); DebugPrintWidInfo( widExisting ); } #endif // CIDBG == 1 // // Add fileid -> wid map // _fileIdMap.LokAdd( fileId, wid, pUsnVolume->VolumeId() ); ciDebugOut(( DEB_ITRACE, "Added fileId %#I64x -> wid 0x%x mapping\n", fileId, wid )); } if ( 0 != pLock ) pLock->Release(); } SCODE sc = Update( wid, GetPartition(), pUsnVolume->VolumeId(), usn, action ); ciDebugOut(( DEB_USN, "File %ws, fileId %#I64x, widParent 0x%x, usn %#I64x, fDelete %d, processed\n", lcaseFunnyPath.GetActualPath(), fileId, widParent, usn, fDeleted )); // // Newly created files have pidLastSeenTime and pidAttrib initialized // to 0 already in CStrings::LokAdd // if ( !fNew && !fDeleted && SUCCEEDED(sc)) { FILETIME ftLastSeen; RtlZeroMemory( &ftLastSeen, sizeof(ftLastSeen) ); CStorageVariant var( ftLastSeen ); XWritePrimaryRecord rec( _propstoremgr, wid ); SCODE scW = _propstoremgr.WritePrimaryProperty( rec.GetReference(), pidLastSeenTime, var ); if ( FAILED( scW ) ) THROW( CException( scW ) ); PROPVARIANT propVar; propVar.vt = VT_UI4; propVar.ulVal = 0; scW = _propstoremgr.WritePrimaryProperty( rec.GetReference(), pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if ( FAILED( scW ) ) THROW( CException( scW ) ); } } CATCH( CException, e ) { if ( fRemoveBackSlash ) { // override const here - unethical, but saves us a copy ((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash(); } RETHROW(); } END_CATCH if ( fRemoveBackSlash ) { // override const here - unethical, but saves us a copy ((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash(); } } //Update //+--------------------------------------------------------------------------- // // Function: IsBackupLikeNotification // // Synopsis: Tests if the change notification is like backup resetting the // archive bit. // // Arguments: [ulAttribNew] - The current file attributes // [ulAttribOld] - Previous file attributes that are stored // // Returns: TRUE if the change notification is due to backup archiving // the file. // FALSE o/w // // History: 5-13-96 srikants Created // // Notes: If the notification is a result of BACKUP turning off the // archive bit: // 1. The archive bit is turned OFF // 2. The attribute mask MAY have the FILE_ATTRIBUTE_NORMAL // turned on. // //---------------------------------------------------------------------------- inline BOOL IsBackupLikeNotification( ULONG ulAttribNew, ULONG ulAttribOld ) { Win4Assert( (ulAttribNew & FILE_ATTRIBUTE_ARCHIVE) == 0 ); // // See if the only difference between the old and the new attributes // is in the FILE_ATTRIBUTE_NORMAL and FILE_ATTRIBUTE_ARCHIVE. // return ( (ulAttribNew ^ ulAttribOld) & (~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ) == 0; } //IsBackupLikeNotification //+--------------------------------------------------------------------------- // // Member: CiCat::IsIgnoreNotification // // Synopsis: Tests if the current notification can be ignored // // Arguments: [wid] - Workid // [funnyPath] - Full Path of the file // [ulFileAttrib] - Current file attributes of the file // // Returns: TRUE if the notification can be ignored. // FALSE if not. // // History: 5-17-96 srikants Created // // Notes: Programs like touch.exe don't update any attributes but just // update the last write time. In addition to checking the // archive bit, we also have to check the last write time. // //---------------------------------------------------------------------------- BOOL CiCat::IsIgnoreNotification( WORKID wid, const CFunnyPath & funnyPath, ULONG ulFileAttrib ) { BOOL fIgnore = TRUE; // // See if the only difference with the existing attributes is // archive bit. In that case, we don't have to refilter the doc. // XPrimaryRecord rec( _propstoremgr, wid ); PROPVARIANT propVar; if ( !_propstoremgr.ReadProperty( rec.GetReference(), pidAttrib, propVar ) ) return FALSE; ULONG ulOldAttrib = propVar.ulVal; if ( !IsBackupLikeNotification(ulFileAttrib, ulOldAttrib) ) return FALSE; // // Read the last "seen" time from the property store. // if ( ( !_propstoremgr.ReadProperty( rec.GetReference(), pidLastSeenTime, propVar ) ) || ( VT_EMPTY == propVar.vt ) || ( 0 == propVar.hVal.QuadPart ) ) return FALSE; // // Retrieve the last write time from the file and then compare. // WIN32_FIND_DATA ffData; if ( !GetFileAttributesEx( funnyPath.GetPath(), GetFileExInfoStandard, &ffData ) ) return FALSE; return CompareFileTime( &ffData.ftLastWriteTime, (FILETIME *) &propVar.hVal ) <= 0; } //IsIgnoreNotification //+--------------------------------------------------------------------------- // // Member: CiCat::Update // // Synopsis: Notifies content index about the change to the given document. // // Arguments: [lcaseFunnyPath] - Path of the document that changed. // [fDeleted] - Set to TRUE if the document is deleted. // [ftLastSeen] - Last seen time // [ulFileAttrib] - File attributes to be written to prop store // // History: 1-18-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::Update( const CLowerFunnyPath & lcaseFunnyPath, BOOL fDeleted, FILETIME const &ftLastSeen, ULONG ulFileAttrib ) { // // Don't filter catalog files // if ( ! IsEligibleForFiltering( lcaseFunnyPath ) ) return; CImpersonateSystem impersonate; // override const here - unethical, but saves us a copy BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash(); TRY { WORKID wid; USN usn; ULONG action; BOOL fNew; { CLock lock( _mutex ); if ( IsShuttingDown() ) THROW( CException( STATUS_TOO_LATE ) ); Win4Assert( 0 != _xCiManager.GetPointer() ); Win4Assert( !IsOnUsnVolume( lcaseFunnyPath.GetActualPath() ) ); wid = _strings.LokFind( lcaseFunnyPath.GetActualPath() ); if ( fDeleted && widInvalid == wid ) { // // This file is not yet known to us. Nothing to do. // return; } usn = 1; action = CI_UPDATE_OBJ; fNew = FALSE; if ( fDeleted ) { usn = 0; action = CI_DELETE_OBJ; _strings.LokDelete( lcaseFunnyPath.GetActualPath(), wid ); } else if ( widInvalid == wid ) { fNew = TRUE; wid = _strings.LokAdd( lcaseFunnyPath.GetActualPath(), fileIdInvalid, FALSE, widInvalid, ulFileAttrib, & ftLastSeen ); } } Win4Assert( widInvalid != wid ); // // BACKUP program just turns off the archive bit. We don't want to // filter in that case. // BOOL fIgnore = FALSE; if ( !fNew && !fDeleted && !(ulFileAttrib & FILE_ATTRIBUTE_ARCHIVE) ) { fIgnore = IsIgnoreNotification( wid, lcaseFunnyPath, ulFileAttrib ); #if CIDBG==1 if ( fIgnore ) { ciDebugOut(( DEB_ITRACE, "Ignoring Archive Bit modification for (%ws)\n", lcaseFunnyPath.GetActualPath() )); } #endif // CIDBG==1 } SCODE sc = S_OK; if ( !fIgnore ) sc = Update( wid, GetPartition(), CI_VOLID_USN_NOT_ENABLED, usn, action ); if ( !fNew && !fDeleted && SUCCEEDED(sc) ) { CStorageVariant var( ftLastSeen ); XWritePrimaryRecord rec( _propstoremgr, wid ); sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(), pidLastSeenTime, var ); if (FAILED(sc)) THROW(CException(sc)); Win4Assert( ulFileAttrib != 0xFFFFFFFF ); PROPVARIANT propVar; propVar.vt = VT_UI4; propVar.ulVal = ulFileAttrib; sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(), pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); } } CATCH( CException, e ) { if ( fRemoveBackSlash ) { // override const here - unethical, but saves us a copy ((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash(); } RETHROW(); } END_CATCH if ( fRemoveBackSlash ) { // override const here - unethical, but saves us a copy ((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash(); } } //Update //+--------------------------------------------------------------------------- // // Member: CiCat::AddDocuments // // Synopsis: Adds the given documents to the content index. // // Arguments: [docList] - List of documents. // // History: 3-19-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::AddDocuments( CDocList const & docList ) { Win4Assert( 0 != _xCiManager.GetPointer() ); FILETIME ftLastSeen; GetSystemTimeAsFileTime( &ftLastSeen ); CStorageVariant var( ftLastSeen ); for ( unsigned i = 0; i < docList.Count(); i++ ) { CDocumentUpdateInfo info( docList.Wid(i), docList.VolumeId(i), docList.Usn(i), FALSE ); SCODE sc = _xCiManager->UpdateDocument( &info ); // NTRAID#DB-NTBUG9-83752-2000/07/31-dlee cicat::AddDocuments ignores return code from CIManager::UpdateDocument if (SUCCEEDED(sc)) { sc = _propstoremgr.WritePrimaryProperty( docList.Wid(i), pidLastSeenTime, var ); if (FAILED(sc)) THROW(CException(sc)); } } } //AddDocuments //+--------------------------------------------------------------------------- // // Member: CiCat::CatalogState, public // // Synopsis: Compute and return catalog state // // Arguments: [cDocuments] -- Total number of documents in catalog // [cPendingScans] -- Count of pending scans // [fState] -- In-progress actions // // History: 04-18-96 KyleP Created // 05-14-96 KyleP Added pending scans // //---------------------------------------------------------------------------- void CiCat::CatalogState( ULONG & cDocuments, ULONG & cPendingScans, ULONG & fState ) { if ( IsShuttingDown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::CatalogState - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } if ( IsStarted() ) { cDocuments = _propstoremgr.CountRecordsInUse(); if ( IsReadOnly() ) { fState = CI_STATE_READ_ONLY; cPendingScans = 0; } else { ULONG ulInProgressScans, ulPendingScans; _scanMgr.Count( ulInProgressScans, ulPendingScans ); ULONG ulInProgressUsnScans, ulPendingUsnScans; _usnMgr.Count( ulInProgressUsnScans, ulPendingUsnScans ); cPendingScans = ulInProgressScans + ulPendingScans + ulInProgressUsnScans + ulPendingUsnScans; if ( ulInProgressScans > 0 || ulInProgressUsnScans > 0 ) fState = CI_STATE_SCANNING; else fState = 0; if ( !_fRecoveryCompleted ) fState |= CI_STATE_RECOVERING; } } else { cDocuments = 0; cPendingScans = 0; fState = 0; } } //CatalogState //+------------------------------------------------------------------------- // // Member: CiCat::EnumerateProperty, public // // Synopsis: Iterates all properties // // Arguments: [ps] -- Property returned here // [cbInCache] -- Number of bytes / record allocated for this property // [type] -- Property type. // [storeLevel]-- Property store level. // [fIsModifiable] -- Can the meta data for this prop be modified? // [iBmk] -- Bookmark for iteration. Begin at 0. // // Returns: TRUE if another property was returned. // // History: 11-Feb-96 KyleP Created // //-------------------------------------------------------------------------- BOOL CiCat::EnumerateProperty( CFullPropSpec & ps, unsigned & cbInCache, ULONG & type, DWORD & dwStoreLevel, BOOL & fIsModifiable, unsigned & iBmk ) { if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::EnumerateProperty - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); // // Cheat a little here. I probably should call CQCat... // BOOL fOk; // // Note that the 'special' property sets (query\rank, etc.) other // than storage are excluded. They can never be in the property cache. // if ( iBmk <= PID_STG_MAX - 2 ) { static const GUID guidStorage = PSGUID_STORAGE; ps.SetPropSet( guidStorage ); ps.SetProperty( iBmk + 2 ); iBmk++; fOk = TRUE; } else { iBmk -= (PID_STG_MAX - 2 + 1); // Want 0-based count. fOk = _PidTable.EnumerateProperty( ps, iBmk ); iBmk += (PID_STG_MAX - 2 + 1); // Back to global count. } if ( fOk ) { PROPID pid = PropertyToPropId( ps, FALSE ); if ( pidInvalid == pid ) return FALSE; cbInCache = _propstoremgr.Size( pid ); type = _propstoremgr.Type( pid ); dwStoreLevel = _propstoremgr.StoreLevel( pid ); fIsModifiable = _propstoremgr.CanBeModified( pid ); } return fOk; } //EnumerateProperty //+------------------------------------------------------------------------- // // Member: CiCat::BeginCacheTransaction, public // // Synopsis: Opens a cache transaction. Aborts any existing open transaction. // // Returns: Token of new transaction. // // History: 20-Jun-96 KyleP Created // //-------------------------------------------------------------------------- ULONG_PTR CiCat::BeginCacheTransaction() { // // Don't let anyone in until the long initialization is completed. // if ( !_fInitialized ) _evtInitialized.Wait(); if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::BeginCacheTransaction - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CImpersonateSystem impersonate; CLock lockAdmin( _mtxAdmin ); return _propstoremgr.BeginTransaction(); } //BeginCacheTransaction //+------------------------------------------------------------------------- // // Member: CiCat::SetupCache, public // // Synopsis: Add/Modify/Remove property from cache. // // Arguments: [ps] -- Property spec // [vt] -- Data type of value // [cbMaxLen] -- Soft-maximum length of value. 0 --> delete // [ulToken] -- Token of active transaction // // History: 17-Jan-96 KyleP Created // //-------------------------------------------------------------------------- void CiCat::SetupCache( CFullPropSpec const & ps, ULONG vt, ULONG cbMaxLen, ULONG_PTR ulToken, BOOL fCanBeModified, DWORD dwStoreLevel ) { // // Don't let anyone in until the long initialization is completed. // if ( !_fInitialized ) _evtInitialized.Wait(); if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::SetupCache - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CLock lockAdmin( _mtxAdmin ); PROPID pid = PropertyToPropId( ps, TRUE ); if ( pid != pidPath && pid != pidLastSeenTime && pid != pidAttrib && pid != pidVirtualPath && pid != pidSecurity && pid != pidParentWorkId && pid != pidSecondaryStorage && pid != pidFileIndex && pid != pidVolumeId && pid != pidSize && pid != pidWriteTime ) { CImpersonateSystem impersonate; _propstoremgr.Setup( pid, vt, cbMaxLen, ulToken, fCanBeModified, dwStoreLevel ); if ( 0 == cbMaxLen ) // Delete DeleteUserProperty( ps ); } else { ciDebugOut(( DEB_WARN, "Attempt to modify caching of required property ignored!\n" )); //THROW( CException( STATUS_INVALID_PARAMETER ) ); } } //SetupCache //+------------------------------------------------------------------------- // // Member: CiCat::EndCacheTransaction, public // // Synopsis: Closes a cache transaction. // // Arguments: [ulToken] -- Token of transaction // [fCommit] -- TRUE --> Commit changes // // History: 20-Jun-96 KyleP Created // //-------------------------------------------------------------------------- void CiCat::EndCacheTransaction( ULONG_PTR ulToken, BOOL fCommit ) { // // Don't let anyone in until the long initialization is completed. // if ( !_fInitialized ) _evtInitialized.Wait(); if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::EndCacheTransaction - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); CImpersonateSystem impersonate; CLock lockAdmin( _mtxAdmin ); _propstoremgr.EndTransaction( ulToken, fCommit, pidSecurity, pidVirtualPath ); } //EndCacheTransaction //+------------------------------------------------------------------------- // // Member: CiCat::StoreValue, public // // Synopsis: Stores value in property cache // // Arguments: [wid] -- Workid // [ps] -- Property spec // [var] -- Value to store // // Returns: TRUE if the property is in the property store schema // FALSE if the property is not in the property store schema // // Notes: Throws on exceptions. // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- BOOL CiCat::StoreValue( WORKID wid, CFullPropSpec const & ps, CStorageVariant const & var ) { if ( !IsStarted() && !IsShuttingDown() ) return FALSE; PROPID pid = PropertyToPropId( ps, FALSE ); if ( pid != pidInvalid && wid != widInvalid ) { // // HACK #721: On NTFS volumes, store LastChange time as LastSeen time. // We don't use LastSeen for NTFS, and we need LastChange. // This trick saves 1 DWORD per primary record. // // Note that only NTFS volumes will ever send a LastChange // time. // if ( pidChangeTime == pid ) pid = pidLastSeenTime; SCODE sc = _propstoremgr.WriteProperty( wid, pid, var ); if (FAILED(sc)) HandleError( sc ); if ( S_OK != sc ) { if ( pidSize == pid ) { ciDebugOut(( DEB_FORCE, "cicat: can't store pidSize: %#x\n", sc )); } } if ( FAILED( sc ) ) THROW( CException( sc ) ); return (S_OK == sc); } return FALSE; } //+------------------------------------------------------------------------- // // Member: CiCat::FetchValue, public // // Synopsis: Retrieves value from property cache // // Arguments: [wid] -- Workid // [pid] -- Property id // [pbData] -- Value returned here // [pcb] -- On input, size in bytes of [pbData]. On // output, size needed to store value. // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- BOOL CiCat::FetchValue( WORKID wid, PROPID pid, PROPVARIANT * pbData, unsigned * pcb ) { if ( !IsStarted() && !IsShuttingDown() ) return FALSE; else return _propstoremgr.ReadProperty( wid, pid, pbData, pcb ); } //+------------------------------------------------------------------------- // // Member: CiCat::FetchValue, public // // Synopsis: Retrieves value from property cache. Uses CoTaskMemAlloc // // Arguments: [wid] -- Workid // [pid] -- Property id // [var] -- Value returned here // // History: 10-Jan-96 KyleP Added header // //-------------------------------------------------------------------------- BOOL CiCat::FetchValue( WORKID wid, CFullPropSpec const & ps, PROPVARIANT & var ) { if ( !IsStarted() && !IsShuttingDown() ) return FALSE; else { PROPID pid = PropertyToPropId( ps, FALSE ); if ( pidInvalid != pid ) return _propstoremgr.ReadProperty( wid, pid, var ); else return FALSE; } } //+------------------------------------------------------------------------- // // Member: CiCat::FetchValue, public // // Synopsis: Retrieves value from property cache. Uses pre-existing // property record. // // Arguments: [pRec] -- Property record // [pid] -- Property id // [pbData] -- Value returned here // [pcb] -- On input, size in bytes of [pbData]. On // output, size needed to store value. // // Returns: TRUE if property exists (and was returned). // // History: 03-Apr-96 KyleP Created // //-------------------------------------------------------------------------- BOOL CiCat::FetchValue( CCompositePropRecord * pRec, PROPID pid, PROPVARIANT * pbData, unsigned * pcb ) { if ( (!IsStarted() && !IsShuttingDown()) || 0 == pRec ) return FALSE; else return _propstoremgr.ReadProperty( *pRec, pid, pbData, pcb ); } //+------------------------------------------------------------------------- // // Member: CiCat::FetchValue, public // // Synopsis: Retrieves value from property cache. Uses pre-existing // property record. // // Arguments: [Rec] -- Property record // [pid] -- Property id // [pbData] -- Value returned here // [pbExtra] -- Where to put data that won't fit in pbData // [pcbExtra]-- On input, size in bytes of [pbExtra]. On // output, size needed to store value. // // Returns: TRUE if property exists (and was returned). // // History: 02-Feb-98 dlee Created // //-------------------------------------------------------------------------- BOOL CiCat::FetchValue( CCompositePropRecord & Rec, PROPID pid, PROPVARIANT * pvData, BYTE * pbExtra, unsigned * pcbExtra ) { if ( (!IsStarted() && !IsShuttingDown()) || 0 == &Rec ) return FALSE; else return _propstoremgr.ReadProperty( Rec, pid, *pvData, pbExtra, pcbExtra ); } //+------------------------------------------------------------------------- // // Member: CiCat::FetchValue, public // // Synopsis: Retrieves value from property cache. Uses pre-existing // property record, and allocations are done with CoTaskMem // // Arguments: [pRec] -- Property record // [pid] -- Property id // [var] -- Value returned here // // Returns: TRUE if property exists (and was returned). // // History: 19-Dec-97 dlee Created // //-------------------------------------------------------------------------- BOOL CiCat::FetchValue( CCompositePropRecord * pRec, PROPID pid, PROPVARIANT & var ) { if ( (!IsStarted() && !IsShuttingDown()) || 0 == pRec ) return FALSE; else return _propstoremgr.ReadProperty( *pRec, pid, var ); } //+------------------------------------------------------------------------- // // Member: CiCat::OpenValueRecord, public // // Synopsis: Opens property record, which can be used for multiple // fetch operations. // // Arguments: [wid] -- WorkId of top-level record to open. // [pb] -- Storage for record // // Returns: Pointer to record, or zero if invalid. // // History: 03-Apr-96 KyleP Created // //-------------------------------------------------------------------------- CCompositePropRecord * CiCat::OpenValueRecord( WORKID wid, BYTE * pb ) { if ( !IsStarted() && !IsShuttingDown() ) return 0; else return _propstoremgr.OpenRecord( wid, pb ); } //+------------------------------------------------------------------------- // // Member: CiCat::CloseValueRecord, public // // Synopsis: Closes property record. // // Arguments: [pRec] -- Pointer to record. 0 is legal. // // History: 03-Apr-96 KyleP Created // //-------------------------------------------------------------------------- void CiCat::CloseValueRecord( CCompositePropRecord * pRec ) { if ( 0 != pRec ) _propstoremgr.CloseRecord( pRec ); } //+------------------------------------------------------------------------- // // Member: CiCat::StoreSecurity, public // // Synopsis: Determine SDID value and save in property cache. // // Arguments: [wid] -- Workid of file to store SDID for // [pSD] -- a pointer to the file's security descriptor // [cbSD] -- the size in bytes of the security descriptor // // History: 02 Feb 96 Alanw Created // //-------------------------------------------------------------------------- BOOL CiCat::StoreSecurity( WORKID wid, PSECURITY_DESCRIPTOR pSD, ULONG cbSD ) { Win4Assert ( IsStarted() || IsShuttingDown() ); PROPVARIANT var; var.vt = VT_UI4; if ( 0 != cbSD ) var.ulVal = _SecStore.LookupSDID( pSD, cbSD ); else var.ulVal = sdidNull; SCODE sc = _propstoremgr.WritePrimaryProperty( wid, pidSecurity, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) HandleError( sc ); return (S_OK == sc); } //StoreSecurity //+------------------------------------------------------------------------- // // Member: CiCat::FetchSDID, public // // Synopsis: Retrieve SDID value for a file // // Arguments: [pRec] -- Property record (may be NULL) // [wid] -- Workid of file for which to fetch SDID // // History: 02 Feb 96 Alanw Created // //-------------------------------------------------------------------------- SDID CiCat::FetchSDID( CCompositePropRecord * pRec, WORKID wid ) { PROPVARIANT var; unsigned cbVar = sizeof var; BOOL fOk; if ( 0 == pRec ) fOk = FetchValue( wid, pidSecurity, &var, &cbVar ); else fOk = FetchValue( pRec, pidSecurity, &var, &cbVar ); // if the property isn't in the schema, we don't care about security if ( !fOk ) return sdidNull; // if there isn't a value yet, assume no access if ( VT_UI4 != var.vt ) return sdidInvalid; return var.ulVal; } //+------------------------------------------------------------------------- // // Member: CiCat::AccessCheck, public // // Synopsis: Perform an access check given an SDID // // Arguments: [sdid] -- SDID of file to be checked // [hToken] -- token handle for client // [am] -- requested access // [fGranted] -- if successful check, whether access was granted // // Returns: BOOL - TRUE if the access check was successfully done. // // History: 02 Feb 96 Alanw Created // //-------------------------------------------------------------------------- BOOL CiCat::AccessCheck( SDID sdid, HANDLE hToken, ACCESS_MASK am, BOOL & fGranted ) { CImpersonateSystem impersonate; return _SecStore.AccessCheck( sdid, hToken, am, fGranted ); } //+--------------------------------------------------------------------------- // // Member: CiCat::PersistMaxUSNs, private // // Synopsis: Writes the maximum USN processed to the scope table, called // at shutdown. // // History: 11-18-98 dlee Created // //---------------------------------------------------------------------------- void CiCat::PersistMaxUSNs() { // // Do nothing for read-only catalogs and error cases. // if ( IsReadOnly() || eStarted != _state ) { ciDebugOut(( DEB_ITRACE, "not persisting max usn...\n" )); return; } // // Update the persistent scope table so the high-water USN is // persisted. This is needed if lots of usns have been processed // but no documents have been indexed, so ProcessChangesFlush hasn't // been called to persist the scope table. // ciDebugOut(( DEB_ITRACE, "flushinfolist count before %d\n", _usnFlushInfoList.Count() )); _usnMgr.GetMaxUSNs( _usnFlushInfoList ); ciDebugOut(( DEB_ITRACE, "flushinfolist count after %d\n", _usnFlushInfoList.Count() )); SerializeChangesInfo(); } //PersistMaxUsns //+--------------------------------------------------------------------------- // // Member: CiCat::ShutdownPhase1, public // // Synopsis: Dismounts the catalog by stopping all scan/update activity. // // History: 1-30-96 srikants Created // // Notes: In phase 1 of shutdown, all activity sent to the framework // is disabled. The framework can still call the property store. // //---------------------------------------------------------------------------- void CiCat::ShutdownPhase1() { ciDebugOut(( DEB_ITRACE, "ShutdownPhase1 %ws\n", _xName.Get() )); TRY { CImpersonateSystem impersonate; TRY { PersistMaxUSNs(); } CATCH( CException, e ) { // Ignore it if we can't persist the max usns // We'll just index more next time. } END_CATCH // =================================================== { CLock lock(_mutex); _state = eShutdownPhase1; } // =================================================== _evtInitialized.Set(); _evtPh2Init.Set(); _strings.Abort(); // // Initiate the shutdown for operations that may take a while. // _scanMgr.InitiateShutdown(); _usnMgr.InitiateShutdown(); _notify.InitiateShutdown(); _workMan.AbortWorkItems(); _scanMgr.WaitForShutdown(); _usnMgr.WaitForShutdown(); _notify.WaitForShutdown(); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CiCat::Shutdown failed with error 0x%X\n", e.GetErrorCode() )); } END_CATCH } //ShutdownPhase1 //+--------------------------------------------------------------------------- // // Member: CiCat::ShutdownPhase2, public // // Synopsis: Finishes catalog dismount // // History: 1-30-96 srikants Created // // Notes: Called after framework is stopped. // //---------------------------------------------------------------------------- void CiCat::ShutdownPhase2() { TRY { CImpersonateSystem impersonate; // =================================================== { CLock lock(_mutex); Win4Assert( eShutdownPhase1 == _state ); _state = eShutdown; } // =================================================== // Tell the property store we're in backedup mode and shutdown // cleanly. TRY { if ( !_propstoremgr.IsBackedUpMode() ) _propstoremgr.MarkBackedUpMode(); _propstoremgr.Shutdown(); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CiCat::ShutdownPhase2: nested try block failed with error 0x%X\n", e.GetErrorCode() )); } END_CATCH if ( _pStorage ) { _strings.Shutdown(); _fileIdMap.Shutdown(); delete _pStorage; _pStorage = 0; } _workMan.WaitForDeath(); CLock lock(_mutex); _xCiManager.Free(); _xAdviseStatus.Free(); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CiCat::ShutdownPhase2 failed with error 0x%X\n", e.GetErrorCode() )); } END_CATCH } //ShutdownPhase2 //+------------------------------------------------------------------------- // // Member: CiCat::~CiCat, public // // Synopsis: Destructor // // History: 10-Mar-92 BartoszM Created // //-------------------------------------------------------------------------- CiCat::~CiCat () { ciDebugOut(( DEB_WARN, "CiCat::~CiCat: %ws\n", _xName.GetPointer() )); if ( !IsShutdown() ) { ShutdownPhase1(); ShutdownPhase2(); } } //+------------------------------------------------------------------------- // // Member: CiCat::Touch, public // // Synopsis: Marks a file as seen (so it appears to the catalog to have // been recently saved). // // Arguments: [wid] - wid of file to be "touched" // [eType] - Seen array type // // History: 10-Mar-92 BartoszM Created // //-------------------------------------------------------------------------- void CiCat::Touch ( WORKID wid, ESeenArrayType eType ) { CLock lock(_mutex); Win4Assert ( IsInit() ); _strings.LokSetSeen ( wid, eType ); } //+------------------------------------------------------------------------- // // Member: CiCat::WorkIdToPath, public // // Synopsis: Returns a path for a document given its work id, as funny path // // Arguments: [wid] - the work id of the document // [funnyPath] - buffer to copy the path // // Returns: 0 if string not found ELSE // Count of total chars in funnyPath // // History: 21-May-98 VikasMan Created // //-------------------------------------------------------------------------- unsigned CiCat::WorkIdToPath ( WORKID wid, CFunnyPath& funnyPath ) { if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToPath - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } Win4Assert( wid != widInvalid ); unsigned cc = _strings.Find( wid, funnyPath ); #if CIDBG == 1 if ( cc > 0 ) ciDebugOut(( DEB_ITRACE, "wid %d (0x%x) --> %.*ws\n", wid, wid, cc, funnyPath.GetActualPath() )); #endif return cc; } //+------------------------------------------------------------------------- // // Member: CiCat::WorkIdToAccuratePath, public // // Synopsis: Returns a path for a document given its work id, as funny path. // This variant uses fileid --> path mappings instead of trusting // the property store. // // Arguments: [wid] - the work id of the document // [funnyPath] - buffer to copy the path // // Returns: 0 if string not found ELSE // Count of total chars in funnyPath // // History: 31-Dec-1998 KyleP Created // //-------------------------------------------------------------------------- unsigned CiCat::WorkIdToAccuratePath ( WORKID wid, CLowerFunnyPath& funnyPath ) { if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToPath - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } Win4Assert( wid != widInvalid ); // // First try to get the path based on file id and volume id. // This only works for files on USN volumes. // unsigned cc; VOLUMEID volumeId; FILEID fileId; CCompositePropRecord PropRec( wid, _propstoremgr ); if ( PropertyRecordToFileId( PropRec, fileId, volumeId ) ) { // PropertyRecordToFileId doesn't return a fileid without a volumeid Win4Assert( CI_VOLID_USN_NOT_ENABLED != volumeId ); cc = FileIdToPath( fileId, volumeId, funnyPath ); } else cc = _strings.Find( PropRec, funnyPath ); #if CIDBG == 1 if ( cc > 0 ) ciDebugOut(( DEB_ITRACE, "wid %d (0x%x) --> %.*ws\n", wid, wid, cc, funnyPath.GetActualPath() )); #endif return cc; } // WorkIdToAccuratePath //+--------------------------------------------------------------------------- // // Member: CiCat::MarkUnReachable // // Synopsis: Marks the given wid currently unreachable. // // Arguments: [wid] - WORKID // // History: 5-21-96 srikants Created // // Notes: We write MAXTIME as the last seen time and this will be used // as a special value during scanning. // //---------------------------------------------------------------------------- void CiCat::MarkUnReachable ( WORKID wid ) { if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::MarkUnReachable - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } Win4Assert( wid != widInvalid ); FILETIME ft; FillMaxTime(ft); CStorageVariant var( ft ); SCODE sc = _propstoremgr.WritePrimaryProperty( wid, pidLastSeenTime, var ); if (FAILED(sc)) THROW(CException(sc)); // // Set the SDID to avoid showing the file in case access was revoked. // var.SetUI4( sdidInvalid ); sc = _propstoremgr.WritePrimaryProperty( wid, pidSecurity, var ); if (FAILED(sc)) THROW(CException(sc)); } //+------------------------------------------------------------------------- // // Member: CiCat::PathToWorkId, public // // Synopsis: Returns a workid for a document given its path. // // Arguments: [lcaseFunnyPath] -- Fully qualified path // [fCreate] -- if TRUE, create workid for path if it // doesn't already have one. // [fNew] -- set to TRUE if a new workid is created // [pftLastSeen] -- If non zero pointer, will have the time of // the file last seen. If the last seen time is // not known, or this file is being seen for // the first time, the value will be set to zero. // [eType] -- Seen array type // [fileId] -- fileid if known or fileidInvalid // [widParent] -- Parent workid // [fGuaranteedNew] -- TRUE if the caller is under cicat lock // and has already checked that the file // is not known yet. // // Returns: Workid. // // History: 10-Jan-96 KyleP Added header // 06-Aug-97 EmilyB Added fCreate parameter // 10-Aug-97 EmilyB Updated to create wids for physical drive // roots (so they can appear in scope // hash table) // 26-Mar-98 KitmanH If the catalog is r/o, don't LokAdd even // though the file is not found on disk, just // return widInvalid // //---------------------------------------------------------------------------- WORKID CiCat::PathToWorkId ( const CLowerFunnyPath & lcaseFunnyPath, BOOL fCreate, BOOL & fNew, FILETIME * pftLastSeen, ESeenArrayType eType, FILEID fileId, WORKID widParent, BOOL fGuaranteedNew ) { if ( lcaseFunnyPath.IsRemote() ) { CImpersonateSystem impersonate; return PathToWorkIdInternal( lcaseFunnyPath, fCreate, fNew, pftLastSeen, eType, fileId, widParent, fGuaranteedNew ); } else { return PathToWorkIdInternal( lcaseFunnyPath, fCreate, fNew, pftLastSeen, eType, fileId, widParent, fGuaranteedNew ); } } WORKID CiCat::PathToWorkIdInternal( const CLowerFunnyPath & lcaseFunnyPath, BOOL fCreate, BOOL & fNew, FILETIME * pftLastSeen, ESeenArrayType eType, FILEID fileId, WORKID widParent, BOOL fGuaranteedNew ) { // // Don't let anyone in until the long initialization is completed. // if ( !_fInitialized ) _evtInitialized.Wait(); if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::PathToWorkId - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } if ( !IsEligibleForFiltering( lcaseFunnyPath ) ) return widInvalid; // override const here - unethical, but saves us a copy BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash(); #if CIDBG == 1 // // Check to see if the input path name contains an 8.3 short name // if (lcaseFunnyPath.IsShortPath( )) { ciDebugOut(( DEB_IWARN, "PathToWorkId: possible shortname path %ws\n", lcaseFunnyPath.GetActualPath() )); } #endif CIDBG TRY { // ========================================================= CLock lock ( _mutex ); BOOL fUsnVolume = VolumeSupportsUsns( lcaseFunnyPath.GetActualPath()[0] ); WORKID wid; if ( fUsnVolume ) { if ( fGuaranteedNew ) { Win4Assert( widInvalid == LokLookupWid( lcaseFunnyPath, fileId ) ); wid = widInvalid; } else { wid = LokLookupWid( lcaseFunnyPath, fileId ); } // If the path was deleted, give up now and return a bogus wid. if ( fileIdInvalid == fileId ) return widInvalid; } else { wid = _strings.LokFind( lcaseFunnyPath.GetActualPath() ); } ciDebugOut(( DEB_ITRACE, "pathtoworkid, fCreate %d, wid %#x, fileid %#I64x\n", fCreate, wid, fileId )); if ( wid != widInvalid ) { _strings.LokSetSeen( wid, eType ); if ( pftLastSeen ) fNew = !_strings.Find( wid, *pftLastSeen ); else fNew = FALSE; } else if ( fCreate ) { if ( !IsReadOnly() ) { wid = _strings.LokAdd( lcaseFunnyPath.GetActualPath(), fileId, fUsnVolume, widParent ); if ( fUsnVolume ) _fileIdMap.LokAdd( fileId, wid, MapPathToVolumeId( lcaseFunnyPath.GetActualPath() ) ); if ( pftLastSeen ) RtlZeroMemory( pftLastSeen, sizeof FILETIME ); fNew = TRUE; } else wid = widInvalid; } else { if ( pftLastSeen ) RtlZeroMemory( pftLastSeen, sizeof FILETIME ); fNew = FALSE; } if ( fRemoveBackSlash ) { // override const here - unethical, but saves us a copy ((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash(); } Win4Assert( fCreate || !fNew ); ciDebugOut(( DEB_ITRACE, "%ws --> wid %d (0x%x)\n", lcaseFunnyPath.GetActualPath(), wid, wid )); // ========================================================= return wid; } CATCH( CException, e ) { if ( fRemoveBackSlash ) { // override const here - unethical, but saves us a copy ((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash(); } RETHROW(); } END_CATCH // Make the compiler happy Win4Assert( !"unused codepath" ); return widInvalid; } //PathToWorkId //+------------------------------------------------------------------------- // // Member: CiCat::ComputeRelevantWords, public // // Synopsis: Computes and returns relevant words for a set of wids // // Arguments: [cRows] -- # of rows to compute and in pwid array // [cRW] -- # of rw per wid // [pwid] -- array of wids to work over // [partid] -- partition // // History: 10-May-94 v-dlee Created // //-------------------------------------------------------------------------- CRWStore * CiCat::ComputeRelevantWords(ULONG cRows,ULONG cRW, WORKID *pwid,PARTITIONID partid) { Win4Assert( !"Not supported in Framework" ); return 0; } //ComputeRelevantWords //+------------------------------------------------------------------------- // // Member: CiCat::RetrieveRelevantWords, public // // Synopsis: Retrieves relevant words already computed // // Arguments: [fAcquire] -- TRUE if ownership is transferred. // [partid] -- partition // // History: 10-May-94 v-dlee Created // //-------------------------------------------------------------------------- CRWStore * CiCat::RetrieveRelevantWords(BOOL fAcquire,PARTITIONID partid) { Win4Assert( !"Not supported in Framework" ); return 0; } //RetrieveRelevantWords //+--------------------------------------------------------------------------- // // Member: CiCat::PidMapToPidRemap, public // // Synopsis: Converts a pidMapperArray into a pidRemapper // // Arguments: [pidMap] -- a pid mapper to convert into a pid remapper // [pidRemap] -- the converted pid remapper; // // History: 01-Mar-95 DwightKr Created // //---------------------------------------------------------------------------- void CiCat::PidMapToPidRemap( const CPidMapper & pidMap, CPidRemapper & pidRemap ) { // // Rebuild the pidRemapper // pidRemap.ReBuild( pidMap ); } //+--------------------------------------------------------------------------- // // Member: CiCat::CiState, public // // Synopsis: Returns state of downlevel CI // // Arguments: [state] -- buffer to return state into // // History: 14-Dec-95 DwightKr Created // //---------------------------------------------------------------------------- #define setState( field, value ) \ if ( state.cbStruct >= ( offsetof( CI_STATE, field) + \ sizeof( state.field ) ) ) \ { \ state.field = ( value ); \ } SCODE CiCat::CiState( CI_STATE & state ) { // // NOTE - We are accessing _xCiManager without explicitly doing an // AddRef() because this call is made very frequently. Making a // Check about the "Started" state is sufficient - we are cheating // but it is a safe cheat. // SCODE sc = S_OK; if ( IsStarted() ) { CIF_STATE cifState; cifState.cbStruct = sizeof( CIF_STATE ); sc = _xCiManager->GetStatus( &cifState ); if ( S_OK == sc ) { // // Copy the status information from the CIF_STATE format to // CI_STATE format. // state.cbStruct = min( state.cbStruct, sizeof(CI_STATE) ); setState( cWordList, cifState.cWordList ); setState( cPersistentIndex, cifState.cPersistentIndex ); setState( cQueries, cifState.cQueries ); setState( cDocuments, cifState.cDocuments ); setState( cSecQDocuments, cifState.cSecQDocuments ); setState( cFreshTest, cifState.cFreshTest ); setState( dwMergeProgress, cifState.dwMergeProgress ); setState( cFilteredDocuments, cifState.cFilteredDocuments ); setState( dwIndexSize, cifState.dwIndexSize ); setState( cUniqueKeys, cifState.cUniqueKeys ); setState( dwPropCacheSize, _propstoremgr.GetTotalSizeInKB() ); ULONG cTotalDocuments, cPendingScans, stateFlags; CatalogState( cTotalDocuments, cPendingScans, stateFlags ); setState( cTotalDocuments, cTotalDocuments ); setState( cPendingScans, cPendingScans ); ULONG eCiState = stateFlags; if ( ! IsReadOnly() ) { if ( IsStarting() ) eCiState = CI_STATE_STARTING; else { if ( cifState.eState & CIF_STATE_SHADOW_MERGE ) eCiState |= CI_STATE_SHADOW_MERGE; if ( cifState.eState & CIF_STATE_MASTER_MERGE ) eCiState |= CI_STATE_MASTER_MERGE; if ( cifState.eState & CIF_STATE_CONTENT_SCAN_REQUIRED ) eCiState |= CI_STATE_CONTENT_SCAN_REQUIRED; if ( cifState.eState & CIF_STATE_ANNEALING_MERGE ) eCiState |= CI_STATE_ANNEALING_MERGE; if ( cifState.eState & CIF_STATE_INDEX_MIGRATION_MERGE ) eCiState |= CI_STATE_INDEX_MIGRATION_MERGE; if ( cifState.eState & CIF_STATE_MASTER_MERGE_PAUSED ) eCiState |= CI_STATE_MASTER_MERGE_PAUSED; if ( cifState.eState & CIF_STATE_HIGH_IO ) eCiState |= CI_STATE_HIGH_IO; if ( cifState.eState & CIF_STATE_LOW_MEMORY ) eCiState |= CI_STATE_LOW_MEMORY; if ( cifState.eState & CIF_STATE_BATTERY_POWER ) eCiState |= CI_STATE_BATTERY_POWER; if ( cifState.eState & CIF_STATE_USER_ACTIVE ) eCiState |= CI_STATE_USER_ACTIVE; if ( !_usnMgr.IsWaitingForUpdates() ) eCiState |= CI_STATE_READING_USNS; } } setState( eState, eCiState ); } } else if ( IsShuttingDown() ) sc = CI_E_SHUTDOWN; else sc = CI_E_NOT_INITIALIZED; return sc; } //CiState //+--------------------------------------------------------------------------- // // Member: CiCat::InitIf // // Synopsis: Initializes CiCat if not already initialized. // // History: 2-27-96 srikants Created // // Notes: To reduce lock contention, check to see if we're already // initialized without taking the lock. // //---------------------------------------------------------------------------- void CiCat::InitIf( BOOL fLeaveCorruptCatalog ) { if ( IsInit() ) { if ( !_statusMonitor.IsOk() ) THROW( CException( _statusMonitor.GetStatus() ) ); return; } CLock lock(_mutex); CImpersonateSystem impersonate; if ( !IsInit() && _statusMonitor.IsOk() && LokExists() ) { SCODE sc = S_OK; TRY { LokInit(); } CATCH( CException, e ) { sc = e.GetErrorCode(); ciDebugOut(( DEB_ERROR, "Error 0x%X while initializing catalog (%ws) \n", sc, _wcsCatDir )); } END_CATCH if ( S_OK != sc ) { // // Normal version changes will be detected much earlier. A // version change detected here is due to corruption. // if ( IsCiCorruptStatus(sc) || CI_INCORRECT_VERSION == sc ) { if ( fLeaveCorruptCatalog ) { Win4Assert( !"leaving corrupt catalog" ); THROW( CException( sc ) ); } // Win4Assert( !"FSCI(Catalog) Data Corruption" ); // // Log an event that we are doing automatic recovery. // _statusMonitor.LogEvent( CCiStatusMonitor::eCiRemoved ); { CImpersonateSystem impersonate; Win4Assert( 0 != _pStorage ); _pStorage->DeleteAllFsCiFiles(); } _statusMonitor.Reset(); LokInit(); sc = S_OK; } else { THROW( CException( sc ) ); } } Win4Assert( S_OK == sc ); _scanMgr.StartRecovery(); } else if ( !_statusMonitor.IsOk() ) { THROW( CException( _statusMonitor.GetStatus() ) ); } } //InitIf //+--------------------------------------------------------------------------- // // Member: CiCat::CreateIf // // Synopsis: Creates CiCat if not already created. // // History: 2-27-96 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CiCat::CreateIf() { CLock lock(_mutex); if ( !LokExists() ) LokCreate(); } //+--------------------------------------------------------------------------- // // Member: CiCat::InitOrCreate // // Synopsis: If there is no ContentIndex, one gets created and intialized // If there is a ContentIndex but it is not initialized, it will // be initialized. // // Otherwise, nothing is done. // // History: 1-21-96 srikants Moved from the UpdateDocuments method. // // Notes: // //---------------------------------------------------------------------------- void CiCat::InitOrCreate() { CImpersonateSystem impersonate; CreateIf(); InitIf(); } //+------------------------------------------------------------------------- // // Member: CiCat::UpdateDocuments, public // // Synopsis: Searches a directory and all its sub-directories for files // to be updated. // // Arguments: [wcsRootPath] - directory to start search from // [updFlag] - Flag indicating whether incremental or full // update. // // History: 27-Mar-92 AmyA Created // 16-Mar-98 KitmanH Don't rescan if the catalog is read-only // //-------------------------------------------------------------------------- void CiCat::UpdateDocuments ( WCHAR const * wcsRootPath, ULONG updFlag ) { if ( IsReadOnly() ) THROW( CException(STATUS_ACCESS_DENIED) ); Win4Assert( 0 != wcsRootPath ); InitOrCreate(); ciDebugOut (( DEB_ITRACE, "Updating the tree %ws\n", wcsRootPath )); ScanOrAddScope( wcsRootPath, FALSE, updFlag, TRUE, TRUE ); // do deletions } //+--------------------------------------------------------------------------- // // Member: CiCat::DoUpdate // // Synopsis: Synchronously scans the given scope and updates CI with // paths from the given scope. It is assumed that the // initialization is already done. // // Arguments: [pwcsPath] - Root of the scope to update from. // [updFlag] - Update flag specifying either incremental // or full. // [fDoDeletions] - Should deletion be done ? // [fAbort] - Set to TRUE to abort processing // [fProcessRoot] - In the case of a directory, should the root // be filtered ? // // History: 1-21-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::DoUpdate( WCHAR const * pwcPath, ULONG updFlag, BOOL fDoDeletions, BOOL & fAbort, BOOL fProcessRoot ) { Win4Assert( 0 != pwcPath ); Win4Assert( UPD_INCREM == updFlag || UPD_FULL == updFlag ); CLowerFunnyPath lcaseFunnyRootPath; lcaseFunnyRootPath.SetPath( pwcPath ); // make sure root is given a wid // NOTE: this is also done inside the CUpdate ctor WORKID widRoot = PathToWorkId( lcaseFunnyRootPath, TRUE ); CUpdate upd( *this, *(_xCiManager.GetPointer()), lcaseFunnyRootPath, GetPartition(), updFlag == UPD_INCREM, fDoDeletions, fAbort, fProcessRoot ); upd.EndProcessing(); } //+--------------------------------------------------------------------------- // // Member: CiCat::DoUpdate // // Synopsis: Scans all the specified scopes for updates // // Arguments: [scopes] - Queue of scopes to scan // [scanMgr] - a reference to the scan manager // [fAbort] - a reference to a flag that will be set to TRUE // if the scan should be aborted // // History: 1-23-96 srikants Created // // Notes: **** Does NOT THROW **** // //---------------------------------------------------------------------------- void CiCat::DoUpdate( CScanInfoList & scopes, CCiScanMgr & scanMgr, BOOL & fAbort ) { // // Performance OPTIMIZATION // Modify this later to scan the PropertyStore just once // for all the paths in the stack instead of once per path. // CImpersonateSystem impersonate; for ( CFwdScanInfoIter scanInfoIter(scopes); !scopes.AtEnd(scanInfoIter); scopes.Advance(scanInfoIter) ) { if ( IsShuttingDown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::DoUpdate - Shutdown initiated. Stopping scans\n" )); break; } CCiScanInfo * pScanInfo = scanInfoIter.GetEntry(); Win4Assert( pScanInfo->GetRetries() <= CCiScanInfo::MAX_RETRIES ); NTSTATUS status = STATUS_SUCCESS; TRY { if ( scanMgr.IsScopeDeleted( pScanInfo ) ) { RemovePathsFromCiCat( pScanInfo->GetPath(), eScansArray ); scanMgr.SetDone( pScanInfo ); } else if ( scanMgr.IsRenameDir( pScanInfo ) ) { CLowerFunnyPath lcaseFunnyOldDir, lcaseFunnyNewDir; lcaseFunnyOldDir.SetPath( pScanInfo->GetDirOldName() ); lcaseFunnyNewDir.SetPath( pScanInfo->GetPath() ); CRenameDir renameDir( *this, lcaseFunnyOldDir, lcaseFunnyNewDir, fAbort, CI_VOLID_USN_NOT_ENABLED ); if ( fAbort ) break; scanMgr.SetScanSuccess( pScanInfo ); } else { DoUpdate( pScanInfo->GetPath(), pScanInfo->GetFlags(), pScanInfo->IsDoDeletions(), fAbort, pScanInfo->GetProcessRoot() ); if ( fAbort ) break; scanMgr.SetScanSuccess( pScanInfo ); } } CATCH( CException, e) { ciDebugOut(( DEB_ERROR, "Exception 0x%x caught in Cicat::DoUpdate\n", e.GetErrorCode() )); scanMgr.SetStatus( pScanInfo, e.GetErrorCode() ); status = e.GetErrorCode(); } END_CATCH if ( STATUS_SUCCESS != status ) { HandleError( status ); if ( _statusMonitor.IsLowOnDisk() ) break; } } } //DoUpdate //+--------------------------------------------------------------------------- // // Member: CiCat::ScanOrAddScope // // Synopsis: Adds the specified scope to the ContentIndex, if one is not // already added. // // Arguments: [pwszScope] - Scope to be added to the ContentIndex // [fAdd] - If set to TRUE, the scope will be added if // not already present. // [updFlag] - Flag indicating whether incremental or full // update. // [fDoDeletions] - Set to TRUE if paths in the catalog that were // not found in the scope must be deleted from // CI (garbage collection). // [fScanIfNotNew] - If TRUE, the scope is scanned even if // it was already in the scope table. // [fCreateShadow] - TRUE if a shadow entry for this scope // should be created in the registry. // // History: 1-21-96 srikants Created // 9-16-98 kitmanh When a new scope is added on the fly, // register the volume where the scope is // for device notifications // //---------------------------------------------------------------------------- void CiCat::ScanOrAddScope( WCHAR const * pwszScope, BOOL fAdd, ULONG updFlag, BOOL fDoDeletions, BOOL fScanIfNotNew, BOOL fCreateShadow ) { Win4Assert( 0 != pwszScope ); InitOrCreate(); CLock lockAdmin( _mtxAdmin ); CLowerFunnyPath lcase( pwszScope ); if ( lcase.IsShortPath() ) { ciDebugOut(( DEB_WARN, "Converting %ws to long filename ", lcase.GetActualPath() )); if ( lcase.ConvertToLongName() ) { ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n", lcase.GetActualPath() )); } else { ciDebugOut(( DEB_WARN, "Couldn't convert short filename %ws.\n", lcase.GetActualPath() )); THROW( CException(STATUS_INVALID_PARAMETER) ); } } if ( lcase.GetActualLength() >= MAX_PATH ) { ciDebugOut(( DEB_ERROR, "ScanOrAddScope: Too big (%d) a path (%ws)\n", lcase.GetActualLength(), lcase.GetActualPath() )); CCiStatusMonitor::ReportPathTooLong( lcase.GetActualPath() ); THROW( CException(STATUS_INVALID_PARAMETER) ); } lcase.AppendBackSlash( ); if ( !IsEligibleForFiltering( lcase ) ) return ; _evtPh2Init.Wait(); if ( IsShuttingDown() ) return; BOOL fSupportsUsns = VolumeSupportsUsns( lcase.GetActualPath()[0] ); VOLUMEID volumeId; if ( fSupportsUsns ) volumeId = MapPathToVolumeId( lcase.GetActualPath() ); else volumeId = CI_VOLID_USN_NOT_ENABLED; BOOL fInScope = _notify.IsInScope( lcase.GetActualPath() ); if ( !fInScope && fAdd ) { // // Log event // CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_PROOT_ADDED, 1 ); item.AddArg( lcase.GetActualPath() ); eventLog.ReportEvent( item ); // // Register the volume where the scope is for Device notifcations // ciDebugOut(( DEB_ITRACE, "Register volume %wc for device notifications\n", pwszScope[0] )); if ( L'\\' != toupper(pwszScope[0]) ) _DrvNotifArray.AddDriveNotification( pwszScope[0] ); // // The given scope is not already in this ContentIndex. It must // be added to CI. // { CLock lock(_mutex); // add the path to the persistent list of scopes. _scopeTable.AddScope( volumeId, lcase.GetActualPath(), fCreateShadow ? _xScopesKey.Get() + SKIP_TO_LM: 0 ); } BOOL fSubScopesRemoved = FALSE; // // Enable notifications on this scope. Usn scopes are also added to // _notify, because the entries in _notify are serialized by the // scope table, but the notifications are not turned on for usn // scopes. // FILETIME ftLastScan; RtlZeroMemory( &ftLastScan, sizeof(FILETIME) ); XPtr xScopeInfo; if ( fSupportsUsns ) xScopeInfo.Set( new CScopeInfo( lcase.GetActualPath(), GetVolumeCreationTime( lcase.GetActualPath() ), GetVolumeSerialNumber( lcase.GetActualPath() ), volumeId, 0, GetJournalId( lcase.GetActualPath() ), TRUE ) ); else xScopeInfo.Set( new CScopeInfo( lcase.GetActualPath(), GetVolumeCreationTime( lcase.GetActualPath() ), GetVolumeSerialNumber( lcase.GetActualPath() ), ftLastScan ) ); _notify.AddPath( xScopeInfo.GetReference(), fSubScopesRemoved ); if ( fSubScopesRemoved ) { // // The scope table has some redundant paths (eg f:\foo, f:\bar) // and now f:\ has been added. We must get rid of f:\foo and f:\bar // CLock lock2( _mutex ); _scopeTable.RemoveSubScopes( lcase.GetActualPath(), _xScopesKey.Get() + SKIP_TO_LM ); } if ( fSupportsUsns ) { // // Inform usn manager to do a scan and start monitoring for // notifications on this scope // USN usnStart = 0; _usnMgr.AddScope( lcase.GetActualPath(), volumeId, FALSE, // No need for deletions for new scopes usnStart, FALSE, // not a full scan FALSE, // not user-initiated TRUE ); // new scope } else { // // trigger a background scan on the scope // _scanMgr.ScanScope( lcase.GetActualPath(), GetPartition(), UPD_FULL, FALSE, // don't do deletions FALSE, // not a delayed scan - immediate TRUE ); // new scope } } else if ( fInScope && fScanIfNotNew ) { // // Just force a re-scan with either usn manager or scan manager // if ( fSupportsUsns ) { USN usnStart = 0; BOOL fFull = ( updFlag == UPD_FULL ); InitUsnTreeScan( lcase.GetActualPath() ); _usnMgr.AddScope( lcase.GetActualPath(), volumeId, fDoDeletions, usnStart, FALSE, // not a full USN scan (which deletes all files first) fFull ); // user-initiated if a full scan } else { _scanMgr.ScanScope( lcase.GetActualPath(), GetPartition(), updFlag, fDoDeletions, FALSE ); // not a delayed scan - immediate scan } } } //ScanOrAddScope //+--------------------------------------------------------------------------- // // Member: CiCat::IsScopeInCI // // Arguments: [pwszScope] - Scope to be checked // // Returns: TRUE if [pwszScope] has already been added to content index. // // History: 2-19-96 KyleP Created // //---------------------------------------------------------------------------- BOOL CiCat::IsScopeInCI( WCHAR const * pwszScope ) { Win4Assert( 0 != pwszScope ); InitOrCreate(); _evtPh2Init.Wait(); if ( IsShuttingDown() ) { ciDebugOut(( DEB_ERROR, "IsScopeInCI - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } CLock lockAdmin( _mtxAdmin ); // // Terminate the path with a trailing backslash if not already terminated // with a backslash. // CLowerFunnyPath lcase( pwszScope ); if ( lcase.IsShortPath() ) { ciDebugOut(( DEB_WARN, "Converting %ws to long filename ", lcase.GetActualPath() )); if ( lcase.ConvertToLongName() ) { ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n", lcase.GetActualPath() )); } else { ciDebugOut(( DEB_WARN, "Couldn't convert short filename %ws.\n", lcase.GetActualPath() )); THROW( CException(STATUS_INVALID_PARAMETER) ); } } if ( lcase.GetActualLength() >= MAX_PATH ) { ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n", lcase.GetActualLength(), lcase.GetActualPath() )); CCiStatusMonitor::ReportPathTooLong( lcase.GetActualPath() ); THROW( CException(STATUS_INVALID_PARAMETER) ); } lcase.AppendBackSlash( ); return _notify.IsInScope( lcase.GetActualPath() ); } //IsScopeInCI //+--------------------------------------------------------------------------- // // Member: CiCat::RemoveScopeFromCI // // Synopsis: Removes scope permanently from ContentIndex // // Arguments: [wcsScopeToRemove] - the scope to remove // [fForceRemovalScan] - if true, the scope is scanned and files // in the scope are removed from the ci // // History: 1-25-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::RemoveScopeFromCI( WCHAR const * wcsScopeToRemove, BOOL fForceRemovalScan ) { CScopeEntry scopeToRemove( wcsScopeToRemove ); InitOrCreate(); _evtPh2Init.Wait(); if ( IsShuttingDown() ) return; CLock lockAdmin( _mtxAdmin ); // // If scope to remove contains special chars, find which scopes need to be // rescanned. // if ( !scopeToRemove.ContainsSpecialChar() ) { ciDebugOut(( DEB_ITRACE, "RemoveThisScope(%ws)\n", scopeToRemove.Get() )); RemoveThisScope( scopeToRemove.Get(), fForceRemovalScan ); } else { if ( !RemoveMatchingScopeTableEntries( wcsScopeToRemove ) ) { // nothing matched, must be file pattern or subdir while ( _scopesIgnored.RegExFind( scopeToRemove.Get() ) ) { if ( !scopeToRemove.SetToParentDirectory() ) { ciDebugOut(( DEB_ITRACE, "Reached Root: %ws, Rescanning \n", scopeToRemove.Get() )); break; } } ciDebugOut(( DEB_ITRACE, "ScanScopeTableEntry(%ws)\n",scopeToRemove.Get() )); ScanScopeTableEntry( scopeToRemove.Get() ); } } } //RemoveScopeFromCI //+------------------------------------------------------------------------- // // Member: CiCat::RemoveMatchingScopeTableEntries, private // // Synopsis: remove any scope table entries that matches input arg. // // Arguments: [pwszRegXScope] -- input scope to match against // // returns: TRUE if scope table entries & removed, FALSE otherwise. // // History: 4-12-98 mohamedn created // //-------------------------------------------------------------------------- BOOL CiCat::RemoveMatchingScopeTableEntries( WCHAR const * pwszRegXScope ) { CTimeLimit tl(0,0); CDFA cdfa( pwszRegXScope, tl, FALSE ); BOOL bRetVal = FALSE; unsigned iBmk = 0; WCHAR awcRoot[MAX_PATH+1]; while ( !IsShuttingDown() && _scopeTable.Enumerate( awcRoot, sizeof awcRoot / sizeof WCHAR, iBmk ) ) { if ( cdfa.Recognize(awcRoot) ) { ciDebugOut(( DEB_ITRACE, "RemoveThisScope(%ws)\n", awcRoot )); RemoveThisScope( awcRoot, TRUE ); bRetVal = TRUE; } } return bRetVal; } //+--------------------------------------------------------------------------- // // Member: CiCat::RemoveThisScope // // Synopsis: Removes scope permanently from ContentIndex // // Arguments: [wcsPath] - the scope to remove // [fForceRemovalScan] - if true, the scope is scanned and files // in the scope are removed from the ci // // History: 1-25-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::RemoveThisScope(WCHAR const * wcsPath, BOOL fForceRemovalScan ) { CLowerFunnyPath lcase( wcsPath ); if ( lcase.IsShortPath() ) { ciDebugOut(( DEB_WARN, "Converting %ws to long filename ", lcase.GetActualPath() )); if ( lcase.ConvertToLongName() ) { ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n", lcase.GetActualPath() )); } else { ciDebugOut(( DEB_WARN, "Couldn't convert short filename %ws.\n", lcase.GetActualPath() )); THROW( CException(STATUS_INVALID_PARAMETER) ); } } // // Log event // CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_PROOT_REMOVED, 1 ); item.AddArg( lcase.GetActualPath() ); eventLog.ReportEvent( item ); // ================================================= { CLock lock(_mutex); _scopeTable.RemoveScope( lcase.GetActualPath(), _xScopesKey.Get() + SKIP_TO_LM ); } // ================================================= // // Remove the scope from the notification manager to prevent further // scan triggers. // BOOL fInCatalog = TRUE; if ( !_notify.RemoveScope( lcase.GetActualPath() ) ) { fInCatalog = FALSE; ciDebugOut(( DEB_ERROR, "Path (%ws) not in catalog. Not deleted\n", lcase.GetActualPath() )); } // Inform the scan manager to stop any in-progress scans on this // scope, and remove any files in this scope from the catalog if ( fInCatalog || fForceRemovalScan ) { BOOL fSupportsUsns = VolumeSupportsUsns( lcase.GetActualPath()[0] ); if ( fSupportsUsns ) { // // Remove scope from usn manager // VOLUMEID volumeId = MapPathToVolumeId( lcase.GetActualPath() ); _usnMgr.RemoveScope( lcase.GetActualPath(), volumeId ); } else { // // Remove scope from scan manager // _scanMgr.RemoveScope( lcase.GetActualPath() ); } } } //RemoveThisScope //+--------------------------------------------------------------------------- // // Member: CiCat::AddVirtualScope, public // // Synopsis: Add virtual/physical mapping to index // // Arguments: [vroot] -- Virtual root // [root] -- Physical root // [fAutomatic] -- TRUE for paths tracking Gibraltar // [eType] -- Type of vroot // [fVRoot] -- TRUE if a virtual root, not just a vpath // [fIsIndexed] -- TRUE if indexed, FALSE otherwise // // Returns: TRUE if a change was made. // // History: 2-05-96 KyleP Created // //---------------------------------------------------------------------------- BOOL CiCat::AddVirtualScope( WCHAR const * vroot, WCHAR const * root, BOOL fAutomatic, CiVRootTypeEnum eType, BOOL fVRoot, BOOL fIsIndexed ) { CLowcaseBuf lcaseVRoot( vroot ); CLowerFunnyPath lcaseRoot( root ); if ( lcaseRoot.IsShortPath() ) { ciDebugOut(( DEB_WARN, "Converting %ws to long filename ", lcaseRoot.GetActualPath() )); if ( lcaseRoot.ConvertToLongName() ) { ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n", lcaseRoot.GetActualPath() )); } else { ciDebugOut(( DEB_ERROR, "Couldn't convert short filename %ws.\n", lcaseRoot.GetActualPath() )); THROW( CException(STATUS_INVALID_PARAMETER) ); } } if ( lcaseRoot.GetActualLength() >= MAX_PATH ) { ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n", lcaseRoot.GetActualLength(), lcaseRoot.GetActualPath() )); CCiStatusMonitor::ReportPathTooLong( root ); THROW( CException(STATUS_INVALID_PARAMETER) ); } if ( lcaseVRoot.Length() >= MAX_PATH ) { ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n", lcaseVRoot.Length(), lcaseVRoot.Get() )); CCiStatusMonitor::ReportPathTooLong( vroot ); THROW( CException(STATUS_INVALID_PARAMETER) ); } if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); _evtPh2Init.Wait(); if ( IsShuttingDown() ) { ciDebugOut(( DEB_ERROR, "AddVirtualScope - Shutdown Initiated\n" )); THROW( CException(STATUS_TOO_LATE) ); } CLock lockAdmin( _mtxAdmin ); BOOL fAdded; { CLock lock ( _mutex ); fAdded = _strings.AddVirtualScope( lcaseVRoot.Get(), lcaseRoot.GetActualPath(), fAutomatic, eType, fVRoot, fIsIndexed ); } ciDebugOut(( DEB_ITRACE, "Virtual scope %.*ws %s list\n", lcaseVRoot.Length(), lcaseVRoot.Get(), fAdded ? "added to" : "already in" )); // // If the root was added to the vmap, add it to the scope table // if ( fVRoot) { if ( fIsIndexed && ( ( _fIndexW3Roots && ( W3VRoot == eType ) ) || ( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) || ( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) ) && !IsScopeInCI( root ) ) { AddScopeToCI( root, TRUE ); ciDebugOut(( DEB_ITRACE, "Physical scope %ws added to list\n", root )); } if ( !fIsIndexed ) RemoveVirtualScope( lcaseVRoot.Get(), FALSE, eType, fVRoot, fAdded ); } return fAdded; } //AddVirtualScope //+--------------------------------------------------------------------------- // // Function: AreVRootTypesEquiv // // Synopsis: Returns TRUE if the two enums are equivalent // // Arguments: [eType] -- Virtual root // [rootType] -- The other style of enum // // Returns: TRUE if they are the same, FALSE otherwise // // History: 6-20-97 dlee Created // //---------------------------------------------------------------------------- BOOL AreVRootTypesEquiv( CiVRootTypeEnum eType, ULONG rootType ) { if ( NNTPVRoot == eType && ( PCatalog::NNTPRoot == rootType ) ) return TRUE; if ( IMAPVRoot == eType && ( PCatalog::IMAPRoot == rootType ) ) return TRUE; if ( W3VRoot == eType && ( 0 == rootType ) ) return TRUE; return FALSE; } //+--------------------------------------------------------------------------- // // Member: CiCat::RemoveVirtualScope, public // // Synopsis: Remove virtual/physical mapping to index // // Arguments: [vroot] -- Virtual root // [fOnlyIfAutomatic] -- If TRUE, then a manual root will not // be removed // [eType] -- Type of vroot // [fVRoot] -- TRUE if a vroot, false if a vdir // [fForceVPathFixing] -- if TRUE, vpaths are fixed // // History: 2-05-96 KyleP Created // //---------------------------------------------------------------------------- BOOL CiCat::RemoveVirtualScope( WCHAR const * vroot, BOOL fOnlyIfAutomatic, CiVRootTypeEnum eType, BOOL fVRoot, BOOL fForceVPathFixing ) { CLowcaseBuf lcaseVRoot( vroot ); if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); _evtPh2Init.Wait(); if ( IsShuttingDown() ) { ciDebugOut(( DEB_ERROR, "RemoveVirtualScope - Shutdown Initiated\n" )); THROW( CException(STATUS_TOO_LATE) ); } if ( lcaseVRoot.Length() >= MAX_PATH ) { ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n", lcaseVRoot.Length(), lcaseVRoot.Get() )); CCiStatusMonitor::ReportPathTooLong( vroot ); THROW( CException( STATUS_INVALID_PARAMETER ) ); } CLock lockAdmin( _mtxAdmin ); BOOL fRemoved; // TRUE if virtual root was removed BOOL fDelete = FALSE; // TRUE if physic root should be deleted BOOL fChildrenToBeAdded = FALSE; // TRUE if deletion of super-scope requires // addition of sub-scopes. CLowerFunnyPath lcaseFunnyPRoot; unsigned cwcPRoot = 0; { CLock lock ( _mutex ); // // Remember the physical root of the virtual root we're // about to delete. // if ( ( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) || ( _fIndexW3Roots && ( W3VRoot == eType ) ) || ( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) ) { _strings.VirtualToPhysicalRoot( lcaseVRoot.Get(), lcaseVRoot.Length(), lcaseFunnyPRoot, cwcPRoot ); } fRemoved = _strings.RemoveVirtualScope( lcaseVRoot.Get(), fOnlyIfAutomatic, eType, fVRoot, fForceVPathFixing ); ciDebugOut(( DEB_ITRACE, "Virtual scope %.*ws %s list\n", lcaseVRoot.Length(), lcaseVRoot.Get(), fRemoved ? "removed from" : "not in" )); // no need to add/remove scopes from the CI if not a vroot if ( !fVRoot ) return fRemoved; // // If we're running in 'automatic' mode, then we will remove the physical root // when the last virtual reference is removed. // if ( fRemoved && ( ( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) || ( _fIndexW3Roots && ( W3VRoot == eType ) ) || ( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) ) ) { fDelete = TRUE; BOOL fDone = FALSE; unsigned iBmk = 0; while ( !fDone && fDelete ) { XGrowable xwcsVRoot; unsigned cwcVRoot; CLowerFunnyPath lcaseFunnyPRoot2; unsigned cwcPRoot2; ULONG Type = _strings.EnumerateVRoot( xwcsVRoot, cwcVRoot, lcaseFunnyPRoot2, cwcPRoot2, iBmk ); if ( Type == PCatalog::EndRoot ) fDone = TRUE; else if ( Type & PCatalog::UsedRoot ) { if ( cwcPRoot2 == cwcPRoot && RtlEqualMemory( lcaseFunnyPRoot.GetActualPath(), lcaseFunnyPRoot2.GetActualPath(), cwcPRoot * sizeof(WCHAR) ) ) { ciDebugOut(( DEB_ITRACE, "Keeping physical root %.*ws\n", cwcPRoot, lcaseFunnyPRoot.GetActualPath() )); fDelete = FALSE; } else if ( cwcPRoot2 > cwcPRoot && (lcaseFunnyPRoot2.GetActualPath())[cwcPRoot] == L'\\' && RtlEqualMemory( lcaseFunnyPRoot.GetActualPath(), lcaseFunnyPRoot2.GetActualPath(), cwcPRoot * sizeof(WCHAR) ) ) { fChildrenToBeAdded = TRUE; } } } } } // // Do the dirty deed... // if ( fDelete ) { Win4Assert(( ( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) || ( _fIndexW3Roots && ( W3VRoot == eType ) ) || ( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) )); ciDebugOut(( DEB_ITRACE, "Deleting physical root %ws\n", lcaseFunnyPRoot.GetActualPath() )); RemoveScopeFromCI( lcaseFunnyPRoot.GetActualPath(), FALSE ); if ( fChildrenToBeAdded ) { BOOL fDone = FALSE; unsigned iBmk = 0; while ( !fDone ) { XGrowable xwcsVRoot; unsigned cwcVRoot; ULONG Type = EnumerateVRoot( xwcsVRoot, cwcVRoot, lcaseFunnyPRoot, cwcPRoot, iBmk ); if ( Type == PCatalog::EndRoot ) fDone = TRUE; else if ( ( Type & PCatalog::UsedRoot ) && ( AreVRootTypesEquiv( eType, Type ) ) ) { if ( !IsScopeInCI( lcaseFunnyPRoot.GetActualPath() ) ) { AddScopeToCI( lcaseFunnyPRoot.GetActualPath(), TRUE ); ciDebugOut(( DEB_ITRACE, "Physical sub-scope %ws added to list\n", lcaseFunnyPRoot.GetActualPath() )); } } } } // // It is possible (though unlikely) that we accidentally deleted a scope // that was both a physical and virtual root. Re-sync physical roots to // re-add the physical root. // SynchWithRegistryScopes( FALSE ); } return fRemoved; } //RemoveVirtualScope //+------------------------------------------------------------------------- // // Member: CiCat::WorkIdToVirtualPath, public // // Synopsis: Returns a virtual path for a document given its work id. // // Arguments: [wid] -- The work id of the document // [cSkip] -- Number of matching virtual roots to skip. // [pwcBuf] -- buffer to copy the path // [cwc] -- size of buffer [in/out] // // History: 07-Feb-96 KyleP Created // //-------------------------------------------------------------------------- unsigned CiCat::WorkIdToVirtualPath( WORKID wid, unsigned cSkip, XGrowable & xBuf ) { if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToVirtualPath - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } Win4Assert( wid != widInvalid ); unsigned cwc ; if ( !IsStarted() ) cwc = 0; else cwc = _strings.FindVirtual( wid, cSkip, xBuf ); return cwc; } //+------------------------------------------------------------------------- // // Member: CiCat::WorkIdToVirtualPath, public // // Synopsis: Returns a virtual path for a document given its work id. // // Arguments: [propRec] -- The property record to use // [cSkip] -- Number of matching virtual roots to skip. // [pwcBuf] -- buffer to copy the path // [cwc] -- size of buffer [in/out] // // History: 07-Feb-96 KyleP Created // //-------------------------------------------------------------------------- unsigned CiCat::WorkIdToVirtualPath( CCompositePropRecord & propRec, unsigned cSkip, XGrowable & xBuf ) { if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToVirtualPath - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } unsigned cwc; if ( !IsStarted() ) cwc = 0; else cwc = _strings.FindVirtual( propRec, cSkip, xBuf ); return cwc; } //+------------------------------------------------------------------------- // // Member: CiCat::VirtualToPhysicalRoot, public // // Synopsis: Given a virtual path, returns a virtual root under that // virtual path, and the corresponding physical root. Will // not return overlapping virtual roots. // // Arguments: [pwcVPath] -- Virtual path // [ccVPath] -- Size in chars of [pwcVPath] // [xwcsVRoot] -- Virtual root // [ccVRoot] -- Returns count of chars in [xwcsVRoot] // [lcaseFunnyPRoot] -- Physical root // [ccPRoot] -- Returns count of actual chars (excluding // funny path) in [lcaseFunnyPRoot] // [iBmk] -- Bookmark for iteration. // // Returns: TRUE if match was found. // // History: 11-Feb-96 KyleP Created // //-------------------------------------------------------------------------- BOOL CiCat::VirtualToPhysicalRoot( WCHAR const * pwcVPath, unsigned ccVPath, XGrowable & xwcsVRoot, unsigned & ccVRoot, CLowerFunnyPath & lcaseFunnyPRoot, unsigned & ccPRoot, unsigned & iBmk ) { if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); return _strings.VirtualToPhysicalRoot( pwcVPath, ccVPath, xwcsVRoot, ccVRoot, lcaseFunnyPRoot, ccPRoot, iBmk ); } //+------------------------------------------------------------------------- // // Member: CiCat::EnumerateVRoot, public // // Synopsis: Iterates all virtual roots. // // Arguments: [xwcVRoot] -- Virtual root // [ccVRoot] -- Returns count of chars in [xwcVRoot] // [lcaseFunnyPRoot] -- Physical root // [ccPRoot] -- Returns count of actual chars (excluding // funny path) in [lcaseFunnyPRoot] // [iBmk] -- Bookmark for iteration. // // Returns: Type of root. PCatalog::EndRoot at end of iteration // // History: 11-Feb-96 KyleP Created // //-------------------------------------------------------------------------- ULONG CiCat::EnumerateVRoot( XGrowable & xwcVRoot, unsigned & ccVRoot, CLowerFunnyPath & lcaseFunnyPRoot, unsigned & ccPRoot, unsigned & iBmk ) { if ( !IsStarted() ) THROW( CException( STATUS_INVALID_PARAMETER ) ); if ( IsShutdown() ) { ciDebugOut(( DEB_ITRACE, "CiCat::EnumerateVRoot - Shutdown Initiated\n" )); THROW( CException( STATUS_TOO_LATE ) ); } return _strings.EnumerateVRoot( xwcVRoot, ccVRoot, lcaseFunnyPRoot, ccPRoot, iBmk ); } //+--------------------------------------------------------------------------- // // Member: CiCat::RemovePathsFromCiCat // // Synopsis: Removes all the paths below the specified root from the // property store. This method must be called ONLY from the // scan thread. // // Arguments: [pwszRoot] - Root of the scope to remove paths from. // [eType] - Seen array type // // History: 1-26-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::RemovePathsFromCiCat( WCHAR const * pwszRoot, ESeenArrayType eType ) { // NTRAID#DB-NTBUG9-83756-2000/07/31-dlee removing paths from cicat isn't restartable on crashes // // Remove it from the hash table and the propertyStore. // _strings.BeginSeen( pwszRoot, _mutex, eType ); EndUpdate( TRUE, eType ); // delete the paths. } //+--------------------------------------------------------------------------- // // Member: CiCat::SerializeChangesInfo // // Synopsis: Serializes last scan time and usn flushed info // // History: 20-Aug-97 SitaramR Created // //---------------------------------------------------------------------------- void CiCat::SerializeChangesInfo() { { CLock lock( _mutex ); _notify.UpdateLastScanTimes( _ftLastCLFlush, _usnFlushInfoList ); } // // Flush the scope table which will persist the last scan time and usn // flush info // _scopeTable.ProcessChangesFlush(); } //+--------------------------------------------------------------------------- // // Member: CiCat::ProcessScansComplete // // Synopsis: Called when there are no more scans outstanding to update // any persistent state information related to scans. // // Arguments: [fForce] - Set to TRUE if scope table must be updated // with the scan time even if there were no updates. // This is set when a "force scan" is completed. // [fShortWait] - [output] Set to TRUE if the scan thread must // do a wait for a shorter time than the usual 30 minutes. // This is set to TRUE when there is a low resource // situation and the scan thread must wake up // frequently to check if the situation has improved. // This is also set to TRUE when the initialization // is not done yet. // // History: 1-26-96 srikants Created // 1-27-97 srikants Added the fShortWait parameter // //---------------------------------------------------------------------------- void CiCat::ProcessScansComplete( BOOL fForce, BOOL & fShortWait ) { fShortWait = TRUE; if ( IsShuttingDown() || !_fInitialized ) return; SerializeChangesInfo(); // // Always flush the strings table and property store. // Property store may get updated without additional documents because // of filtering. // { CLock lock( _mutex ); _fileIdMap.LokFlush(); _strings.LokFlush(); } _propstoremgr.Flush(); _notify.ResetUpdateCount(); // // Book-keeping - Schedule any scans if necessary. // _scopeTable.ScheduleScansIfNeeded( _docStore ); // // See if any scans must be done on network paths with no notifications. // _notify.ForceNetPathScansIf(); // NTRAID#DB-NTBUG9-83758-2000/07/31-dlee If failed scans, don't record scans complete _scopeTable.RecordScansComplete(); fShortWait = _scopeTable.IsFullScanNeeded() || _scopeTable.IsIncrScanNeeded() || _statusMonitor.IsLowOnDisk(); // Scans are complete and we won't need that code for awhile if ( _regParams.GetMinimizeWorkingSet() ) SetProcessWorkingSetSize( GetCurrentProcess(), (SIZE_T) -1, (SIZE_T) -1 ); } //ProcessScansComplete //+--------------------------------------------------------------------------- // // Member: CiCat::ReScanPath // // Synopsis: Rescans the given scope. It is called when a notification // is lost for a scope. // // Arguments: [wcsPath] - The scope to be rescanned. It is assumed that // this path is already of interest to CI and so no checks // are made to verify that this is a path in CI. // // History: 1-22-96 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CiCat::ReScanPath( WCHAR const * wcsPath, BOOL fDelayed ) { _scanMgr.ScanScope( wcsPath, GetPartition(), UPD_INCREM, TRUE, // do deletions fDelayed // delayed scan ); } //+------------------------------------------------------------------------- // // Member: CiCat::StartUpdate, public // // Synopsis: Marks start of update // // Arguments: [time] - (out) the time of the last update of the catalog // [pwcsRoot] - Root // [fDoDeletions] - Should wids be marked as seen/unseen ? // [eType] - Seen array type // // History: 10-Mar-92 BartoszM Created // //-------------------------------------------------------------------------- void CiCat::StartUpdate ( FILETIME* time, WCHAR const * pwcsRoot, BOOL fDoDeletions, ESeenArrayType eType ) { if ( IsShuttingDown() ) { ciDebugOut(( DEB_ITRACE, "Shutting down in the middle of StartUpdate\n" )); THROW( CException(STATUS_TOO_LATE) ); } Win4Assert( IsStarted() ); if ( fDoDeletions ) _strings.BeginSeen( pwcsRoot, _mutex, eType ); _notify.GetLastScanTime( pwcsRoot, *time ); } //+------------------------------------------------------------------------- // // Member: CiCat::EndUpdate, public // // Synopsis: Marks the end of the update // // Arguments: [fDoDeletions] - if TRUE, unseen files will be deleted and // files not seen before will be added. // [eType] - Seen array type // // History: 10-Mar-92 BartoszM Created // //-------------------------------------------------------------------------- void CiCat::EndUpdate ( BOOL fDoDeletions, ESeenArrayType eType ) { USN usn = 0; if ( !fDoDeletions ) return; ciDebugOut (( DEB_CAT, "Files not seen:\n" )); int cDeletedTotal = 0; int cNewTotal = 0; const PARTITIONID partId = GetPartition(); CPropertyStoreWids iter( _propstoremgr ); for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( IsShuttingDown() ) { ciDebugOut(( DEB_WARN, "Shutting down in the middle of EndUpdate\n" )); THROW( CException(STATUS_TOO_LATE) ); } // ==================== lock =================== CLock lock( _mutex ); if ( _strings.LokNotSeen( wid, eType ) ) { # if CIDBG == 1 XGrowable awc; _strings.Find( wid, awc ); ciDebugOut (( DEB_CAT, "\tdel: %u (0x%x) %ws\n", wid, wid, awc.Get() )); # endif // Files from USN volumes can only be deleted if the scan was // from a USN volume -- only do this work if appropriate. if ( eUsnsArray == eType ) { // // If the wid is from an ntfs 5.0 volume, then remove it // from the file id map. // PROPVARIANT propVar; BOOL fFound = _propstoremgr.ReadPrimaryProperty( wid, pidFileIndex, propVar ); FILEID fileId = propVar.uhVal.QuadPart; if ( fileIdInvalid != fileId ) { // // File can be added to propstore, then deleted before // the file can be filtered, which means that it's // possible for a file to be in propstore but not in // fileid map. // fFound = _propstoremgr.ReadPrimaryProperty( wid, pidVolumeId, propVar ); Win4Assert( fFound ); WORKID widExisting = _fileIdMap.LokFind( fileId, propVar.ulVal ); if ( widExisting != widInvalid && wid == widExisting ) { // // If wid != widExisting then it means that // LokWriteFileUsnInfo changed the fileid mapping to // a new wid, i.e. fileid now maps to widExisting, // and so the new fileid mapping shouldn't be deleted. // _fileIdMap.LokDelete( fileId, wid ); } } } // // Delete the wid from strings table and property store // BOOL fUsnVolume = ( eUsnsArray == eType ); _strings.LokDelete( 0, wid, fUsnVolume, fUsnVolume ); // // The use of CI_VOLID_USN_NOT_ENABLED below is okay because the // usn is 0 and so the real volume id does not matter. In // CheckPointChangesFlushed, usns with a value of 0 are ignored. // If the real volume id is needed then it needs to be stored // in the property store, which is a big overhead. // CDocumentUpdateInfo info( wid, CI_VOLID_USN_NOT_ENABLED, 0, TRUE ); _xCiManager->UpdateDocument( &info ); cDeletedTotal++; } else if ( _strings.LokSeenNew(wid, eType) ) { # if CIDBG == 1 XGrowable awc; _strings.Find( wid, awc ); ciDebugOut (( DEB_CAT, "\tnew: %u (0x%x) %ws\n", wid, wid, awc.Get() )); # endif CDocumentUpdateInfo info( wid, CI_VOLID_USN_NOT_ENABLED, 0, FALSE ); _xCiManager->UpdateDocument( &info ); cNewTotal++; } // ==================== end lock =================== } { CLock lock( _mutex ); _strings.LokEndSeen( eType ); } ciDebugOut (( DEB_ITRACE, "%d new documents, %d deleted documents\n", cNewTotal, cDeletedTotal )); } //EndUpdate //+--------------------------------------------------------------------------- // // Member: CiCat::HandleError // // Synopsis: Checks the status code passed and if it indicates corruption, // the index will be marked corrupt in-memory as well as on // disk. The recovery will happen on a subsequent restart. // // Arguments: [status] - The status code to check for corruption. // // History: 3-21-96 srikants Created // 3-20-98 kitmanh Don't Mark catalog corrupt if the catalog // is read-only // // Notes: MUST NOT THROW // //---------------------------------------------------------------------------- void CiCat::HandleError( NTSTATUS status ) { TRY { if ( IsCiCorruptStatus(status) && !IsReadOnly() ) { CLock lock(_mutex); // // If we are initialized and not marked that we are corrupt yet, // mark CI corrupt. // if ( IsInit() ) { // // Mark that we are corrupt persistently. // if ( CI_CORRUPT_DATABASE == status ) { if ( !_scopeTable.IsCiDataCorrupt() ) _scopeTable.MarkCiDataCorrupt(); } else { Win4Assert( CI_CORRUPT_CATALOG == status ); if ( !_scopeTable.IsFsCiDataCorrupt() ) _scopeTable.MarkFsCiDataCorrupt(); } if ( !_statusMonitor.IsCorrupt() ) { _statusMonitor.ReportFailure( status ); _statusMonitor.SetStatus( status ); } } } else if ( CI_PROPSTORE_INCONSISTENCY == status ) { CLock lock(_mutex); _statusMonitor.ReportPropStoreError(); _statusMonitor.SetStatus( status ); } else if ( IsDiskLowError(status) ) { NoLokProcessDiskFull(); } } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error (0x%X) in CiCat::HandleError\n", e.GetErrorCode() )); } END_CATCH } //HandleError //+--------------------------------------------------------------------------- // // Member: CiCat::MarkFullScanNeeded // // Synopsis: Marks that a full scan is needed. // // History: 1-27-97 srikants Created // //---------------------------------------------------------------------------- void CiCat::MarkFullScanNeeded() { _scopeTable.RecordFullScanNeeded(); } //+--------------------------------------------------------------------------- // // Member: CiCat::MarkIncrScanNeeded // // Synopsis: Marks that an incremental scan is needed for all the scopes. // // History: 1-27-97 srikants Created // //---------------------------------------------------------------------------- void CiCat::MarkIncrScanNeeded() { _scopeTable.RecordIncrScanNeeded( FALSE ); } //+--------------------------------------------------------------------------- // // Member: CiCat::ProcessChangesFlush // // Arguments: [ft] - Time stamp of the last successful flush. // [cEntries ] - Number of entries in the pUsnEntries. // [ppUsnEntries] - Array of USN entries // // Synopsis: Records the time of the last successful changelog flush, and // make a copy of usn flush info. // // History: 1-28-97 srikants Created // 05-07-97 SitaramR Usns // //---------------------------------------------------------------------------- void CiCat::ProcessChangesFlush( FILETIME const & ft, ULONG cEntries, USN_FLUSH_INFO const * const * ppUsnEntries ) { ciDebugOut(( DEB_ITRACE, "ProcessChangesFlush\n" )); // // We shouldn't flush until Recovery has been performed. // Wait for the recovery to be complete before obtaining // a lock. Recovery could take a long time. // _evtPh2Init.Wait(); // Fix for bug 151799. We will be setting _evtPh2Init if we detect // corruption to break a potential deadlock. So we should bail out // without flushing if recovery wasn't successfully completed. if ( IsShuttingDown() || !IsRecoveryCompleted() ) { ciDebugOut(( DEB_ITRACE, "ProcessChangesFlush abort: shutting down\n" )); return; } CLock lock( _mutex ); { BOOL fAnyInitialScans = ( _scanMgr.AnyInitialScans() || _usnMgr.AnyInitialScans() ); BOOL fIsBackedUpMode = _propstoremgr.IsBackedUpMode(); if ( !fAnyInitialScans ) { if ( !fIsBackedUpMode ) { ciDebugOut(( DEB_ITRACE, "switch from NotBackedUp to BackedUp\n" )); _propstoremgr.MarkBackedUpMode(); } } else { if ( fIsBackedUpMode ) { ciDebugOut(( DEB_ITRACE, "switch from BackedUp to NotBackedUp\n" )); _propstoremgr.MarkNotBackedUpMode(); } } } // // Flush the property store so that all documents indexed into it so far // will be persisted before the marker is advanced. If we do not flush the // prop store before advancing the marker, and if the prop store shut down // dirty, we will end up with a few docs that will have to be forced out // of the prop store because their wids would be unreliable, and they won't // get rescanned because the marker was already advanced. // // Note: flushing the propertystore manager will reset the backup streams, // so be sure to do this when switching backup modes. // if ( _propstoremgr.IsBackedUpMode() ) _propstoremgr.Flush(); _ftLastCLFlush = ft; _usnFlushInfoList.SetUsns( cEntries, ppUsnEntries ); ScheduleSerializeChanges(); } //ProcessChangesFlush //+--------------------------------------------------------------------------- // // Member: CiCat::ScheduleSerializeChanges // // Synopsis: Schedules a task with scan manager to serialize changes info // // History: 4-16-96 srikants Created // //---------------------------------------------------------------------------- void CiCat::ScheduleSerializeChanges() { _scanMgr.ScheduleSerializeChanges(); } //+--------------------------------------------------------------------------- // // Member: CiCat::NoLokProcessDiskFull // // Synopsis: Processes the disk full situation. Stops any in progress scans // and also prevents future scans until the situation eases. // Further notifications are also disabled. // // History: 4-16-96 srikants Created // // Notes: Don't have a lock on CiCat when calling this // //---------------------------------------------------------------------------- void CiCat::NoLokProcessDiskFull() { if ( IsStarted() && !_statusMonitor.IsCorrupt() ) { TRY { // // Don't call _scopeTable.ProcessDiskFull with CiCat lock - // there can be a deadlock with the notification manager. // _scopeTable.ProcessDiskFull( _docStore, GetPartition() ); // // Don't take the lock here. These are atomic dword updates // and the only risk is two debugouts. // The resman lock may be held here. // if ( !_statusMonitor.IsLowOnDisk() ) { ciDebugOut(( DEB_WARN, "CiCat:Entering low disk space state\n" )); _statusMonitor.SetDiskFull(); } } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "Error (0x%X) caught in CiCat::ProcessDiskFull\n" )); } END_CATCH } } //NoLokProcessDiskFull //+--------------------------------------------------------------------------- // // Member: CiCat::NoLokClearDiskFull // // Synopsis: Clears the disk full situation, re-enables notifications and // starts a scan on all the paths. // // History: 4-16-96 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CiCat::NoLokClearDiskFull() { if ( IsStarted() && !_statusMonitor.IsCorrupt() ) { if ( _statusMonitor.IsLowOnDisk() ) { // // don't obtain cicat lock while calling to scope table - there // can be a deadlock with notification manager. // _scopeTable.ClearDiskFull( _docStore ); // // Don't take the lock here since the resman lock is probably // held and this is just an atomic dword write. // ciDebugOut(( DEB_WARN, "CiCat:Clearing Low DiskSpace\n" )); _statusMonitor.ClearLowDiskSpace(); } } } //NoLokClearDiskFull //+------------------------------------------------------------------------- // // Function: GetVRoots // // Synopsis: Retrieves virtual roots from the given vserver // // Arguments: [ulInstance] -- the vserver instance // [eType] -- w3/nntp/imap // [xDirs] -- where to write the vroot info // // History: 27-Jan-98 dlee Created // //-------------------------------------------------------------------------- void GetVRoots( ULONG ulInstance, CiVRootTypeEnum eType, XPtr & xDirs ) { // // If the vserver instance no longer exists (so the path wasn't found), // remove the vroots for that vserver by returning an empty list of // vroots. For any other error, rethrow it. // TRY { CMetaDataMgr mdMgr( FALSE, eType, ulInstance ); xDirs.Set( new CIISVirtualDirectories( eType ) ); mdMgr.EnumVPaths( xDirs.GetReference() ); } CATCH( CException, e ) { if ( HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) != e.GetErrorCode() ) RETHROW(); } END_CATCH } //GetVRoots //+------------------------------------------------------------------------- // // Member: CiCat::SynchWithIIS, private // // Synopsis: Grovel metabase looking for IIS virtual roots (w3 and/or nntp) // // Arguments: [fRescanTC] -- if TRUE, rescan the impersonation token cache // [fSleep] -- if TRUE, sleep before doing work. // // Effects: Automatic virtual roots that are no longer in the metabase // (or have changed) are removed here. // If Indexing W3 or NNTP is turned off, vroots are removed. // // Notes: The idea behind the locking is that multiple threads can // call this function at once, and only 1 thread should sync // at once. If a thread is syncing and another is waiting // on the mutex, the first thread can abort early since the // second thread will do the sync. // // History: 15-Feb-95 KyleP Created // //-------------------------------------------------------------------------- void CiCat::SynchWithIIS( BOOL fRescanTC, BOOL fSleep ) { ciDebugOut(( DEB_ITRACE, "SynchWithIIS\n" )); // // Only need 1 thread waiting for a second thread to abort // if ( _cIISSynchThreads > 1 ) { ciDebugOut(( DEB_ITRACE, "sync abort really early\n" )); return; } CReferenceCount count( _cIISSynchThreads ); // // Sleep in case other IIS changes are being made in bulk. // if ( fSleep ) { for ( int i = 0; i < 8; i++ ) { if ( !IsShuttingDown() ) SleepEx( 1000, TRUE ); } } CLock lock( _mtxIISSynch ); TRY { // // If another thread is above waiting on _mtxIISSynch, abort. // if ( _cIISSynchThreads > 1 ) { ciDebugOut(( DEB_ITRACE, "sync abort early\n" )); count.Decrement(); return; } // // Enumerate IIS up front and save info in a local copy // XPtr xW3VDirs; if ( _fIndexW3Roots ) GetVRoots( _W3SvcInstance, W3VRoot, xW3VDirs ); XPtr xNNTPVDirs; if ( _fIndexNNTPRoots ) GetVRoots( _NNTPSvcInstance, NNTPVRoot, xNNTPVDirs ); XPtr xIMAPVDirs; if ( _fIndexIMAPRoots ) GetVRoots( _IMAPSvcInstance, IMAPVRoot, xIMAPVDirs ); XGrowable xwcsVRoot; CLowerFunnyPath lcaseFunnyPRoot; unsigned iBmk = 0; BOOL fDone = FALSE; // // First, find any deletions. // while ( !fDone && !IsShuttingDown() && ( 1 == _cIISSynchThreads ) ) { unsigned cwcVRoot, cwcPRoot; ULONG Type = _strings.EnumerateVRoot( xwcsVRoot, cwcVRoot, lcaseFunnyPRoot, cwcPRoot, iBmk ); if ( Type == PCatalog::EndRoot ) fDone = TRUE; else if ( Type & PCatalog::AutomaticRoot ) { BOOL fDelete = FALSE; BOOL fIMAP = (0 != (Type & PCatalog::IMAPRoot)); BOOL fNNTP = (0 != (Type & PCatalog::NNTPRoot)); Win4Assert( !(fIMAP && fNNTP) ); BOOL fW3 = !fIMAP && !fNNTP; CiVRootTypeEnum eType = fW3 ? W3VRoot : fNNTP ? NNTPVRoot : IMAPVRoot; BOOL fNonIndexedVDir = ( 0 != (Type & PCatalog::NonIndexedVDir) ); if ( ( fNNTP && !_fIndexNNTPRoots ) || ( fIMAP && !_fIndexIMAPRoots ) || ( fW3 && !_fIndexW3Roots ) ) fDelete = TRUE; else { ciDebugOut(( DEB_ITRACE, "Looking for type %d %ws vroot %ws\n", Type, GetVRootService( eType ), xwcsVRoot.Get() )); // // Look up the vpath in the cached metabase list // CIISVirtualDirectories * pDirs = fNNTP ? xNNTPVDirs.GetPointer() : fW3 ? xW3VDirs.GetPointer() : xIMAPVDirs.GetPointer(); Win4Assert( 0 != pDirs ); if ( ! pDirs->Lookup( xwcsVRoot.Get(), cwcVRoot, lcaseFunnyPRoot.GetActualPath(), cwcPRoot ) ) fDelete = TRUE; } if ( fDelete ) { // OPTIMIZATION: if path is a registry scope, migrate // scope from vroot to registry scope. Otherwise the // files will be deleted, rescanned, and readded. ciDebugOut(( DEB_ITRACE, "removing %ws vroot %ws\n", GetVRootService( eType ), xwcsVRoot.Get() )); RemoveVirtualScope( xwcsVRoot.Get(), TRUE, eType, !fNonIndexedVDir ); } } } // // If another thread is above waiting on _mtxIISSynch, abort. // if ( _cIISSynchThreads > 1 ) { ciDebugOut(( DEB_ITRACE, "sync abort early\n" )); count.Decrement(); return; } // only set up impersonation and add vroots if indexing w3, nntp, or imap if ( _fIndexW3Roots || _fIndexNNTPRoots || _fIndexIMAPRoots ) { if ( !IsShuttingDown() && fRescanTC ) { SignalDaemonRescanTC(); _impersonationTokenCache.ReInitializeIISScopes( xW3VDirs.GetPointer(), xNNTPVDirs.GetPointer(), xIMAPVDirs.GetPointer() ); } // // Now add paths we haven't seen from IIS services // if ( _fIndexW3Roots && !IsShuttingDown() ) xW3VDirs->Enum( *this ); if ( _fIndexNNTPRoots && !IsShuttingDown() ) xNNTPVDirs->Enum( *this ); if ( _fIndexIMAPRoots && !IsShuttingDown() ) xIMAPVDirs->Enum( *this ); } } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "Exception 0x%x caught groveling IIS metabase.\n", e.GetErrorCode() )); HandleError( e.GetErrorCode() ); } END_CATCH ciDebugOut(( DEB_ITRACE, "SynchWithIIS (done)\n" )); } //SynchWithIIS //+------------------------------------------------------------------------- // // Member: CiCat::SynchWithRegistryScopes, private // // Synopsis: Grovel registry looking for CI scopes to filter // // Arguments: [fRescanTC] -- if TRUE, rescan the impersonation token cache // // History: 16-Oct-96 dlee Created // 30-Jun-98 kitmanh handle read-only catalogs // //-------------------------------------------------------------------------- void CiCat::SynchWithRegistryScopes( BOOL fRescanTC ) { // All catalogs are named now Win4Assert( 0 != GetName() ); TRY { ciDebugOut(( DEB_ITRACE, "Reading scopes from '%ws'\n", GetScopesKey() )); CRegAccess regScopes( RTL_REGISTRY_ABSOLUTE, GetScopesKey() ); if ( !IsReadOnly() ) { // First, find any deletions. WCHAR awcRoot[MAX_PATH]; unsigned iBmk = 0; BOOL fDone = FALSE; // // update ignoredscopes table // while ( !IsShuttingDown() && _scopesIgnored.Enumerate( awcRoot, sizeof awcRoot/sizeof WCHAR, iBmk ) ) { OnIgnoredScopeDelete(awcRoot, iBmk, regScopes); } // // update _scopeTable // iBmk = 0; while ( !IsShuttingDown() && _scopeTable.Enumerate( awcRoot, sizeof awcRoot / sizeof WCHAR, iBmk ) ) { OnIndexedScopeDelete(awcRoot, iBmk, regScopes); } if ( !IsShuttingDown() ) { if ( fRescanTC ) { SignalDaemonRescanTC(); _impersonationTokenCache.ReInitializeScopes(); } } // Now add paths we haven't seen. if ( !IsShuttingDown() ) { CRegistryScopesCallBackAdd callback( *this ); regScopes.EnumerateValues( 0, callback ); // Do seen processing to remove stale fixups. SetupScopeFixups(); } } else { if ( !IsShuttingDown() ) { CRegistryScopesCallBackFillUsnArray callback( *this ); regScopes.EnumerateValues( 0, callback ); } } } CATCH( CException, e ) { ciDebugOut(( DEB_WARN, "Exception 0x%x caught groveling ci registry.\n", e.GetErrorCode() )); HandleError( e.GetErrorCode() ); } END_CATCH } //SynchWithRegistryScopes //+------------------------------------------------------------------------- // // Member: CiCat::OnIndexedScopeDelete, private // // Synopsis: Grovel registry looking for indexed CI scopes to remove // // Arguments: [pwcRoot] -- indexed scope table entry to search for and // possibly remove // [iBmk] -- enumeration index // [regScopes] -- registry key to enumerate // // returns: none. // // History: 16-Oct-96 dlee Created // 3-20-98 mohamedn cut from SynchWithRegistryScopes // //-------------------------------------------------------------------------- void CiCat::OnIndexedScopeDelete(WCHAR const * pwcRoot, unsigned & iBmk, CRegAccess & regScopes) { ciDebugOut(( DEB_ITRACE, "Looking for on-disk '%ws'\n", pwcRoot )); BOOL fDelete = FALSE; CRegistryScopesCallBackFind callback( pwcRoot ); TRY { regScopes.EnumerateValues( 0, callback ); // If the physical scope isn't in the registry and it isn't // an IIS root, delete it. if ( ( !callback.WasFound() ) && ( !_strings.DoesPhysicalRootExist( pwcRoot ) ) ) { fDelete = TRUE; } } CATCH( CException, e ) { ciDebugOut(( DEB_WARN, "Exception 0x%x enumerating regscopes for Root '%ws'\n", e.GetErrorCode(), pwcRoot )); // No scopes registry key is the likely exception. Only // remove the root if it isn't an IIS root. fDelete = !_strings.DoesPhysicalRootExist( pwcRoot ); } END_CATCH if ( fDelete ) { ciDebugOut(( DEB_WARN, "removing on-disk root '%ws'\n", pwcRoot )); RemoveScopeFromCI( pwcRoot, TRUE ); } } //+------------------------------------------------------------------------- // // Member: CiCat::OnIgnoredScopeDelete, private // // Synopsis: Grovel registry looking for excluded CI scopes to remove // // Arguments: [pwcRoot] -- ignored scope table entry to search for and // possibly remove // [iBmk] -- enumeration index // [regScopes] -- registry key to enumerate // // returns: none. // // History: 3-20-98 mohamedn created // //-------------------------------------------------------------------------- void CiCat::OnIgnoredScopeDelete(WCHAR const * pwcRoot, unsigned & iBmk, CRegAccess & regScopes) { ciDebugOut(( DEB_ITRACE, "OnIgnoredScopeDelete(%ws)\n", pwcRoot )); BOOL fDelete = FALSE; CRegistryScopesCallBackFind callback( pwcRoot ); TRY { regScopes.EnumerateValues( 0, callback ); if ( !callback.WasFound() ) { fDelete = TRUE; } } CATCH( CException, e ) { ciDebugOut(( DEB_WARN, "Exception 0x%x enumerating regscopes for Root '%ws'\n", e.GetErrorCode(), pwcRoot )); // No scopes registry key is the likely exception. Only // remove the root if it isn't an IIS root. fDelete = !_strings.DoesPhysicalRootExist( pwcRoot ); } END_CATCH if ( fDelete ) { ciDebugOut(( DEB_WARN, "removing IgnoredScope '%ws'\n", pwcRoot )); // // need to rescan scopes that match the regx being removed. // _scopesIgnored.RemoveElement(pwcRoot); iBmk--; CScopeEntry deletedIgnoredScope(pwcRoot); ciDebugOut(( DEB_ITRACE, "GetScopeToRescan(%ws)\n", deletedIgnoredScope.Get() )); ScanScopeTableEntry(deletedIgnoredScope.Get()); } } //+------------------------------------------------------------------------- // // Member: CiCat::ScanScopeTableEntry, private // // Synopsis: Initiates a Scan on suitable scope table entries, or passed-in // scope. // // Arguments: [pwszScopeToRescan] -- input scope to rescan // // returns: none. // // History: 3-20-98 mohamedn created // //-------------------------------------------------------------------------- void CiCat::ScanScopeTableEntry(WCHAR const * pwszScopeToRescan) { unsigned iBmk = 0; WCHAR awcRoot[MAX_PATH+1]; while ( !IsShuttingDown() && _scopeTable.Enumerate( awcRoot, sizeof awcRoot / sizeof WCHAR, iBmk ) ) { CScopeMatch scopeToRescan( pwszScopeToRescan, wcslen(pwszScopeToRescan) ); if ( scopeToRescan.IsPrefix( awcRoot, wcslen(awcRoot) ) ) { ciDebugOut(( DEB_ERROR, "ScanThisScope(%ws)\n", pwszScopeToRescan )); ScanThisScope(pwszScopeToRescan); break; } else if ( scopeToRescan.IsInScope( awcRoot, wcslen(awcRoot) ) ) { ciDebugOut(( DEB_ERROR, "ScanThisScope(%ws)\n", awcRoot )); ScanThisScope(awcRoot); } } } //+------------------------------------------------------------------------- // // Member: CiCat::ScanThisScope, private // // Synopsis: Forces a scan on the supplied path // // Arguments: [pwszPath] -- path to scan // // returns: none // // History: 3-20-98 mohamedn created // //-------------------------------------------------------------------------- void CiCat::ScanThisScope(WCHAR const * pwszPath) { Win4Assert( pwszPath ); BOOL fDoDeletions = TRUE; BOOL fSupportsUsns = VolumeSupportsUsns( pwszPath[0] ); VOLUMEID volumeId; if ( fSupportsUsns ) volumeId = MapPathToVolumeId( pwszPath ); else volumeId = CI_VOLID_USN_NOT_ENABLED; if ( fSupportsUsns ) { InitUsnTreeScan( pwszPath ); _usnMgr.AddScope( pwszPath, volumeId, fDoDeletions ); } else { _scanMgr.ScanScope( pwszPath, GetPartition(), UPD_FULL, fDoDeletions, FALSE ); // not a delayed scan - immediate scan } } //+------------------------------------------------------------------------- // // Member: CiCat::SetupScopeFixups, private // // Synopsis: Grovel registry looking for CI scopes and setup fixups // // History: 16-Oct-96 dlee Created // //-------------------------------------------------------------------------- void CiCat::SetupScopeFixups() { // is the catalog not named? if so, it isn't in the registry if ( 0 == GetName() ) return; TRY { _scopeFixup.BeginSeen(); ciDebugOut(( DEB_ITRACE, "Reading scope fixups from '%ws'\n", GetScopesKey() )); CRegAccess regScopes( RTL_REGISTRY_ABSOLUTE, GetScopesKey() ); CRegistryScopesCallBackFixups callback( this ); regScopes.EnumerateValues( 0, callback ); _scopeFixup.EndSeen(); } CATCH( CException, e ) { ciDebugOut(( DEB_WARN, "Exception 0x%x caught groveling ci registry fixups.\n", e.GetErrorCode() )); HandleError( e.GetErrorCode() ); } END_CATCH } //SetupScopeFixups // // Support for CI Frame Work // //+--------------------------------------------------------------------------- // // Member: CiCat::StartupCiFrameWork // // Synopsis: Takes the CiManager object pointer and refcounts it. // // Arguments: [pICiManager] - // // History: 12-05-96 srikants Created // // Note: Choose a better name. // //---------------------------------------------------------------------------- // void CiCat::StartupCiFrameWork( ICiManager * pICiManager ) { Win4Assert( 0 != pICiManager ); Win4Assert( 0 == _xCiManager.GetPointer() ); if ( 0 == _xCiManager.GetPointer() ) { pICiManager->AddRef(); _xCiManager.Set( pICiManager ); } RefreshRegistryParams(); } //+--------------------------------------------------------------------------- // // Member: CiCat::RefreshRegistryParams // // Synopsis: Refreshes CI registry parameters. // // History: 12-12-96 srikants Created // 1-25-97 mohamedn ICiAdminParams, ICiAdmin // Notes: // //---------------------------------------------------------------------------- void CiCat::RefreshRegistryParams() { ICiAdminParams *pICiAdminParams = 0; XInterface xICiAdminParams; ICiAdmin *pICiAdmin = 0; XInterface xICiAdmin; // ++++++++++++++++++++++++++++++++++++++++++++++++++++ { CLock lock(_mutex); if ( !IsShuttingDown() && 0 != _xCiManager.GetPointer() ) { // get pICiAdminParams SCODE sc = _xCiManager->GetAdminParams( &pICiAdminParams ); if ( FAILED(sc) ) { Win4Assert( 0 == pICiAdminParams ); THROW( CException(sc) ); } xICiAdminParams.Set(pICiAdminParams); // get pICiAdmin sc = _xCiManager->QueryInterface(IID_ICiAdmin,(void **)&pICiAdmin); if ( FAILED(sc) ) { Win4Assert( 0 == pICiAdmin ); THROW( CException(sc) ); } xICiAdmin.Set(pICiAdmin); } } // ----------------------------------------------------- if ( !xICiAdmin.IsNull() ) { SCODE sc = xICiAdmin->InvalidateLangResources(); if ( FAILED (sc) ) { Win4Assert( !"Failed to InvalidateLangResources\n" ); THROW( CException(sc) ); } } if ( 0 != pICiAdminParams ) _regParams.Refresh(pICiAdminParams); // // Did we switch on/off auto-aliasing? // if ( _fAutoAlias != _regParams.IsAutoAlias() ) { _fAutoAlias = _regParams.IsAutoAlias(); SynchShares(); } } //RefreshRegistryParams //+--------------------------------------------------------------------------- // // Member: CiCat::MakeBackupOfPropStore // // Synopsis: Pass through to CPropertyStore::MakeBackupCopy // // Arguments: [pwszDir] - The directory in which to create the // backup. // [pIProgressNotify] - // [fAbort] - // [pIWorkIds] - // // History: 3-26-97 srikants Created // 3-12-98 kitmanh Passed FALSE to the constructor of // CiStorage, since backup should always // be writable // 01-Nov-98 KLam Pass DiskSpaceToLeave to CiStorage // //---------------------------------------------------------------------------- void CiCat::MakeBackupOfPropStore( WCHAR const * pwszDir, IProgressNotify * pIProgressNotify, BOOL & fAbort, ICiEnumWorkids * pIWorkIds ) { XPtr xStorage( new CiStorage( pwszDir, _xAdviseStatus.GetReference(), _regParams.GetMinDiskSpaceToLeave(), CURRENT_VERSION_STAMP, FALSE) ); _propstoremgr.MakeBackupCopy( pIProgressNotify, fAbort, xStorage.GetReference(), pIWorkIds, 0); } //+--------------------------------------------------------------------------- // // Member: CiCat::IsDirectory // // Synopsis: Returns TRUE if the file is a directory // // Arguments: [pOldFunnyPath] - File to check // [pNewFunnyPath] - If a file/dir is renamed, then the attributes // of this file are checked to determine whether // pwcsOldFileName is a directory or not // // History: 20-Mar-96 SitaramR Created // //---------------------------------------------------------------------------- BOOL CiCat::IsDirectory( const CLowerFunnyPath * pOldFunnyPath, const CLowerFunnyPath * pNewFunnyPath ) { BOOL fUsnVolume = IsOnUsnVolume( pOldFunnyPath->GetActualPath() ); WORKID wid; { CLock lock( _mutex ); FILEID fileId = fileIdInvalid; if ( fUsnVolume ) wid = LokLookupWid( *pOldFunnyPath, fileId ); else wid = _strings.LokFind( pOldFunnyPath->GetActualPath() ); } if ( wid == widInvalid ) { if ( pNewFunnyPath == 0 ) return FALSE; else { // // For rename file notifications, use attributes of pwcsNewFileName // to determine whether pwcsOldFileName is a directory or not // ULONG ulFileAttrib = GetFileAttributes( pNewFunnyPath->GetPath() ); if ( ulFileAttrib == 0xFFFFFFFF ) return FALSE; else return ( ulFileAttrib & FILE_ATTRIBUTE_DIRECTORY ); } } else { PROPVARIANT propVar; if ( _propstoremgr.ReadPrimaryProperty( wid, pidAttrib, propVar ) ) { if ( propVar.vt == VT_EMPTY ) { // // Case where directories are not being filtered // return FALSE; } Win4Assert( propVar.vt == VT_UI4 ); return ( propVar.ulVal & FILE_ATTRIBUTE_DIRECTORY ); } else return FALSE; } } //IsDirectory //+--------------------------------------------------------------------------- // // Member: CiCat::IsOnUsnVolume // // Synopsis: Is the path on a volume that supports usns // // Arguments: [pwszPath] - Path to check // // History: 03-Feb-98 dlee Created // //---------------------------------------------------------------------------- BOOL CiCat::IsOnUsnVolume( WCHAR const *pwszPath ) { Win4Assert( 0 != pwszPath ); // Usns are not supported for remote paths if ( L'\\' == pwszPath[0] ) return FALSE; // Look in cache of known usn volumes for ( unsigned i = 0; i < _cUsnVolumes; i++ ) { if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] ) return _aUsnVolumes[i].FUsnVolume(); } return FALSE; } //IsOnUsnVolume //+--------------------------------------------------------------------------- // // Member: CiCat::VolumeSupportsUsns // // Synopsis: Checks if the volume supports USNs // // Arguments: [wcVolume] -- volume letter to check // // History: 05-May-97 SitaramR Created // // Notes: This method has the side-effect of registering this volume // for USN processing. // //---------------------------------------------------------------------------- BOOL CiCat::VolumeSupportsUsns( WCHAR wcVolume ) { // // Usns are not supported for remote paths // if ( L'\\' == wcVolume ) return FALSE; // // Look in the cache of known usn volumes // for ( unsigned i = 0; i < _cUsnVolumes; i++ ) { if ( _aUsnVolumes[i].DriveLetter() == wcVolume ) return _aUsnVolumes[i].FUsnVolume(); } CLock lock( _mutex ); // // Look in the cache again under lock, in case some other thread slipped // it in the array. // for ( i = 0; i < _cUsnVolumes; i++ ) { if ( _aUsnVolumes[i].DriveLetter() == wcVolume ) return _aUsnVolumes[i].FUsnVolume(); } // // Find out if it's a USN volume and add the result to the cache // CImpersonateSystem impersonate; BOOL fUsnVolume = FALSE; USN_JOURNAL_DATA UsnJournalInfo; FILE_FS_VOLUME_INFORMATION VolumeInfo; WCHAR wszVolumePath[] = L"\\\\.\\a:"; wszVolumePath[4] = wcVolume; VolumeInfo.VolumeCreationTime.QuadPart = 0; VolumeInfo.VolumeSerialNumber = 0; HANDLE hVolume = CreateFile( wszVolumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if ( hVolume == INVALID_HANDLE_VALUE ) fUsnVolume = FALSE; else { SWin32Handle xHandleVolume( hVolume ); // // Look up the volume serial number and create time. We'll use these // later to decide if the volume has been reformatted underneath us. // We don't bother to check error, because there's nothing we could // do except set fields to 0, which has already been done. // IO_STATUS_BLOCK iosb; NtQueryVolumeInformationFile( hVolume, &iosb, &VolumeInfo, sizeof(VolumeInfo), FileFsVolumeInformation ); // // This call will only succeed on NTFS NT5 w/ USN Journal enabled. // NTSTATUS Status = NtFsControlFile( hVolume, NULL, NULL, NULL, &iosb, FSCTL_QUERY_USN_JOURNAL, 0, 0, &UsnJournalInfo, sizeof(UsnJournalInfo) ); Win4Assert( STATUS_PENDING != Status ); if ( NT_SUCCESS(Status) && NT_SUCCESS(iosb.Status) ) fUsnVolume = TRUE; else if ( !IsReadOnly() && ( STATUS_JOURNAL_NOT_ACTIVE == Status || STATUS_INVALID_DEVICE_STATE == Status ) ) { // // Usn journal not created, create it // CREATE_USN_JOURNAL_DATA usnCreateData; usnCreateData.MaximumSize = _regParams.GetMaxUsnLogSize(); usnCreateData.AllocationDelta = _regParams.GetUsnLogAllocationDelta(); Status = NtFsControlFile( hVolume, NULL, NULL, NULL, &iosb, FSCTL_CREATE_USN_JOURNAL, &usnCreateData, sizeof(usnCreateData), NULL, NULL ); if ( NT_SUCCESS( Status ) && NT_SUCCESS(iosb.Status) ) { Status = NtFsControlFile( hVolume, NULL, NULL, NULL, &iosb, FSCTL_QUERY_USN_JOURNAL, 0, 0, &UsnJournalInfo, sizeof(UsnJournalInfo) ); Win4Assert( STATUS_PENDING != Status ); if ( NT_SUCCESS(Status) && NT_SUCCESS(iosb.Status) ) fUsnVolume = TRUE; else fUsnVolume = FALSE; } else fUsnVolume = FALSE; } else fUsnVolume = FALSE; } _aUsnVolumes[ _cUsnVolumes ].Set( wcVolume, VolumeInfo.VolumeCreationTime.QuadPart, VolumeInfo.VolumeSerialNumber, fUsnVolume, UsnJournalInfo.UsnJournalID ); _cUsnVolumes++; return fUsnVolume; } //VolumeSupportsUsns //+--------------------------------------------------------------------------- // // Member: CiCat::GetJournalId, public // // Arguments: [pwszPath] -- Path to USN-enabled volume // // Returns: Current USN Journal ID. // // History: 17-Mar-98 KyleP Created // //---------------------------------------------------------------------------- ULONGLONG const & CiCat::GetJournalId( WCHAR const * pwszPath ) { static ULONGLONG _zero = 0; Win4Assert( L'\\' != pwszPath[0] ); // // Look in cache of known usn volumes // for ( unsigned i=0; i < _cUsnVolumes; i++ ) { if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] ) return _aUsnVolumes[i].JournalId(); } Win4Assert( !"Not USN volume!" ); return _zero; } //+--------------------------------------------------------------------------- // // Member: CiCat::GetVolumeCreationTime, public // // Arguments: [pwszPath] -- Path to volume // // Returns: Current create time (changed on reformat) // // History: 17-Mar-98 KyleP Created // //---------------------------------------------------------------------------- ULONGLONG const & CiCat::GetVolumeCreationTime( WCHAR const * pwszPath ) { static ULONGLONG _zero = 0; if ( pwszPath[0] == L'\\' ) { static ULONGLONG ullZero = 0; return ullZero; } // // Look in cache of known usn volumes // for ( unsigned i=0; i < _cUsnVolumes; i++ ) { if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] ) return _aUsnVolumes[i].CreationTime(); } Win4Assert( !"Not known volume!" ); return _zero; } //+--------------------------------------------------------------------------- // // Member: CiCat::GetVolumeSerialNumber, public // // Arguments: [pwszPath] -- Path to volume // // Returns: Current serial number (changed on reformat) // // History: 17-Mar-98 KyleP Created // //---------------------------------------------------------------------------- ULONG CiCat::GetVolumeSerialNumber( WCHAR const * pwszPath ) { if ( pwszPath[0] == L'\\' ) return 0; // // Look in cache of known usn volumes // for ( unsigned i=0; i < _cUsnVolumes; i++ ) { if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] ) return _aUsnVolumes[i].SerialNumber(); } Win4Assert( !"Not known volume!" ); return 0; } //+--------------------------------------------------------------------------- // // Member: CiCat::MapPathToVolumeId // // Synopsis: Maps a path to a volume id // // Arguments: [pwszPath] -- Path to map // // History: 07-May-97 SitaramR Created // // Notes: The volume id is obtained from the drive letter // //---------------------------------------------------------------------------- VOLUMEID CiCat::MapPathToVolumeId( const WCHAR *pwszPath ) { Win4Assert( wcslen(pwszPath) > 1 && pwszPath[0] != L'\\' && pwszPath[1] == L':' ); VOLUMEID volId = pwszPath[0]; Win4Assert( volId <= 0xff ); return volId; } //+--------------------------------------------------------------------------- // // Member: CiCat::ProcessFile // // Synopsis: Process given file during usn tree traversal. It returns // the workid of file which is used to determine if the file // should be processed as an update notification. // // Arguments: [lcaseFunnyPath] -- Path of file // [fileId] -- Fileid of file // [volumeId] -- Volume id of file // [widParent] -- Workid of parent // [wid] -- Workid of file returned here // // Returns: TRUE if the file was added to CI // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- BOOL CiCat::ProcessFile( const CLowerFunnyPath & lcaseFunnyPath, FILEID fileId, VOLUMEID volumeId, WORKID widParent, DWORD dwFileAttributes, WORKID & wid ) { Win4Assert( 0 != &_mutex ); CLock lock( _mutex ); wid = _fileIdMap.LokFind( fileId, volumeId ); if ( wid == widInvalid ) { BOOL fNew; wid = PathToWorkId( lcaseFunnyPath, TRUE, // Add to CI fNew, 0, eUsnsArray, fileId, widParent, TRUE ); // CI doesn't know about this if (wid == widInvalid) { // // Unknown file // return FALSE; } // // New file, don't re-write property store info just written // LokWriteFileUsnInfo( wid, fileId, FALSE, volumeId, widParent, dwFileAttributes ); WriteFileAttributes( wid, dwFileAttributes ); return TRUE; } else { // // If the path has changed (due to a rename of the file or a parent // directory), update the path in the property store. // // override const here - unethical, but saves us a copy CFunnyPath oldPath; WorkIdToPath( wid, oldPath ); BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash(); SCODE sc = S_OK; if ( ( lcaseFunnyPath.GetActualLength() != oldPath.GetActualLength() ) || ( 0 != memcmp( lcaseFunnyPath.GetActualPath(), oldPath.GetActualPath(), oldPath.GetActualLength() * sizeof WCHAR ) ) ) { ciDebugOut(( DEB_ITRACE, "renaming '%ws' to '%ws' during scan\n", oldPath.GetActualPath(), lcaseFunnyPath.GetActualPath() )); PROPVARIANT var; var.vt = VT_LPWSTR; var.pwszVal = (WCHAR*) lcaseFunnyPath.GetActualPath(); sc = _propstoremgr.WriteProperty( wid, pidPath, *(CStorageVariant const *)(ULONG_PTR)&var ); } // override const here - unethical, but saves us a copy if ( fRemoveBackSlash ) ((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash(); if ( FAILED( sc ) ) THROW( CException( sc ) ); } return FALSE; } //ProcessFile //+--------------------------------------------------------------------------- // // Member: CiCat::LokWriteFileUsnInfo // // Synopsis: Write file's usn info to property store // // Arguments: [wid] -- Workid // [fileId] -- Fileid of wid // [fWriteToPropStore] -- If TRUE, write to the property store // [volumeId] -- Volume id of wid // [widParent] -- Workid of parent // [dwFileAttributes] -- Attributes of the file // // History: 05-May-97 SitaramR Created // //---------------------------------------------------------------------------- void CiCat::LokWriteFileUsnInfo( WORKID wid, FILEID fileId, BOOL fWriteToPropStore, VOLUMEID volumeId, WORKID widParent, DWORD dwFileAttributes ) { Win4Assert( _mutex.IsHeld() ); WORKID widExisting = _fileIdMap.LokFind( fileId, volumeId ); if ( widExisting == widInvalid ) { // // Add fileid -> wid map // _fileIdMap.LokAdd( fileId, wid, volumeId ); } else if ( wid != widExisting ) { // // Remove previous stale fileid mapping and add current mapping. // This can happen when a file is renamed to a different file // during the usn tree traversal. // ciDebugOut(( DEB_ITRACE, "LokWriteFileUsnInfo, blasting fid %#I64x, %#x to %#x\n", fileId, widExisting, wid )); _fileIdMap.LokDelete( fileId, widExisting ); _fileIdMap.LokAdd( fileId, wid, volumeId ); } if ( fWriteToPropStore ) { PROPVARIANT propVar; propVar.vt = VT_UI8; propVar.uhVal.QuadPart = fileId; XWritePrimaryRecord rec( _propstoremgr, wid ); SCODE sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(), pidFileIndex, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); propVar.vt = VT_UI4; propVar.ulVal = volumeId; sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(), pidVolumeId, *(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)); propVar.ulVal = dwFileAttributes; sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(), pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc)); } } //LokWriteFileUsnInfo //+------------------------------------------------------------------------- // // Member: CiCat::UpdateDuringRecovery, public // // Synopsis: Updates an individual workid with usn == 0 during prop // store recovery. // // Arguments: [wid] -- Workid // [fileid] -- File Id. Valid only for NTFS 5.0 volumes. // [fDelete] -- TRUE to indicate deletion. Modify o/w. // [pUserData]-- Pointer to CiCat to use for updates. // // History: 06-18-97 KrishnaN Created // // Notes: This will be called at property store recovery time to cause // deletion and modification of wids. The property store will always // be flushed before the usn marker is advanced in // CheckPointchangesFlushed. Which means that docs newly added to the // property store can be deleted with a usn of 0. Using usn of 0 causes // the marker to not move forward, but that is OK because the docs // being deleted were not marked as having been filtered. As a result, // rescan will cause these docs to be picked up agains and scheduled // for filtering. // //-------------------------------------------------------------------------- void UpdateDuringRecovery( WORKID wid, BOOL fDelete, void const *pUserData ) { CiCat *pCiCat = (CiCat *)pUserData; Win4Assert(pCiCat); pCiCat->Update( wid, pCiCat->GetPartition(), CI_VOLID_USN_NOT_ENABLED, 0, fDelete ? CI_DELETE_OBJ : CI_UPDATE_OBJ); } //+--------------------------------------------------------------------------- // // Member: CiCat::FileIdToPath // // Synopsis: Converts fileid to path and checks if path is in scope // // Arguments: [fileId] -- Fileid of wid // [pUsnVolume] -- Volume // [lcaseFunnyPath] -- Path returned here // [fPathInScope] -- Scope info returned here // // History: 23-Jun-97 SitaramR Created // //---------------------------------------------------------------------------- void CiCat::FileIdToPath( FILEID fileId, CUsnVolume *pUsnVolume, CLowerFunnyPath & lcaseFunnyPath, BOOL & fPathInScope ) { fPathInScope = FALSE; WORKID wid; { CLock lock( _mutex ); wid = _fileIdMap.LokFind( fileId, pUsnVolume->VolumeId() ); if ( widInvalid == wid ) return; #if 0 // valid, but expensive PROPVARIANT propVar; BOOL fFound = _propstoremgr.ReadPrimaryProperty( wid, pidFileIndex, propVar ); Win4Assert( fFound && propVar.uhVal.QuadPart == fileId ); #endif // CIDBG == 1 unsigned cc = _strings.Find( wid, lcaseFunnyPath ); if ( 0 == cc ) THROW ( CException( STATUS_INVALID_PARAMETER ) ); } if ( !_usnMgr.IsPathIndexed( pUsnVolume, lcaseFunnyPath ) ) return; if ( ! IsEligibleForFiltering( lcaseFunnyPath ) ) return; fPathInScope = TRUE; } //FileIdToPath //+--------------------------------------------------------------------------- // // Member: CiCat::FileIdToPath // // Synopsis: Converts a fileid and volumeid to a path. // // Arguments: [fileId] -- Fileid of wid // [pUsnVolume] -- Volume // [funnyPath] -- Path returned here // // Returns: Size of path (or 0 if not found). // // History: 19-Mar-1998 dlee Created // 31-Dec-1998 KyleP Re-enabled and fixed for > MAX_PATH // //---------------------------------------------------------------------------- unsigned CiCat::FileIdToPath( FILEID & fileId, VOLUMEID volumeId, CLowerFunnyPath & funnyPath ) { Win4Assert( 0 != volumeId ); Win4Assert( CI_VOLID_USN_NOT_ENABLED != volumeId ); Win4Assert( fileIdInvalid != fileId ); Win4Assert( 0 != fileId ); // // Make sure the volume information is faulted in -- it may not exist // yet when the catalog is just starting. // VolumeSupportsUsns( (WCHAR) volumeId ); // // Look for the volume handle // unsigned cc = 0; HANDLE hVolume = INVALID_HANDLE_VALUE; for ( unsigned i = 0; i < _cUsnVolumes; i++ ) { if ( _aUsnVolumes[i].VolumeId() == volumeId ) { hVolume = _aUsnVolumes[i].Volume(); break; } } // Was the volume found in the list of volumes being indexed? if ( INVALID_HANDLE_VALUE != hVolume ) { // Put the fileid in the unicode string -- ntfs expects this UNICODE_STRING uScope; uScope.Buffer = (WCHAR *) &fileId; uScope.Length = sizeof fileId; uScope.MaximumLength = sizeof fileId; OBJECT_ATTRIBUTES ObjectAttr; InitializeObjectAttributes( &ObjectAttr, // Structure &uScope, // Name OBJ_CASE_INSENSITIVE, // Attributes hVolume, // Root 0 ); // Security IO_STATUS_BLOCK IoStatus; HANDLE h = INVALID_HANDLE_VALUE; NTSTATUS Status = NtOpenFile( &h, FILE_READ_ATTRIBUTES, &ObjectAttr, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_BY_FILE_ID ); if ( NT_SUCCESS( Status ) ) Status = IoStatus.Status; if ( NT_SUCCESS( Status ) ) { SHandle xHandle( h ); // retrieve the path info for the file opened by id XGrowable xbuf; do { PFILE_NAME_INFORMATION FileName = (PFILE_NAME_INFORMATION) xbuf.Get(); FileName->FileNameLength = xbuf.SizeOf() - sizeof(FILE_NAME_INFORMATION); Status = NtQueryInformationFile( h, &IoStatus, FileName, xbuf.SizeOf(), FileNameInformation ); Win4Assert( STATUS_PENDING != Status ); if ( NT_SUCCESS( Status ) ) Status = IoStatus.Status; if ( NT_SUCCESS( Status ) ) { ULONG Length = FileName->FileNameLength; cc = 2 + Length/sizeof(WCHAR); // // Major cheating here. The FileNameLength field precedes // the filename itself, and is juuuust big enough to hold // the two character : preface. // Win4Assert( xbuf.Get() + sizeof(WCHAR)*2 == (BYTE *)&FileName->FileName ); WCHAR * pwszPath = (WCHAR *)xbuf.Get(); pwszPath[ 0 ] = (WCHAR) volumeId; pwszPath[ 1 ] = L':'; funnyPath.SetPath( pwszPath, cc ); ciDebugOut(( DEB_ITRACE, "translated fileid %#I64x to path '%ws'\n", fileId, funnyPath.GetPath() )); break; } else if ( STATUS_BUFFER_OVERFLOW == Status ) { xbuf.SetSize( xbuf.SizeOf() * 2 ); continue; } else { ciDebugOut(( DEB_WARN, "unable to query filenameinfo: %#x\n", Status )); THROW( CException( Status ) ); } } while (TRUE); } else { // STATUS_INVALID_PARAMETER means the file wasn't found. if ( ( !IsSharingViolation( Status ) ) && ( STATUS_INVALID_PARAMETER != Status ) && ( STATUS_ACCESS_DENIED != Status ) && ( STATUS_SHARING_VIOLATION != Status ) && ( STATUS_IO_REPARSE_TAG_NOT_HANDLED != Status ) && ( STATUS_DELETE_PENDING != Status ) && ( STATUS_OBJECT_NAME_NOT_FOUND != Status ) && ( STATUS_OBJECT_NAME_INVALID != Status) && ( STATUS_OBJECT_PATH_NOT_FOUND != Status) ) { if ( ( STATUS_VOLUME_DISMOUNTED != Status ) && ( STATUS_NO_MEDIA_IN_DEVICE != Status ) && ( STATUS_UNRECOGNIZED_VOLUME != Status ) && ( STATUS_INSUFFICIENT_RESOURCES != Status ) ) { ciDebugOut(( DEB_WARN, "unable to open by id: %#x\n", Status )); #if CIDBG == 1 ciDebugOut(( DEB_WARN, "error %#x, can't get path for %#I64\n", Status, fileId )); char acTemp[ 200 ]; sprintf( acTemp, "New error %#x from FileIdToPath. Call DLee", Status ); Win4AssertEx( __FILE__, __LINE__, acTemp ); #endif } THROW( CException( Status ) ); } } } else { ciDebugOut(( DEB_WARN, "no volume match for volume %#x\n", volumeId )); } return cc; } //FileIdToPath //+--------------------------------------------------------------------------- // // Member: CiCat::UsnRecordToPathUsingParentId // // Synopsis: Converts an usn record to path via the parent fileid // and checks if path is in scope // // Arguments: [fileId] -- Fileid of wid // [pUsnVolume] -- Volume // [lowerFunnyPath] -- Path returned here // [fPathInScope] -- Scope info returned here // [widParent] -- Parent wid returned here // [fParentMayNotBeIndexed] -- TRUE if the parent directory may // have the not-indexed attribute, so it's // not in the fileid map. // // History: 23-Jun-97 SitaramR Created // //---------------------------------------------------------------------------- void CiCat::UsnRecordToPathUsingParentId( USN_RECORD *pUsnRec, CUsnVolume *pUsnVolume, CLowerFunnyPath & lowerFunnyPath, BOOL &fPathInScope, WORKID & widParent, BOOL fParentMayNotBeIndexed ) { // // Workid of parent will be widInvalid for files in root directory // widParent = widInvalid; fPathInScope = FALSE; WCHAR *pwszFilename = (WCHAR *) (((BYTE *)pUsnRec) + pUsnRec->FileNameOffset); unsigned cbFileNameLen = pUsnRec->FileNameLength; BOOL fAddParent = FALSE; CLowerFunnyPath parentPath; if ( pUsnRec->ParentFileReferenceNumber == pUsnVolume->RootFileId() ) { // // File is in root directory, so build path by prefixing drive letter // WCHAR awc[3]; awc[0] = pUsnVolume->DriveLetter(); awc[1] = L':'; awc[2] = L'\\'; lowerFunnyPath.SetPath( awc, 3 ); // The root is named "x:\." (the filename is ".") if ( pUsnRec->FileReferenceNumber != pUsnRec->ParentFileReferenceNumber ) lowerFunnyPath.AppendPath( pwszFilename, cbFileNameLen/sizeof(WCHAR) ); } else { { CLock lock( _mutex ); // // Build up the full path by prefixing parent directory // widParent = _fileIdMap.LokFind( pUsnRec->ParentFileReferenceNumber, pUsnVolume->VolumeId() ); if ( widParent == widInvalid ) { if ( fParentMayNotBeIndexed ) { // // See if the parent directory is marked as non-indexed. // If so proceed with the add, otherwise bail. // ciDebugOut(( DEB_ITRACE, "doing an expensive open!\n" )); unsigned cc = FileIdToPath( pUsnRec->ParentFileReferenceNumber, pUsnVolume->VolumeId(), lowerFunnyPath ); if ( 0 == cc ) return; DWORD dw = GetFileAttributes( lowerFunnyPath.GetActualPath() ); if ( 0xffffffff == dw ) return; if ( 0 == ( dw & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ) ) return; fAddParent = TRUE; parentPath = lowerFunnyPath; } else { // // Unknown parent, which means that this file is out of // scope or hasn't been scanned yet, hence do nothing // return; } } else { unsigned cwcPathLen = _strings.Find( widParent, lowerFunnyPath ); if ( 0 == cwcPathLen ) THROW( CException( STATUS_INVALID_PARAMETER ) ); Win4Assert( cwcPathLen == wcslen(lowerFunnyPath.GetActualPath()) ); } } lowerFunnyPath.AppendBackSlash(); lowerFunnyPath.AppendPath( pwszFilename, cbFileNameLen/sizeof(WCHAR) ); } // // Make sure the path is being indexed // if ( !_usnMgr.IsPathIndexed( pUsnVolume, lowerFunnyPath ) ) return; // // Don't filter catalog files and files in ignored scopes // if ( ! IsEligibleForFiltering( lowerFunnyPath ) ) return; if ( fAddParent ) { BOOL fNew; widParent = PathToWorkId( parentPath, TRUE, fNew, 0, eUsnsArray, pUsnRec->ParentFileReferenceNumber, widInvalid, // "go figure" FALSE ); } fPathInScope = TRUE; } //UsnRecordToPathUsingParentId //+--------------------------------------------------------------------------- // // Member: CiCat::LokMarkForDeletion // // Synopsis: Mark the wid for deletion. Assumes cicat mutex is held. // // Arguments: [fileId] -- File id // [wid] -- Workid // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- void CiCat::LokMarkForDeletion( FILEID fileId, WORKID wid ) { Win4Assert( _mutex.IsHeld() ); ciDebugOut(( DEB_ITRACE, "delete usn %#I64x wid 0x%x\n", fileId, wid )); _fileIdMap.LokDelete( fileId, wid ); _strings.LokDelete( 0, wid, TRUE, TRUE ); } //LokMarkForDeletion //+--------------------------------------------------------------------------- // // Member: CiCat::InitUsnTreeScan // // Synopsis: Initializes the entry in notification manager that a usn // for usn tree traversal is starting. // // Arguments: [pwszPath] -- Scope // // History: 28-Jul-97 SitaramR Created // // Notes: PwszPath may not be in notifymgr because it may have been removed // by the time we get here. // //---------------------------------------------------------------------------- void CiCat::InitUsnTreeScan( WCHAR const *pwszPath ) { for ( CCiNotifyIter iter(_notify); !iter.AtEnd(); iter.Advance() ) { if ( AreIdenticalPaths( iter.Get(), pwszPath ) ) { iter.InitUsnTreeScan(); break; } } } //+--------------------------------------------------------------------------- // // Member: CiCat::SetUsnTreeScanComplete // // Synopsis: Writes usn as the restart usn for the given scope // // Arguments: [pwszPath] -- Scope // [usn] -- Restart usn // // History: 27-Jun-97 SitaramR Created // // Notes: PwszPath may not be in notifymgr because it may have been removed // by the time we get here. // //---------------------------------------------------------------------------- void CiCat::SetUsnTreeScanComplete( WCHAR const *pwszPath, USN usn ) { for ( CCiNotifyIter iter(_notify); !iter.AtEnd(); iter.Advance() ) { if ( AreIdenticalPaths( iter.Get(), pwszPath ) ) { iter.SetUsnTreeScanComplete( usn ); break; } } } //+--------------------------------------------------------------------------- // // Member: CiCat::SetTreeScanComplete // // Synopsis: Indicates end of non-USN volume tree scan. Mostly for // updating Volume Id info. // // Arguments: [pwszPath] -- Scope // // History: 13-Apr-1998 KyleP Created // //---------------------------------------------------------------------------- void CiCat::SetTreeScanComplete( WCHAR const *pwszPath ) { for ( CCiNotifyIter iter(_notify); !iter.AtEnd(); iter.Advance() ) { if ( AreIdenticalPaths( iter.Get(), pwszPath ) ) { iter.SetTreeScanComplete(); break; } } } //+--------------------------------------------------------------------------- // // Member: CiCat::CheckUsnTreeScan // // Synopsis: Checks if Usn tree scan has been initialized // // Arguments: [pwszPath] -- Scope // // History: 8-Sep-97 SitaramR Created // // Notes: PwszPath may not be in notifymgr because it may have been removed // by the time we get here. // //---------------------------------------------------------------------------- #if CIDBG==1 void CiCat::CheckUsnTreeScan( WCHAR const *pwszPath ) { for ( CCiNotifyIter iter(_notify); !iter.AtEnd(); iter.Advance() ) { if ( AreIdenticalPaths( iter.Get(), pwszPath ) ) { // This assert is either bogus or hit in cases we don't understand. // When there is time, add logging code to see what's up. // Win4Assert( iter.FUsnTreeScan() ); break; } } } #endif //+--------------------------------------------------------------------------- // // Member: CiCat::DebugPrintWidInfo // // Synopsis: Debug prints wid info // // Arguments: [wid] -- Wid to print // // History: 05-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- #if CIDBG == 1 void CiCat::DebugPrintWidInfo( WORKID wid ) { XGrowable xPath; unsigned ccSize; { CLock lock( _mutex ); ccSize = _strings.Find( wid, xPath ); } if ( ccSize == 0 ) xPath[0] = 0; PROPVARIANT propVar; _propstoremgr.ReadPrimaryProperty( wid, pidFileIndex, propVar ); FILEID fileId = propVar.uhVal.QuadPart; _propstoremgr.ReadPrimaryProperty( wid, pidVolumeId, propVar ); VOLUMEID volumeId = propVar.ulVal; _propstoremgr.ReadPrimaryProperty( wid, pidParentWorkId, propVar ); WORKID widParent = propVar.ulVal; ciDebugOut(( DEB_ERROR, " Wid %#x, fileid %#I64x, volumeId %#x, path %ws, widParent %#x\n", wid, fileId, volumeId, xPath.Get(), widParent )); } #endif //+--------------------------------------------------------------------------- // // Member: CiCat::IsUNCName // // Synopsis: Determines if a path is a UNC // // Arguments: [pwszVal] -- The path to check // // History: ? srikants Created // //---------------------------------------------------------------------------- BOOL CiCat::IsUNCName( WCHAR const * pwszVal ) { Win4Assert( 0 != pwszVal ); CPathParser parser(pwszVal); return parser.IsUNCName(); } //+--------------------------------------------------------------------------- // // Member: CiCat::AddShadowScopes, private // // Synopsis: Re-adds shadow (virtual) scopes to registry. // // History: 12-Oct-1997 KyleP Created // // Notes: This code handles the case where the registry got wiped, // possibly by an upgrade, but the catalog data still exists. // //---------------------------------------------------------------------------- void CiCat::AddShadowScopes() { // // Enumerate through the scope table, looking for entries that map // into the virtual namespace. // WCHAR wcsPRoot[MAX_PATH+1]; unsigned ccPRoot; unsigned iBmk = 0; for ( ccPRoot = sizeof(wcsPRoot)/sizeof(WCHAR); _scopeTable.Enumerate( wcsPRoot, ccPRoot, iBmk ); ccPRoot = sizeof(wcsPRoot)/sizeof(WCHAR) ) { if ( _strings.DoesPhysicalRootExist( wcsPRoot ) ) { ciDebugOut(( DEB_FORCE, "Refresh %ws\n", wcsPRoot )); _scopeTable.RefreshShadow( wcsPRoot, _xScopesKey.Get() + SKIP_TO_LM ); } } } //+--------------------------------------------------------------------------- // // Class: CUserPropCallback // // Purpose: Helper class for CiCat::RecoverUserProperties. Reads // registry entries describing cached properties and adds them // to the property store. // // History: 07-Nov-97 KyleP Created // //---------------------------------------------------------------------------- class CUserPropCallback : public CRegCallBack { public: CUserPropCallback( CiCat & cat, ULONG_PTR ulToken, DWORD dwStoreLevel ) : _cat( cat ), _ulToken( ulToken ), _dwStoreLevel( dwStoreLevel ) { } NTSTATUS CallBack( WCHAR *pValueName, ULONG uValueType, VOID *pValueData, ULONG uValueLength ) { CParseRegistryProperty ParseProp( pValueName, uValueType, pValueData, uValueLength ); if ( ParseProp.IsOk() ) { PROPID pid = _cat.PropertyToPropId( ParseProp.GetFPS(), TRUE ); if ( pidInvalid == pid ) return STATUS_INVALID_PARAMETER; // Silently fail attempts to monkey with built-in properties. if ( pidPath == pid || pidLastSeenTime == pid || pidAttrib == pid || pidVirtualPath == pid || pidSecurity == pid || pidParentWorkId == pid || pidSecondaryStorage == pid || pidFileIndex == pid || pidVolumeId == pid || pidSize == pid || pidWriteTime == pid ) return STATUS_SUCCESS; if ( _dwStoreLevel == ParseProp.StorageLevel() ) _cat._propstoremgr.Setup( pid, ParseProp.Type(), ParseProp.Size(), _ulToken, ParseProp.IsModifiable(), _dwStoreLevel ); return STATUS_SUCCESS; } else return STATUS_INVALID_PARAMETER; } private: CiCat & _cat; ULONG_PTR _ulToken; DWORD _dwStoreLevel; }; //+--------------------------------------------------------------------------- // // Member: CiCat::RecoverUserProperties, private // // Synopsis: Re-adds user-defined properties to property cache // // Arguments: [ulToken] -- Token for open property metadata transaction // [dwStoreLevel] -- Which storage level are we operating on? // // History: 07-Nov-1997 KyleP Created // //---------------------------------------------------------------------------- void CiCat::RecoverUserProperties( ULONG_PTR ulToken, DWORD dwStoreLevel ) { TRY { // // Iterate over all the user-defined properties // XArray xPropKey; BuildRegistryPropertiesKey( xPropKey, GetCatalogName() ); CRegAccess regProps( RTL_REGISTRY_ABSOLUTE, xPropKey.Get() ); CUserPropCallback PropCallback( *this, ulToken, dwStoreLevel ); regProps.EnumerateValues( 0, PropCallback ); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CiCat::RecoverUserProperties: caught 0x%x\n", e.GetErrorCode() )); } END_CATCH } //+--------------------------------------------------------------------------- // // Member: CiCat::DeleteUserProperty, private // // Synopsis: Removes user property definition for registry // // Arguments: [fps] -- Property to remove // // History: 11-Nov-1997 KyleP Created // //---------------------------------------------------------------------------- void CiCat::DeleteUserProperty( CFullPropSpec const & fps ) { TRY { XArray xPropKey; BuildRegistryPropertiesKey( xPropKey, GetCatalogName() ); HKEY hkey; DWORD dwError = RegOpenKey( HKEY_LOCAL_MACHINE, xPropKey.Get() + SKIP_TO_LM, &hkey ); if ( ERROR_SUCCESS != dwError ) { ciDebugOut(( DEB_ERROR, "Error %d opening %ws\n", dwError, xPropKey.Get() )); } else { SRegKey xkey( hkey ); CBuildRegistryProperty PropBuild( fps, 0, 0 ); // Note type and size don't matter. RegDeleteValue( hkey, PropBuild.GetValue() ); } } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CiCat::DeleteUserProperty: caught 0x%x\n", e.GetErrorCode() )); } END_CATCH } //DeleteUserProperty //+--------------------------------------------------------------------------- // // Member: CiCat::RenameFile, public // // Synopsis: Renames a file // // Arguments: [lcaseFunnyOldName] -- The old file name // [lcaseFunnyNewName] -- The new file name // [ulFileAttrib] -- The new file attributes // [volumeId] -- The volume id of the file // [fileId] -- The file id of the file or fileIdInvalid // if not a USN volume // // History: 4-Mar-1998 dlee Moved from cicat.hxx // //---------------------------------------------------------------------------- void CiCat::RenameFile( const CLowerFunnyPath & lcaseFunnyOldName, const CLowerFunnyPath & lcaseFunnyNewName, ULONG ulFileAttrib, VOLUMEID volumeId, FILEID fileId, WORKID widParent ) { CLock lock( _mutex ); if ( lcaseFunnyOldName.IsRemote() ) { CImpersonateSystem impersonate; RenameFileInternal(lcaseFunnyOldName, lcaseFunnyNewName, ulFileAttrib, volumeId, fileId, widParent); } else { RenameFileInternal(lcaseFunnyOldName, lcaseFunnyNewName, ulFileAttrib, volumeId, fileId, widParent); } } //RenameFile //+--------------------------------------------------------------------------- // // Member: CiCat::RenameFileInternal, public // // Synopsis: Renames a file // // Arguments: [lcaseFunnyOldName] -- The old file name // [lcaseFunnyNewName] -- The new file name // [ulFileAttrib] -- The new file attributes // [volumeId] -- The volume id of the file // [fileId] -- The file id of the file or fileIdInvalid // if not a USN volume // // History: 4-Mar-1998 dlee Moved from cicat.hxx // //---------------------------------------------------------------------------- void CiCat::RenameFileInternal( const CLowerFunnyPath & lcaseFunnyOldName, const CLowerFunnyPath & lcaseFunnyNewName, ULONG ulFileAttrib, VOLUMEID volumeId, FILEID fileId, WORKID widParent ) { BOOL fUsnVolume = IsOnUsnVolume( lcaseFunnyOldName.GetActualPath() ); WORKID wid; if ( fUsnVolume ) if ( fileIdInvalid == fileId ) { // Since the old file has been deleted, use the new filename, // which should have the same fileId wid = LokLookupWid( lcaseFunnyNewName, fileId); if ( widInvalid == wid ) { // Could not get a valid wid for the new file. Looks like // it has been deleted. Abort !!! return; } } else { wid = LokLookupWid( lcaseFunnyOldName, fileId); } else wid = _strings.LokFind( lcaseFunnyOldName.GetActualPath() ); ciDebugOut(( DEB_ITRACE, "renamefile %#I64x '%ws' to '%ws'\n", fileId, lcaseFunnyOldName.GetActualPath(), lcaseFunnyNewName.GetActualPath() )); _strings.LokRenameFile( lcaseFunnyOldName.GetActualPath(), lcaseFunnyNewName.GetActualPath(), wid, ulFileAttrib, volumeId, widParent ); } //RenameFile //+--------------------------------------------------------------------------- // // Member: CiCat::HasChanged, public // // Returns: TRUE if [ft] > last filter time // // Arguments: [wid] -- WorkId // [ft] -- Filetime // // History: 08-Apr-1998 KyleP Created // //---------------------------------------------------------------------------- BOOL CiCat::HasChanged( WORKID wid, FILETIME const & ft ) { PROPVARIANT propVar; if ( !_propstoremgr.ReadPrimaryProperty( wid, pidLastSeenTime, propVar ) || propVar.vt != VT_FILETIME ) RtlZeroMemory( &propVar.filetime, sizeof(propVar.filetime) ); return ( *(ULONGLONG *)&ft > *(ULONGLONG *)&propVar.filetime ); } //+------------------------------------------------------------------------- // // Member: CScopeEntry::CScopeEntry, public // // Synopsis: Ctor. // // Arguments: [pwszScope] -- scope entry to init CScopeEntry // // Notes: If the passed-in scope contains special chars, // only scope preceeding the special char is stored. // // History: 3-20-98 mohamedn created // //-------------------------------------------------------------------------- CScopeEntry::CScopeEntry(WCHAR const * pwszScope) : _fContainsSpecialChar(FALSE) { Win4Assert( 0 != pwszScope ); ULONG scopeLen = wcslen( pwszScope ); // // Terminate the path with a trailing backslash if not already terminated. // _xwcsPath.SetSize( scopeLen + 2 ); RtlCopyMemory( _xwcsPath.Get(), pwszScope, (scopeLen+1) * sizeof(WCHAR) ); TerminateWithBackSlash( _xwcsPath.Get(), scopeLen ); // // truncate paths with special chars. // WCHAR * pwszSpecial = GetSpecialCharLocation(); if ( pwszSpecial ) { *pwszSpecial = L'\0'; while ( pwszSpecial >= _xwcsPath.Get() && L'\\' != *pwszSpecial ) { pwszSpecial--; } Win4Assert( L'\\' == *pwszSpecial ); // // terminate substring. // *(pwszSpecial+1) = L'\0'; _fContainsSpecialChar = TRUE; ciDebugOut(( DEB_ITRACE, "CScopeEntry: %ws\n", _xwcsPath.Get() )); } _cclen = wcslen( _xwcsPath.Get() ); } //+------------------------------------------------------------------------- // // Member: CScopeEntry::SetToParentDirectory, public // // Synopsis: truncates current path to its parent. // // Arguments: none. // // returns: TRUE if path was set to parent, FALSE if root was reached. // // History: 3-20-98 mohamedn created // //-------------------------------------------------------------------------- BOOL CScopeEntry::SetToParentDirectory(void) { Win4Assert( _fContainsSpecialChar ); if ( IsRoot() ) { return FALSE; } Win4Assert( L'\\' == _xwcsPath[_cclen-1] ); _xwcsPath[_cclen-1] = L'\0'; // skip last backslash WCHAR * pwszTemp = wcsrchr( _xwcsPath.Get(), L'\\' ); *(pwszTemp+1) = L'\0'; _cclen = wcslen( _xwcsPath.Get() ); return TRUE; } //+--------------------------------------------------------------------------- // // Member: CScopeEntry::IsRoot, Private // // Synopsis: finds if current path is root path // // Arguments: none. // // Returns: TRUE if Path is root path, False otherwise. // // History: 3-30-98 mohamedn created // //---------------------------------------------------------------------------- BOOL CScopeEntry::IsRoot(void) { Win4Assert( _fContainsSpecialChar ); unsigned weight = 0; if ( _cclen > 3 && _xwcsPath[1] == L':' ) { weight = 3; } for ( WCHAR const * pwszTemp = _xwcsPath.Get(); *pwszTemp; pwszTemp++ ) { if ( L'\\' == *pwszTemp ) { weight += 1; } } if ( weight > 4 ) return FALSE; else return TRUE; } //+------------------------------------------------------------------------- // // Member: CScopeEntry::GetSpecialCharLocation, private // // Synopsis: Finds location of 1st special char of contained scope. // // Arguments: None. // // returns: pointer to regx wild card (if one found), null otherwise. // // History: 3-20-98 mohamedn created // //-------------------------------------------------------------------------- WCHAR * CScopeEntry::GetSpecialCharLocation() { for ( WCHAR * pwszTemp = _xwcsPath.Get(); *pwszTemp; pwszTemp++ ) { if ( IsSpecialChar(*pwszTemp) ) return pwszTemp; } return 0; } //+------------------------------------------------------------------------- // // Member: CScopeEntry::IsSpecialChar, private // // Synopsis: determines if input char is regex special char // // Arguments: [c] -- wchar to test // // returns: TRUE if input wchar is special regex wchar, FALSE otherwise. // // History: 3-20-98 mohamedn created // //-------------------------------------------------------------------------- BOOL CScopeEntry::IsSpecialChar(WCHAR c) { for ( WCHAR const * pwszTemp = awcSpecialRegex; *pwszTemp; pwszTemp++ ) { if ( c == *pwszTemp ) return TRUE; } return FALSE; } //+--------------------------------------------------------------------------- // // Class: XSmartNetApiBuffer // // Purpose: Frees buffer allocated by the network APIs // // History: 7/26/00 dlee created // //---------------------------------------------------------------------------- class XSmartNetApiBuffer { public: XSmartNetApiBuffer( CDynLoadNetApi32 & dlNetApi32, void * p ) : _dlNetApi32( dlNetApi32 ), _p( p ) {} ~XSmartNetApiBuffer() { _dlNetApi32.NetApiBufferFree( _p ); } private: CDynLoadNetApi32 & _dlNetApi32; void * _p; }; //+------------------------------------------------------------------------- // // Member: CiCat::AddShares, public // // Synopsis: Examines shared resources and adds to fixup list. // // Arguments: [dlNetApi32] -- Dynamically loaded NetApi32 DLL (for performance) // // History: 09-Jun-1998 KyleP Created // //-------------------------------------------------------------------------- void CiCat::AddShares( CDynLoadNetApi32 & dlNetApi32 ) { CDynLoadMpr dlMpr; HANDLE hEnum = (HANDLE)INVALID_HANDLE_VALUE; do { WCHAR wcsCompName[MAX_COMPUTERNAME_LENGTH+3] = L"\\\\"; DWORD ccCompName = sizeof(wcsCompName)/sizeof(WCHAR) - 2; if ( !GetComputerName( &wcsCompName[2], &ccCompName ) ) { ciDebugOut(( DEB_ERROR, "Error %u from GetComputerName\n", GetLastError() )); break; } // // Open the enumerator // NETRESOURCE nr; nr.dwScope = RESOURCE_GLOBALNET; nr.dwType = RESOURCETYPE_ANY; nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER; nr.dwUsage = RESOURCEUSAGE_CONTAINER; nr.lpLocalName = 0; nr.lpRemoteName = wcsCompName; nr.lpComment = 0; nr.lpProvider = 0; DWORD dwError = dlMpr.WNetOpenEnumW( RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, &nr, &hEnum); if ( dwError != NO_ERROR ) { ciDebugOut(( DEB_ERROR, "Error %u from WNetOpenEnum\n", dwError )); break; } // // Open key for reg refresh. // HKEY hkey = (HKEY)INVALID_HANDLE_VALUE; dwError = RegOpenKey( HKEY_LOCAL_MACHINE, _xScopesKey.Get() + SKIP_TO_LM, &hkey ); if ( ERROR_SUCCESS != dwError ) { ciDebugOut(( DEB_ERROR, "Error %d opening %ws\n", dwError, _xScopesKey.Get() )); break; } SRegKey xKey( hkey ); // // Enumerate all shares // XArray abBuffer( 4 * 1024 ); DWORD dwResult; do { NETRESOURCE * pnr = (NETRESOURCE *)abBuffer.GetPointer(); DWORD cbBuffer = abBuffer.SizeOf(); DWORD cEntries = 0xFFFFFFFF; dwResult = dlMpr.WNetEnumResourceW( hEnum, &cEntries, pnr, &cbBuffer ); if ( NO_ERROR == dwResult ) { for( unsigned i = 0; i < cEntries; i++ ) { // // Is this a disk share? // if ( pnr[i].dwType == RESOURCETYPE_DISK && pnr[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE ) { // // Find the local physical path. // BYTE * pbShareInfo; DWORD dwError = dlNetApi32.NetShareGetInfo( &wcsCompName[2], // Server pnr[i].lpRemoteName + ccCompName + 3, // Share 2, // Level 2 &pbShareInfo ); // Result if ( NO_ERROR == dwError ) { XSmartNetApiBuffer xbuf( dlNetApi32, pbShareInfo ); SHARE_INFO_2 * psi = (SHARE_INFO_2 *)pbShareInfo; Win4Assert( STYPE_DISKTREE == psi->shi2_type ); // // Is the alias already mapped correctly? // If it is, then the alias is correctly recorded // in the registry. If not, add it. // CLowcaseBuf lcase( psi->shi2_path ); if ( !_scopeFixup.IsExactMatch( psi->shi2_path, pnr[i].lpRemoteName ) ) { if ( _notify.IsInScope( lcase.Get() ) ) { // // Add scope to registry. // RefreshIfShadowAlias( psi->shi2_path, pnr[i].lpRemoteName, hkey ); } } else { if ( !_notify.IsInScope( lcase.Get() ) ) { // // Remove bogus shadow. // DeleteIfShadowAlias( psi->shi2_path, pnr[i].lpRemoteName ); } } } } } } } while( NO_ERROR == dwResult ); } while ( FALSE ); if ( hEnum != (HANDLE)INVALID_HANDLE_VALUE ) dlMpr.WNetCloseEnum( hEnum ); } //+--------------------------------------------------------------------------- // // Method: CiCat::SynchShares, public // // Synopsis: Adds/removes fixups to match net shares // // History: 15-May-97 KyleP Created // //---------------------------------------------------------------------------- void CiCat::SynchShares() { CDynLoadNetApi32 dlNetApi32; // // First, enumerate the registry and remove automatically added aliases that // No longer exist. // ciDebugOut(( DEB_ITRACE, "Reading scope fixups from '%ws'\n", GetScopesKey() )); BOOL fChanged; do { fChanged = FALSE; CRegAccess regScopes( RTL_REGISTRY_ABSOLUTE, GetScopesKey() ); CRegistryScopesCallBackRemoveAlias callback( *this, dlNetApi32, !_fAutoAlias ); TRY { regScopes.EnumerateValues( 0, callback ); fChanged = callback.WasScopeRemoved(); } CATCH( CException, e ) { ciDebugOut(( DEB_WARN, "Exception 0x%x caught groveling ci registry in SynchShares.\n", e.GetErrorCode() )); HandleError( e.GetErrorCode() ); } END_CATCH } while ( fChanged ); // // Now, enumerate the shares on the machine, and add all the aliases that // are not already in the list. // if ( _fAutoAlias ) AddShares( dlNetApi32 ); } // // Local constants // WCHAR const wcsShadowAlias[] = L",,41"; // ,,41 --> Shadow alias, Physical, Indexed //+--------------------------------------------------------------------------- // // Method: CiCat::DeleteIfShadowAlias, private // // Synopsis: Deletes shadow scope registry entry // // Arguments: [pwcsScope] -- Scope to delete // [pwcsAlias] -- Alias of [pwcsScope] // // History: 15-May-97 KyleP Created // // Notes: Only deletes exact matches (as stored by system) // //---------------------------------------------------------------------------- void CiCat::DeleteIfShadowAlias( WCHAR const * pwcsScope, WCHAR const * pwcsAlias ) { HKEY hkey = (HKEY)INVALID_HANDLE_VALUE; DWORD dwError = RegOpenKey( HKEY_LOCAL_MACHINE, _xScopesKey.Get() + SKIP_TO_LM, &hkey ); if ( ERROR_SUCCESS != dwError ) { ciDebugOut(( DEB_ERROR, "Error %d opening %ws\n", dwError, _xScopesKey.Get() )); } if ( ERROR_SUCCESS == dwError ) { SRegKey xKey( hkey ); unsigned ccAlias = wcslen(pwcsAlias); unsigned cbAlias = ccAlias * sizeof(WCHAR); // // See if this is a shadow entry (flags == 2) // WCHAR wcsValueData[MAX_PATH]; DWORD cbValueData = sizeof(wcsValueData); DWORD dwType; // // First, try the most obvious candidate (no '<#>' at end) // dwError = RegQueryValueEx( hkey, // Key handle pwcsScope, // Name 0, // Reserved &dwType, // Datatype (BYTE *)wcsValueData, // Data returned here &cbValueData ); // Size of data if ( ERROR_SUCCESS == dwError && REG_SZ == dwType && cbValueData == ( sizeof(wcsShadowAlias) + cbAlias ) && RtlEqualMemory( wcsValueData + cbValueData/sizeof(WCHAR) - sizeof(wcsShadowAlias)/sizeof(WCHAR), wcsShadowAlias, sizeof(wcsShadowAlias) ) && RtlEqualMemory( wcsValueData, pwcsAlias, cbAlias ) ) { dwError = RegDeleteValue( hkey, pwcsScope ); if ( ERROR_SUCCESS != dwError ) { ciDebugOut(( DEB_ERROR, "Error %d deleting %ws\n", dwError, pwcsScope )); } } // // Try it the hard way, via enumeration // else { DWORD dwIndex = 0; unsigned ccScope = wcslen(pwcsScope); do { WCHAR wcsValueName[MAX_PATH+4]; DWORD ccValueName = sizeof(wcsValueName)/sizeof(WCHAR); cbValueData = sizeof(wcsValueData); dwError = RegEnumValue( hkey, // handle of key to query dwIndex, // index of value to query wcsValueName, // address of buffer for value string &ccValueName, // address for size of value buffer 0, // reserved &dwType, // address of buffer for type code (BYTE *)wcsValueData, // address of buffer for value data &cbValueData ); // address for size of data buffer dwIndex++; if ( ERROR_SUCCESS == dwError && // Call succeeded ccValueName == (ccScope + 3) && // It's long enough (path + '<#>') wcsValueName[ccScope] == L'<' && // It's a special path RtlEqualMemory( wcsValueName, pwcsScope, ccScope * sizeof(WCHAR) ) && // To the right scope REG_SZ == dwType && // With a string value cbValueData == ( sizeof(wcsShadowAlias) + cbAlias ) && RtlEqualMemory( wcsValueData + ccAlias, wcsShadowAlias, sizeof(wcsShadowAlias) ) && // That *is* an auto-alias RtlEqualMemory( wcsValueData, pwcsAlias, cbAlias ) ) // To the right scope { dwError = RegDeleteValue( hkey, wcsValueName ); if ( ERROR_SUCCESS != dwError ) { ciDebugOut(( DEB_ERROR, "Error %d deleting %ws\n", dwError, wcsValueName )); } break; } } while (ERROR_SUCCESS == dwError ); } } } //+--------------------------------------------------------------------------- // // Method: CiCat::AddShadowAlias, private // // Synopsis: Adds shadow scope registry entry // // Arguments: [pwcsScope] -- Scope to add // [pwcsAlias] -- Alias // [iSlot] -- Empty <#> slot // [hkey] -- Registry key to catalog // // History: 15-May-97 KyleP Created // //---------------------------------------------------------------------------- void CiCat::AddShadowAlias( WCHAR const * pwcsScope, WCHAR const * pwcsAlias, unsigned iSlot, HKEY hkey ) { // // Build string: NAME: ,,41 // XGrowable xValue; unsigned ccAlias = wcslen(pwcsAlias); xValue.SetSize( ccAlias + sizeof(wcsShadowAlias)/sizeof(WCHAR) ); RtlCopyMemory( xValue.Get(), pwcsAlias, ccAlias * sizeof(WCHAR) ); RtlCopyMemory( xValue.Get() + ccAlias, wcsShadowAlias, sizeof(wcsShadowAlias) ); WCHAR wcsValueName[MAX_PATH+4]; unsigned ccScope = wcslen(pwcsScope); if ( ccScope + 4 > sizeof(wcsValueName)/sizeof(WCHAR) ) { ciDebugOut(( DEB_ERROR, "CiCat::AddShadowAlias: Scope too big (%ws)\n", pwcsScope )); } else { RtlCopyMemory( wcsValueName, pwcsScope, ccScope * sizeof(WCHAR) ); wcsValueName[ccScope] = L'<'; wcsValueName[ccScope+1] = iSlot + L'0'; wcsValueName[ccScope+2] = L'>'; wcsValueName[ccScope+3] = 0; DWORD dwError = RegSetValueEx( hkey, // Key wcsValueName, // Value name 0, // Reserved REG_SZ, // Type (BYTE *)xValue.Get(), // Data ccAlias * sizeof(WCHAR) + sizeof(wcsShadowAlias) ); // Size (in bytes) if ( ERROR_SUCCESS != dwError ) { ciDebugOut(( DEB_ERROR, "Error %d writing %ws\n", dwError, pwcsScope )); } } } //+--------------------------------------------------------------------------- // // Method: CiCat::RefreshIfShadow, private // // Synopsis: Refresh shadow scope registry entry (if blank) // // Arguments: [pwcsScope] -- Scope to refresh // [pwcsAlias] -- Alias // [hkey] -- Registry key to catalog // // History: 11-Oct-97 KyleP Created // // Notes: Only refresh blank (missing) entries // //---------------------------------------------------------------------------- void CiCat::RefreshIfShadowAlias( WCHAR const * pwcsScope, WCHAR const * pwcsAlias, HKEY hkey ) { unsigned Empty = 1000; // Just bigger than 9; unsigned ccAlias = wcslen(pwcsAlias); unsigned cbAlias = ccAlias * sizeof(WCHAR); // // See if this is a missing entry // WCHAR wcsData[MAX_PATH]; DWORD dwType; DWORD dwSize = sizeof(wcsData); DWORD dwError = RegQueryValueEx( hkey, // Key handle pwcsScope, // Name 0, // Reserved &dwType, // Datatype (BYTE *)wcsData, // Data returned here &dwSize ); // Size of data if ( ERROR_SUCCESS == dwError && REG_SZ == dwType && dwSize > (ccAlias * sizeof(WCHAR)) && wcsData[ccAlias] == L',' && RtlEqualMemory( pwcsAlias, wcsData, cbAlias ) ) Empty = 1000; else { WCHAR wcsValueName[MAX_PATH+4]; unsigned ccScope = wcslen(pwcsScope); RtlCopyMemory( wcsValueName, pwcsScope, ccScope * sizeof(WCHAR) ); wcsValueName[ccScope] = L'<'; //wcsValueName[ccScope+1] = L'1'; wcsValueName[ccScope+2] = L'>'; wcsValueName[ccScope+3] = 0; for ( unsigned i = 0; i < 10; i++ ) { wcsValueName[ccScope+1] = i + L'0'; dwSize = sizeof(wcsData); dwError = RegQueryValueEx( hkey, // Key handle wcsValueName, // Name 0, // Reserved &dwType, // Datatype (BYTE *)wcsData, // Data returned here &dwSize ); // Size of data if ( ERROR_FILE_NOT_FOUND == dwError && Empty > 9 ) Empty = i; if ( ERROR_SUCCESS == dwError && REG_SZ == dwType && dwSize > (ccAlias * sizeof(WCHAR)) && wcsData[ccAlias] == L',' && RtlEqualMemory( pwcsAlias, wcsData, cbAlias ) ) { Empty = 1000; break; } } } if ( Empty < 10 ) AddShadowAlias( pwcsScope, pwcsAlias, Empty, hkey ); } //+--------------------------------------------------------------------------- // // Member: CiCat::PropertyRecordToFileId, public // // Synopsis: Looks up the fileid and volumeid from a property record // // Arguments: [PropRec] -- The record to use for the reads // [fileId] -- Returns the fileid of the file // [volumeId] -- Returns the volumeid of the file // // Returns: TRUE if valid fileid and volumeid values were found // // History: 2-April-1998 dlee created // //---------------------------------------------------------------------------- BOOL CiCat::PropertyRecordToFileId( CCompositePropRecord & PropRec, FILEID & fileId, VOLUMEID & volumeId ) { PROPVARIANT var; _propstoremgr.ReadProperty( PropRec, pidFileIndex, var ); fileId = var.uhVal.QuadPart; if ( ( VT_UI8 == var.vt ) && ( fileIdInvalid != fileId ) ) { _propstoremgr.ReadProperty( PropRec, pidVolumeId, var ); volumeId = var.ulVal; ciDebugOut(( DEB_ITRACE, "propertyrecordtofileid, type %#x, volid %#x\n", var.vt, volumeId )); return ( ( VT_UI4 == var.vt ) && ( CI_VOLID_USN_NOT_ENABLED != volumeId ) ); } ciDebugOut(( DEB_ITRACE, "propertyrecordtofileid failed!\n" )); return FALSE; } //PropertyRecordToFileId //+------------------------------------------------------------------------- // // Member: CiCat::ClearNonStorageProperties, public // // Synopsis: write VT_EMPTY into the properties in the PropertyStores ( // except the storage properties, e.g. path, filename, etc.) // before a file is reindexed. Thus, if a property value is // deleted from the document, the old values will be wiped out. // // Arguments: [wid] -- Workid // // History: 06-Oct-2000 KitmanH Created // //-------------------------------------------------------------------------- void CiCat::ClearNonStoragePropertiesForWid( WORKID wid ) { if ( widInvalid != wid ) { XWriteCompositeRecord rec( _propstoremgr, wid ); _propstoremgr.ClearNonStorageProperties( rec.GetReference() ); } }