Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2648 lines
88 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997-1999.
//
// File: usnmgr.cxx
//
// Contents: Usn manager
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <eventlog.hxx>
#include <cievtmsg.h>
#include <smatch.hxx>
#include <cifrmcom.hxx>
#include "cicat.hxx"
#include "notifmgr.hxx"
#include "usnmgr.hxx"
#include "usntree.hxx"
//
// Local constants
//
unsigned const USN_LOG_DANGER_THRESHOLD = 50;
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::CUsnMgr
//
// Synopsis: Constructor
//
// Arguments: [cicat] -- Catalog
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
CUsnMgr::CUsnMgr( CiCat & cicat )
: _cicat(cicat),
_fAbort(FALSE),
_fBatch(FALSE),
#pragma warning( disable : 4355 ) // this used in base initialization
_thrUsn(UsnThread, this, TRUE),
#pragma warning( default : 4355 )
_fUpdatesDisabled(FALSE),
_fDoingRenameTraverse( FALSE ),
_waitForMultObj(1+RTL_MAX_DRIVE_LETTERS), // 1 for _evtUsn + RTL_MAX_DRIVE_LETTERS drives (a: to z: + few special chars)
_fWaitingForUpdates( FALSE )
{
_evtUsn.Reset();
_thrUsn.SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::~CUsnMgr
//
// Synopsis: Destructor
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
CUsnMgr::~CUsnMgr()
{
InitiateShutdown();
WaitForShutdown();
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::GetMaxUSNs, public
//
// Synopsis: Updates or adds entries to flushInfoList to reflect the max
// usn processed for the volumes.
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::GetMaxUSNs( CUsnFlushInfoList & flushInfoList )
{
for ( CUsnVolumeIter usnVolIter( _usnVolumesToMonitor );
!_usnVolumesToMonitor.AtEnd( usnVolIter );
_usnVolumesToMonitor.Advance( usnVolIter ) )
{
//
// Look for the volume and store the highest usn if found.
//
BOOL fFound = FALSE;
for ( unsigned i = 0; i < flushInfoList.Count(); i++ )
{
CUsnFlushInfo & info = * ( flushInfoList.Get( i ) );
if ( usnVolIter->VolumeId() == info.volumeId )
{
ciDebugOut(( DEB_ITRACE,
"GetMaxUSNs updating vol %wc from %#I64x to %#I64x\n",
usnVolIter->DriveLetter(),
info.usnHighest,
usnVolIter->MaxUsnRead() ));
info.usnHighest = usnVolIter->MaxUsnRead();
fFound = TRUE;
break;
}
}
//
// If the volume isn't in the list yet, add it.
//
if ( !fFound )
{
ciDebugOut(( DEB_ITRACE,
"GetMaxUSNs adding vol %wc %#I64x\n",
usnVolIter->DriveLetter(),
usnVolIter->MaxUsnRead() ));
XPtr<CUsnFlushInfo> xInfo( new CUsnFlushInfo( usnVolIter->VolumeId(),
usnVolIter->MaxUsnRead() ) );
flushInfoList.Add( xInfo.GetPointer(), i );
xInfo.Acquire();
}
}
} //GetMaxUSNs
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::AddScope
//
// Synopsis: Scans scope and then monitors scope for usn notifications
//
// Arguments: [pwcsScope] -- Scope
// [volumeId] -- Volume id
// [fDoDeletions] -- Should delete processing be done ?
// [usnStart] -- "High water mark" of previous activity
// [fFullScan] -- TRUE if we should start over from scratch
// [fUserInitiatedScan] -- TRUE if the user asked for the scan
// [fNewScope] -- TRUE if scope was just added to the catalog
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::AddScope( WCHAR const *pwcsScope,
VOLUMEID volumeId,
BOOL fDoDeletions,
USN const & usnStart,
BOOL fFullScan,
BOOL fUserInitiatedScan,
BOOL fNewScope )
{
Win4Assert( wcslen(pwcsScope) < MAX_PATH );
XPtr<CCiScanInfo> xScanInfo( QueryScanInfo( pwcsScope,
volumeId,
usnStart,
fDoDeletions,
fUserInitiatedScan,
fNewScope ) );
xScanInfo->SetScan();
if ( fFullScan )
xScanInfo->SetFullUsnScan();
ScanScope( xScanInfo, FALSE );
} //AddScope
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::MonitorScope
//
// Synopsis: Monitors scope for usn notifications, i.e. no scan is done
//
// Arguments: [pwcsScope] - Scope
// [volumeId] - Volume id
// [usnStart] - Usn to start monitoring from
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::MonitorScope( WCHAR const *pwcsScope,
VOLUMEID volumeId,
USN usnStart )
{
Win4Assert( wcslen(pwcsScope) < MAX_PATH );
XPtr<CCiScanInfo> xScanInfo( QueryScanInfo( pwcsScope,
volumeId,
usnStart,
FALSE ) );
xScanInfo->SetMonitorOnly();
ScanScope( xScanInfo, FALSE );
} //MonitorScope
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::RemoveScope
//
// Synopsis: Removes usn path from catalog
//
// Arguments: [pwscScope] - Scope to be removed
// [volumeId] - Volume id
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::RemoveScope( WCHAR const *pwcsScope, VOLUMEID volumeId )
{
//
// If the given scope is in the list of paths being currently
// scanned, we must mark it deleted.
//
BOOL fRemoved = FALSE;
{
CLock lock(_mutex);
if ( _fAbort || _fUpdatesDisabled )
return;
for ( CFwdScanInfoIter scanInfoIter1( _usnScansToDo );
!_usnScansToDo.AtEnd( scanInfoIter1 );
_usnScansToDo.Advance( scanInfoIter1 ) )
{
WCHAR const * pwcsPath = scanInfoIter1->GetPath();
if ( AreIdenticalPaths( pwcsScope, pwcsPath ) )
{
fRemoved = TRUE;
if ( !scanInfoIter1->LokIsDelScope() )
scanInfoIter1->LokSetDelScope();
}
}
if ( !fRemoved )
{
LokScheduleRemove( pwcsScope, volumeId );
fRemoved = TRUE;
}
{
CLock lock(_mutex);
if ( !_fBatch )
_evtUsn.Set(); // wake up the usn thread.
}
Win4Assert( fRemoved );
}
} //RemoveScope
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::ScanScope
//
// Synopsis: Scan the given scopes and then start monitoring for usn
// notifications
//
// Arguments: [xScanInfo] - Scan info
// [fRefiled] - Is this a refiled scan ?
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::ScanScope( XPtr<CCiScanInfo> & xScanInfo,
BOOL fRefiled )
{
CLock lock(_mutex);
#if CIDBG==1
//
// Debug code to track down the cause of _fUsnTreeScan assert
//
if ( xScanInfo->LokIsInScan() )
_cicat.CheckUsnTreeScan( xScanInfo->GetPath() );
#endif
ciDebugOut(( DEB_ITRACE, "CUsnMgr::ScanScope, retries %d\n",
xScanInfo->GetRetries() ));
if ( xScanInfo->GetRetries() > CCiScanInfo::MAX_RETRIES )
{
//
// Log event that scan failed, cleanup and then return
//
CEventLog eventLog( NULL, wcsCiEventSource );
CEventItem item( EVENTLOG_ERROR_TYPE,
CI_SERVICE_CATEGORY,
MSG_CI_CONTENTSCAN_FAILED,
1 );
item.AddArg( xScanInfo->GetPath() );
eventLog.ReportEvent( item );
CCiScanInfo * pScanInfo = xScanInfo.Acquire();
delete pScanInfo;
return;
}
if ( LokIsScanScheduled( xScanInfo ) )
{
ciDebugOut(( DEB_ITRACE, "CUsnMgr::ScanScope scan was already scheduled\n" ));
CCiScanInfo * pScanInfo = xScanInfo.Acquire();
delete pScanInfo;
}
else
{
Win4Assert( !xScanInfo->LokIsInFinalState() );
Win4Assert( xScanInfo->LokIsInScan()
|| xScanInfo->LokIsInFullUsnScan()
|| xScanInfo->LokIsDelScope()
|| xScanInfo->LokIsRenameDir()
|| xScanInfo->LokIsMonitorOnly() );
ciDebugOut(( DEB_ITRACE,
"CUsnMgr::ScanScope scan wasn't already scheduled, fRefiled: %d\n",
fRefiled ));
if ( fRefiled )
{
//
// A scan that has been refiled should be done before new scans to
// ensure that all scans are done in FIFO order
//
_usnScansToDo.Push( xScanInfo.GetPointer() );
}
else
_usnScansToDo.Queue( xScanInfo.GetPointer() );
xScanInfo.Acquire();
if ( !_fBatch )
_evtUsn.Set();
}
} //ScanScope
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::LokIsScanScheduled
//
// Synopsis: Tests if the given scope is already scheduled for a scan.
//
// Arguments: [xScanInfo] - Smart pointer to scaninfo
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
BOOL CUsnMgr::LokIsScanScheduled( const XPtr<CCiScanInfo> & xScanInfo )
{
WCHAR const * pwszNewScope = xScanInfo->GetPath();
unsigned lenNewScope = wcslen( pwszNewScope );
for ( CFwdScanInfoIter scanInfoIter(_usnScansToDo);
!_usnScansToDo.AtEnd(scanInfoIter);
_usnScansToDo.Advance(scanInfoIter) )
{
if ( xScanInfo->LokGetWorkType() == scanInfoIter->LokGetWorkType() )
{
WCHAR const * pwszPath = scanInfoIter->GetPath();
CScopeMatch scope( pwszPath, wcslen(pwszPath) );
if (scope.IsInScope( pwszNewScope, lenNewScope ))
{
ciDebugOut(( DEB_WARN,
"Usn scan already scheduled for (%ws)\n",
pwszNewScope ));
return TRUE;
}
}
}
return FALSE;
} //LokIsScanScheduled
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::QueryScanInfo, private
//
// Synopsis: Returns a new instance of CCiScanInfo
//
// Arguments: [pwcsScope] -- Scope
// [volumeId] -- Volume id
// [usnStart] -- Start usn
// [fDoDeletions] -- Shoud deletions be done ?
// [fUserInitiatedScan] -- TRUE if the user asked for the scan
// [fNewScope] -- TRUE if scope was just added to the catalog
//
// History: 05-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
CCiScanInfo * CUsnMgr::QueryScanInfo( WCHAR const * pwcsScope,
VOLUMEID volumeId,
USN usnStart,
BOOL fDoDeletions,
BOOL fUserInitiatedScan,
BOOL fNewScope )
{
Win4Assert( 0 != pwcsScope );
//
// Check for remote path
//
Win4Assert( pwcsScope[0] != L'\\' );
ULONG len = wcslen( pwcsScope );
Win4Assert( pwcsScope[len-1] == L'\\' );
XArray<WCHAR> xPath( len+1 );
WCHAR * pwcsLocal = xPath.Get();
RtlCopyMemory( pwcsLocal, pwcsScope, (len+1)*sizeof(WCHAR) );
CCiScanInfo * pScanInfo = new CCiScanInfo( xPath,
1,
UPD_FULL,
fDoDeletions,
volumeId,
usnStart,
fUserInitiatedScan,
fNewScope );
return pScanInfo;
} //QueryScanInfo
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::DisableUpdates
//
// Synopsis: Disables all usn updates
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::DisableUpdates()
{
CLock lock( _mutex );
_fUpdatesDisabled = TRUE;
_evtUsn.Set();
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::EnableUpdates
//
// Synopsis: Enables usn updates
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::EnableUpdates()
{
CLock lock( _mutex );
if ( _fUpdatesDisabled )
{
_fUpdatesDisabled = FALSE;
_evtUsn.Set();
}
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::InitiateShutdown
//
// Synopsis: Initiates the shutdown process
//
// History: 05-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::InitiateShutdown()
{
CLock lock(_mutex);
_fAbort = TRUE;
_fUpdatesDisabled = TRUE;
_usnScansToDo.Clear();
_evtUsn.Set();
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::WaitForShutdown
//
// Synopsis: Waits for shutdown to complete
//
// History: 05-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::WaitForShutdown()
{
//
// If we never started running, then just bail out.
//
if ( _thrUsn.IsRunning() )
{
ciDebugOut(( DEB_ITRACE, "Waiting for death of usn thread\n" ));
_thrUsn.WaitForDeath();
}
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::LokEmptyInProgressScans
//
// Synopsis: Removes all the scans from the "in-progress stack" and either
// deletes them or re-schedules them. If the scan is in its
// "terminal state", the scan is deleted. If there is a retry
// it will be re-scheduled.
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::LokEmptyInProgressScans()
{
//
// Refile any scopes that still need to be worked on
//
while ( _usnScansInProgress.Count() > 0 )
{
if ( _fAbort )
break;
XPtr<CCiScanInfo> xScanInfo( _usnScansInProgress.RemoveLast() );
if ( _fUpdatesDisabled )
{
CCiScanInfo * pScanInfo = xScanInfo.Acquire();
delete pScanInfo;
}
else if ( xScanInfo->LokIsInFinalState() )
{
//
// Scan of scope completed successfully, so start monitoring
//
LokAddScopeForUsnMonitoring( xScanInfo );
}
else
{
#if CIDBG==1
if ( xScanInfo->LokIsDelScope() )
{
ciDebugOut(( DEB_ITRACE,
"Requeing scope (%ws) for removal\n",
xScanInfo->GetPath() ));
}
else
{
ciDebugOut(( DEB_ITRACE,
"Requeuing scope (%ws) for scan\n",
xScanInfo->GetPath() ));
}
#endif // CIDBG==1
ScanScope( xScanInfo, TRUE ); // It's a refiled scan
}
}
} //LokEmptyInProgressScans
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::LokScheduleRemove
//
// Synopsis: Schedules a usn path for removal
//
// Arguments: [pwscScope] - Scope to be removed
// [volumeId] - Volume id
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::LokScheduleRemove( WCHAR const * pwcsScope,
VOLUMEID volumeId )
{
CCiScanInfo * pScanInfo = QueryScanInfo( pwcsScope,
volumeId,
0,
TRUE );
XPtr<CCiScanInfo> xScanInfo( pScanInfo );
pScanInfo->LokSetDelScope();
ScanScope( xScanInfo, FALSE );
}
//+---------------------------------------------------------------------------
//
// Member: CUnsMgr::UsnThread
//
// Synopsis: Usn thread start routine
//
// Arguments: [self] - This pointer
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
DWORD CUsnMgr::UsnThread( void * self )
{
SCODE sc = CoInitializeEx( 0, COINIT_MULTITHREADED );
((CUsnMgr *) self)->DoUsnProcessing();
CoUninitialize();
ciDebugOut(( DEB_ITRACE, "Terminating usn thread\n" ));
return 0;
} //UsnThread
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::DoUsnProcessing
//
// Synopsis: Main loop of usn thread
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::DoUsnProcessing()
{
CImpersonateSystem impersonate;
while ( TRUE )
{
//
// Don't do any work until the system has booted
//
while ( GetTickCount() < _cicat.GetRegParams()->GetStartupDelay() )
{
Sleep( 200 );
if ( _fAbort )
break;
}
NTSTATUS status = STATUS_SUCCESS;
TRY
{
XPtr<CCiScanInfo> xScanInfo;
//--------------------------------------------------------------------
{
CLock lock( _mutex );
if ( _fAbort )
break;
if ( !_fBatch )
{
//
// If requests are being batched, then don't process
// requests until the _fBatch flag is cleared.
//
// Refile any incomplete paths that could not be processed due to
// low resources
//
if ( _usnScansInProgress.Count() > 0 )
LokEmptyInProgressScans();
while ( _usnScansToDo.Count() > 0 )
{
xScanInfo.Set( _usnScansToDo.Pop() );
if ( _fUpdatesDisabled )
{
delete xScanInfo.Acquire();
break;
}
else
{
Win4Assert( !xScanInfo->LokIsInFinalState() );
_usnScansInProgress.Queue( xScanInfo.GetPointer() );
xScanInfo.Acquire();
}
}
if ( _fUpdatesDisabled )
{
//
// Empty usn lists, because usns notifications
// are not needed until updates are enabled.
//
_usnScansToDo.Clear();
_usnScansInProgress.Clear();
_usnVolumesToMonitor.Clear();
}
_evtUsn.Reset();
}
}
//--------------------------------------------------------------------
if ( _usnScansInProgress.Count() > 0 )
{
DoUsnScans();
Win4Assert( _usnScansInProgress.Count() == 0
|| _fAbort
|| _fUpdatesDisabled );
}
//
// ProcessUsnNotifications waits for usn notifications and processes
// them, or it waits until evtUsn is set. Only do this if there
// are no more scans to do.
//
if ( 0 == _usnScansToDo.Count() )
ProcessUsnNotifications();
}
CATCH (CException, e)
{
ciDebugOut(( DEB_ERROR,
"CUsnMgr::DoUsnProcessing, caught exception 0x%X\n",
e.GetErrorCode() ));
status = e.GetErrorCode();
}
END_CATCH
if ( status == CI_CORRUPT_CATALOG || status == CI_CORRUPT_DATABASE )
{
//
// Disable further updates until corruption is cleared
//
CLock lock( _mutex );
_fUpdatesDisabled = TRUE;
}
}
} //DoUsnProcessing
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::DoUsnScans
//
// Synopsis: Does tree walk for any usn scopes that were added
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::DoUsnScans()
{
//
// No need for a lock because only this thread modifies _usnScansInProgress
//
for ( CFwdScanInfoIter scanInfoIter(_usnScansInProgress);
!_usnScansInProgress.AtEnd(scanInfoIter);
_usnScansInProgress.Advance(scanInfoIter) )
{
if ( _fAbort || _fUpdatesDisabled )
break;
CCiScanInfo *pScanInfo = scanInfoIter.GetEntry();
Win4Assert( pScanInfo->GetRetries() <= CCiScanInfo::MAX_RETRIES );
NTSTATUS status = STATUS_SUCCESS;
TRY
{
ICiManager *pCiManager = _cicat.CiManager();
if ( pScanInfo->LokIsDelScope() )
{
_cicat.RemovePathsFromCiCat( pScanInfo->GetPath(), eUsnsArray );
if ( !_fAbort && !_fUpdatesDisabled )
{
SCODE sc = pCiManager->FlushUpdates();
if ( FAILED(sc) )
THROW( CException( sc ) );
pScanInfo->LokSetDone();
}
}
else if ( pScanInfo->LokIsMonitorOnly() )
{
//
// The scope needs to be monitored only and so set it to done
//
pScanInfo->LokSetDone();
}
else
{
//
// A starting USN from 0 means this is a first scan, or a
// complete recovery. A starting USN > 0 means we are
// looking for items changed by NT4 and not recorded in
// the USN log.
//
USN usnStart = 0;
//
// A full scan is executed after a volume has been reformatted.
// We first wipe all existing data (which will have incorrect
// fileid <--> wid mappings) and then do the scan.
//
if ( pScanInfo->LokIsInFullUsnScan() )
{
_cicat.RemovePathsFromCiCat( pScanInfo->GetPath(),
eUsnsArray );
if ( !_fAbort && !_fUpdatesDisabled )
{
SCODE sc = pCiManager->FlushUpdates();
if ( FAILED(sc) )
THROW( CException( sc ) );
}
}
else
usnStart = pScanInfo->UsnStart();
ciDebugOut(( DEB_ITRACE, "scan usnstart: %#I64x\n",
pScanInfo->UsnStart() ));
//
// Use the end of the usn log or the last usn processed by
// CI for this volume if available.
//
USN usnMax = FindCurrentMaxUsn( pScanInfo->GetPath() );
{
ciDebugOut(( DEB_ITRACE,
"usn scan looking for better maxusn than %#I64x\n",
usnMax ));
for ( CUsnVolumeIter usnVolIter( _usnVolumesToMonitor );
!_usnVolumesToMonitor.AtEnd( usnVolIter );
_usnVolumesToMonitor.Advance( usnVolIter ) )
{
if ( usnVolIter->VolumeId() == pScanInfo->VolumeId() )
{
ciDebugOut(( DEB_ITRACE,
"usn scan using %#I64x instead of %#I64x\n",
usnVolIter->MaxUsnRead(),
usnMax ));
usnMax = usnVolIter->MaxUsnRead();
break;
}
}
}
pScanInfo->SetStartUsn( usnMax );
CLowerFunnyPath lcaseFunnyPath;
lcaseFunnyPath.SetPath( pScanInfo->GetPath() );
CUsnTreeTraversal usnTreeTrav( _cicat,
*this,
*pCiManager,
lcaseFunnyPath,
pScanInfo->IsDoDeletions(),
_fUpdatesDisabled,
TRUE, // Process root
pScanInfo->VolumeId(),
usnStart,
usnMax,
pScanInfo->IsUserInitiated() );
usnTreeTrav.EndProcessing();
//
// Flush updates, because we will be writing usnMax as the
// restart usn for this scope, and we want to make sure that
// all updates with usn of 0 are serialized before writing
// the restart usn.
//
if ( !_fAbort && !_fUpdatesDisabled )
{
SCODE sc = pCiManager->FlushUpdates();
if ( FAILED(sc) )
THROW( CException( sc ) );
_cicat.SetUsnTreeScanComplete( pScanInfo->GetPath(), usnMax );
//
// Make SetUsnTreeScanComplete info persistent by
// serializing the scope table.
//
_cicat.ScheduleSerializeChanges();
pScanInfo->LokSetDone();
}
}
}
CATCH( CException, e)
{
ciDebugOut(( DEB_ERROR, "Exception 0x%x caught in CUsnMgr::DoUsnScans\n", e.GetErrorCode() ));
status = e.GetErrorCode();
}
END_CATCH
if ( status != STATUS_SUCCESS )
{
_cicat.HandleError( status );
break;
}
}
//
// Requeue failed usn scans
//
while ( _usnScansInProgress.Count() > 0 )
{
XPtr<CCiScanInfo> xScanInfo( _usnScansInProgress.RemoveLast() );
if ( _fUpdatesDisabled )
{
continue;
}
else if ( xScanInfo->LokIsInFinalState() )
{
if ( xScanInfo->LokIsDelScope() )
{
CLock lock( _mutex );
LokRemoveScopeFromUsnMonitoring( xScanInfo );
//
// All processing for a delete scope has been completed
//
CCiScanInfo *pScanInfo = xScanInfo.Acquire();
delete pScanInfo;
}
else
{
//
// Start monitoring for changes on this scope
//
CLock lock( _mutex );
LokAddScopeForUsnMonitoring( xScanInfo );
}
}
else
{
//
// We couldn't complete the scan for this, and so refile
//
xScanInfo->SetStartUsn( 0 );
xScanInfo->IncrementRetries();
xScanInfo->SetDoDeletions();
ScanScope( xScanInfo, TRUE );
}
}
} //DoUsnScans
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::LokAddScopeForUsnMonitoring
//
// Synopsis: Registers a scope for usn notifications
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::LokAddScopeForUsnMonitoring( XPtr<CCiScanInfo> & xScanInfo )
{
WCHAR const *pwszPath = xScanInfo->GetPath();
TRY
{
//
// Check that it is not a remote path
//
Win4Assert( pwszPath[0] != L'\\' );
for ( CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
!_usnVolumesToMonitor.AtEnd(usnVolIter);
_usnVolumesToMonitor.Advance(usnVolIter) )
{
if ( usnVolIter->DriveLetter() == pwszPath[0] )
{
//
// Add the scope to the list of scopes being monitored
// on this drive, if it is not already being monitored.
//
CScanInfoList & usnScopeList = usnVolIter->GetUsnScopesList();
for ( CFwdScanInfoIter scanIter1(usnScopeList);
!usnScopeList.AtEnd(scanIter1);
usnScopeList.Advance(scanIter1) )
{
if ( AreIdenticalPaths( scanIter1->GetPath(), xScanInfo->GetPath() ) )
return;
CScopeMatch superScope( scanIter1->GetPath(), wcslen( scanIter1->GetPath() ) );
if ( superScope.IsInScope( xScanInfo->GetPath(), wcslen( xScanInfo->GetPath() ) ) )
return;
}
//
// Remove subscopes as an optimization
//
CScopeMatch superScope( xScanInfo->GetPath(), wcslen( xScanInfo->GetPath() ) );
CFwdScanInfoIter scanIter2(usnScopeList);
while ( !usnScopeList.AtEnd(scanIter2) )
{
CCiScanInfo *pScanInfo = scanIter2.GetEntry();
usnScopeList.Advance(scanIter2);
if ( superScope.IsInScope( pScanInfo->GetPath(), wcslen( pScanInfo->GetPath() ) ) )
{
usnScopeList.RemoveFromList( pScanInfo );
delete pScanInfo;
}
}
if ( xScanInfo->UsnStart() < usnVolIter->MaxUsnRead() )
{
usnVolIter->SetMaxUsnRead( xScanInfo->UsnStart() );
//
// Cancel pending fsctl (if any) to pick up earlier usn
// notifications
//
usnVolIter->CancelFsctl();
}
usnScopeList.Push( xScanInfo.Acquire() );
return;
}
}
CUsnVolume *pUsnVolume = new CUsnVolume( pwszPath[0], xScanInfo->VolumeId() );
pUsnVolume->SetMaxUsnRead( xScanInfo->UsnStart() );
_usnVolumesToMonitor.Push( pUsnVolume );
CScanInfoList &usnScopeList = pUsnVolume->GetUsnScopesList();
usnScopeList.Push( xScanInfo.Acquire() );
}
CATCH( CException, e )
{
ciDebugOut(( DEB_ERROR, "Error 0x%x trying to monitor USN scope %wc.\n", e.GetErrorCode(), xScanInfo->VolumeId() ));
HandleError( pwszPath[0], e.GetErrorCode() );
}
END_CATCH
} //LokAddScopeForUsnMonitoring
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::LokRemoveScopeFromUsnMonitoring
//
// Synopsis: Deregisters a scope from usn notifications
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::LokRemoveScopeFromUsnMonitoring( XPtr<CCiScanInfo> & xScanInfo )
{
WCHAR const *pwszPath = xScanInfo->GetPath();
//
// Check that it is not a remote path
//
Win4Assert( pwszPath[0] != L'\\' );
for ( CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
!_usnVolumesToMonitor.AtEnd(usnVolIter);
_usnVolumesToMonitor.Advance(usnVolIter) )
{
if ( usnVolIter->DriveLetter() == pwszPath[0] )
{
CScanInfoList & usnScopeList = usnVolIter->GetUsnScopesList();
CFwdScanInfoIter scanInfoIter(usnScopeList);
while ( !usnScopeList.AtEnd(scanInfoIter) )
{
CCiScanInfo *pScanInfo = scanInfoIter.GetEntry();
usnScopeList.Advance(scanInfoIter);
if ( AreIdenticalPaths( pScanInfo->GetPath(), xScanInfo->GetPath() ) )
{
usnScopeList.RemoveFromList( pScanInfo );
delete pScanInfo;
break;
}
}
//
// If no more scopes on this usn volume then remove the entry from the
// list of volumes being monitored.
//
if ( usnScopeList.Count() == 0 )
{
CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
_usnVolumesToMonitor.RemoveFromList( pUsnVolume );
delete pUsnVolume;
break;
}
}
}
} //LokRemoveScopeFromUsnMonitoring
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::ProcessNotifications
//
// Synopsis: Wait for usn notifications, and then process them
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::ProcessUsnNotifications()
{
//
// No need for a lock because only this thread modifies _usnVolumesToMonitor
//
for (;;)
{
//
// Loop by waiting for usn notifications and processing them.
// Loop returns when _evtUsn is signalled.
//
BOOL fWait; // TRUE --> Every volume is now waiting. (assigned below)
BOOL fLowResource;
do
{
_waitForMultObj.ResetCount();
//
// _evtUsn is the first handle, and so it gets priority if more
// than one event is signalled.
//
_waitForMultObj.AddEvent( _evtUsn.GetHandle() );
CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
//
// Are we in a high i/o state? If so, don't read the USN Journal
//
fLowResource = IsLowResource();
fWait = TRUE;
while ( !_usnVolumesToMonitor.AtEnd(usnVolIter) )
{
if ( _fAbort || _fUpdatesDisabled )
return;
CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
_usnVolumesToMonitor.Advance(usnVolIter);
if ( !pUsnVolume->IsOnline() )
continue;
ciDebugOut(( DEB_ITRACE, "Drive %wc: %u%% read\n", pUsnVolume->DriveLetter(), pUsnVolume->PercentRead() ));
if ( fLowResource && pUsnVolume->PercentRead() >= USN_LOG_DANGER_THRESHOLD )
continue;
//
// Does this volume have something to do now?
//
if ( pUsnVolume->FFsctlPending() && WAIT_TIMEOUT != pUsnVolume->WaitFsctl( 0 ) )
pUsnVolume->ResetFsctlPending();
if ( pUsnVolume->FFsctlPending() )
_waitForMultObj.AddEvent( pUsnVolume->GetFsctlEvent() );
else
{
NTSTATUS status = ProcessUsnNotificationsFromVolume( pUsnVolume );
switch( status )
{
case STATUS_PENDING:
pUsnVolume->SetFsctlPending();
_waitForMultObj.AddEvent( pUsnVolume->GetFsctlEvent() );
break;
case STATUS_JOURNAL_ENTRY_DELETED:
break;
case STATUS_SUCCESS:
fWait = FALSE; // More to do, processing bailed to enable round-robin
break;
default:
Win4Assert( !NT_SUCCESS( status ) );
HandleError( pUsnVolume, status );
break;
}
}
}
} while( !fWait );
if ( _fAbort )
return;
//
// Wait for evtUsn to be signaled or for an usn notification
//
DWORD dwTimeout = _cicat.GetRegParams()->GetUsnReadTimeout() * 1000;
//
// But only wait forever if we're actually watching notifications...
//
if ( 0 == dwTimeout )
{
if ( fLowResource )
dwTimeout = 60 * 1000;
else
dwTimeout = INFINITE;
}
//
// If we're in a low resource situation, this is a good place to flush
// the working set.
//
if ( fLowResource && _cicat.GetRegParams()->GetMinimizeWorkingSet() )
SetProcessWorkingSetSize( GetCurrentProcess(), ~0, ~0 );
_fWaitingForUpdates = TRUE;
DWORD dwIndex = _waitForMultObj.Wait( dwTimeout );
_fWaitingForUpdates = FALSE;
if ( _fAbort || _fUpdatesDisabled )
return;
if ( WAIT_FAILED == dwIndex )
{
Win4Assert( !"Waitformultipleobjects failed" );
ciDebugOut(( DEB_ERROR, "WaitForMultipleObjects failed, 0x%x", GetLastError() ));
THROW( CException( ) );
}
if ( WAIT_OBJECT_0 == dwIndex )
{
//
// _evtUsn has been signaled -- cancel pending requests
//
CUsnVolumeIter usnVolIter( _usnVolumesToMonitor );
while ( !_usnVolumesToMonitor.AtEnd(usnVolIter) )
{
CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
_usnVolumesToMonitor.Advance(usnVolIter);
pUsnVolume->CancelFsctl();
}
return;
}
else if ( WAIT_TIMEOUT != dwIndex )
{
//
// Process usn notifications from the volume that was signaled.
// GetIth does a linear lookup, it can be optimized to index
// directly into an array of pUsnVolumes.
//
unsigned i = dwIndex - WAIT_OBJECT_0 - 1;
CUsnVolume *pUsnVolume = _usnVolumesToMonitor.GetIth( i );
//
// We know the volume is at least the Ith volume, but it may be
// farther if some volumes were skipped.
//
i++; // Account for control event
while ( pUsnVolume->GetFsctlEvent() != _waitForMultObj.Get(i) )
pUsnVolume = (CUsnVolume *)pUsnVolume->Next();
pUsnVolume->ResetFsctlPending();
ciDebugOut(( DEB_ITRACE, "PENDING COMPLETED #1 (0x%x)\n", pUsnVolume ));
if ( !fLowResource || pUsnVolume->PercentRead() < USN_LOG_DANGER_THRESHOLD )
ProcessUsnLogRecords( pUsnVolume );
if ( _fAbort || _fUpdatesDisabled )
return;
}
if ( 0 != dwTimeout && !fLowResource )
{
//
// Check to see if there is anything that's sat in the buffer too long.
//
LONGLONG ftExpired;
GetSystemTimeAsFileTime( (FILETIME *)&ftExpired );
ftExpired -= _cicat.GetRegParams()->GetUsnReadTimeout() * 10000i64;
CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
while( !_usnVolumesToMonitor.AtEnd(usnVolIter) )
{
if ( _fAbort || _fUpdatesDisabled )
break;
CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
_usnVolumesToMonitor.Advance(usnVolIter);
if ( !pUsnVolume->IsOnline() )
continue;
if ( pUsnVolume->FFsctlPending() && pUsnVolume->PendingTime() <= ftExpired )
{
pUsnVolume->CancelFsctl();
ciDebugOut(( DEB_ITRACE, "PENDING CANCELLED #1 (0x%x)\n", pUsnVolume ));
NTSTATUS status = ProcessUsnNotificationsFromVolume( pUsnVolume, TRUE ); // TRUE --> Immediate
switch( status )
{
case STATUS_PENDING:
pUsnVolume->SetFsctlPending();
break;
case STATUS_JOURNAL_ENTRY_DELETED:
break;
case STATUS_SUCCESS:
fWait = FALSE; // More to do, processing bailed to enable round-robin
break;
default:
Win4Assert( !NT_SUCCESS( status ) );
HandleError( pUsnVolume, status );
break;
}
}
}
}
}
} //ProcessUsnNotifications
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::ProcessUsnNotificationsFromVolume
//
// Synopsis: Process usn notifications from given volume
//
// Arguments: [pUsnVolume] -- Usn volume
// [fImmediate] -- If TRUE, look for *any* new data in log,
// no matter how little.
// [fWait] -- If TRUE, wait for data if fImmediate is TRUE
// and no data is available.
//
//
// Returns: STATUS_PENDING when there are no more usn notifications to be
// processed, or STATUS_JOURNAL_ENTRY_DELETED when usn records
// have been removed from log due to shrink from front.
//
// History: 05-Jul-97 SitaramR Created
//
//----------------------------------------------------------------------------
NTSTATUS CUsnMgr::ProcessUsnNotificationsFromVolume( CUsnVolume * pUsnVolume,
BOOL fImmediate,
BOOL fWait )
{
Win4Assert( pUsnVolume->IsOnline() );
//
// No need for a lock because only this thread modifies _usnVolumesToMonitor
//
Win4Assert( !pUsnVolume->FFsctlPending() );
NTSTATUS status;
unsigned cRetries = 0; // Unsuccessful read attempts
unsigned cTries = 0; // Successful read attempts
READ_USN_JOURNAL_DATA usnReadData;
usnReadData.UsnJournalID = pUsnVolume->JournalId();
usnReadData.StartUsn = pUsnVolume->MaxUsnRead();
usnReadData.ReasonMask = ~(USN_REASON_RENAME_OLD_NAME | USN_REASON_COMPRESSION_CHANGE);
usnReadData.ReturnOnlyOnClose = TRUE;
usnReadData.Timeout = 0;
do
{
if ( _fAbort || _fUpdatesDisabled )
{
//
// Doesn't matter what status we return because all notifications
// will be turned off soon.
//
return STATUS_TOO_LATE;
}
//
// In immediate mode, read *any* data, else read as requested by client.
//
if ( fImmediate )
usnReadData.BytesToWaitFor = 1;
else
usnReadData.BytesToWaitFor = _cicat.GetRegParams()->GetUsnReadMinSize();
#if CIDBG == 1
RtlFillMemory( pUsnVolume->GetBuffer(), pUsnVolume->GetBufferSize(), 0x11 );
#endif
ciDebugOut(( DEB_ITRACE, "READ #1 (0x%x)\n", pUsnVolume ));
Win4Assert( !pUsnVolume->FFsctlPending() );
ciDebugOut(( DEB_ITRACE, "reading usns from %#I64x\n", usnReadData.StartUsn ));
status = NtFsControlFile( pUsnVolume->VolumeHandle(),
pUsnVolume->GetFsctlEvent(),
NULL,
NULL,
pUsnVolume->IoStatusBlock(),
FSCTL_READ_USN_JOURNAL,
&usnReadData,
sizeof(usnReadData),
pUsnVolume->GetBuffer(),
pUsnVolume->GetBufferSize() );
if ( fImmediate )
{
if ( fWait )
fImmediate = FALSE;
//
// STATUS_PENDING --> nothing in the log. Go back to regular waits.
//
if ( STATUS_PENDING == status )
{
ciDebugOut(( DEB_ITRACE, "PENDING CANCELLED #2 (0x%x)\n", pUsnVolume ));
pUsnVolume->SetFsctlPending();
pUsnVolume->CancelFsctl();
// If no journal entries are available and we're not supposed
// to wait, return now.
if ( !fWait )
return STATUS_SUCCESS;
continue;
}
}
if ( STATUS_PENDING == status )
break;
if ( NT_SUCCESS( status ) )
status = pUsnVolume->IoStatusBlock()->Status;
if ( NT_SUCCESS( status ) )
{
ciDebugOut(( DEB_ITRACE, "IMMEDIATE COMPLETED #1 (0x%x)\n", pUsnVolume ));
ProcessUsnLogRecords( pUsnVolume );
usnReadData.StartUsn = pUsnVolume->MaxUsnRead();
Win4Assert( usnReadData.ReasonMask == ~(USN_REASON_RENAME_OLD_NAME | USN_REASON_COMPRESSION_CHANGE) );
Win4Assert( usnReadData.ReturnOnlyOnClose == TRUE );
//
// Don't read a single volume too long...
//
cTries++;
if ( cTries > 5 )
{
ciDebugOut(( DEB_ITRACE, "Stopping read on drive %wc: to support round-robin\n", pUsnVolume->DriveLetter() ));
break;
}
else
continue;
}
else if ( STATUS_JOURNAL_ENTRY_DELETED == status )
{
ciDebugOut(( DEB_ERROR, "STATUS_JOURNAL_ENTRY_DELETED #1 fWait %d, (drive %wc:)\n",
fWait, pUsnVolume->DriveLetter() ));
// Already doing a scan due to missed notifications.
if ( !fWait )
return STATUS_JOURNAL_ENTRY_DELETED;
//
// STATUS_JOURNAL_ENTRY_DELETED is returned when usn records have
// been deallocated from the usn journal. This means that we have to
// do a scan before continuing usn monitoring. Even if some other
// error code has been returned, the simplest recovery is to restart
// monitoring.
//
XBatchUsnProcessing xBatchUsns( *this );
CScanInfoList & usnScopeList = pUsnVolume->GetUsnScopesList();
while ( usnScopeList.Count() > 0 )
{
XPtr<CCiScanInfo> xScanInfo( usnScopeList.Pop() );
xScanInfo->SetStartState();
xScanInfo->SetScan();
ciDebugOut(( DEB_ERROR, "journal_entry_deleted, filing a scan for %ws\n",
xScanInfo->GetPath() ));
xScanInfo->SetStartUsn( pUsnVolume->MaxUsnRead() );
xScanInfo->SetDoDeletions();
_cicat.InitUsnTreeScan( xScanInfo->GetPath() );
ScanScope( xScanInfo, TRUE );
}
_usnVolumesToMonitor.RemoveFromList( pUsnVolume );
delete pUsnVolume;
return STATUS_JOURNAL_ENTRY_DELETED;
}
else
{
ciDebugOut(( DEB_WARN, "Usn read fsctl returned 0x%x\n", status ));
cRetries++;
if ( cRetries > 10 )
{
ciDebugOut(( DEB_WARN, "Giving up on USN read.\n" ));
break;
}
// the fsctl failed -- so try again
continue;
}
} while ( status != STATUS_PENDING );
return status;
} //ProcessUsnNotificationsFromVolume
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::ProcessUsnLogRecords
//
// Synopsis: Process usn notifications from usn records in buffer
//
// Arguments: [pUsnVolume] -- Usn volume that contains the buffer
//
// History: 05-Jul-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::ProcessUsnLogRecords( CUsnVolume *pUsnVolume )
{
Win4Assert( pUsnVolume->IsOnline() );
//
// No need for a lock because only this thread modifies _usnVolumesToMonitor
//
USN usnNextStart;
USN_RECORD * pUsnRec = 0;
ciDebugOut(( DEB_ITRACE, "process usns, Status 0x%x, Bytes %d\n",
pUsnVolume->IoStatusBlock()->Status,
pUsnVolume->IoStatusBlock()->Information ));
if ( !NT_SUCCESS( pUsnVolume->IoStatusBlock()->Status ) )
{
//
// If we cancelled the notification request (usually because
// we're going into a mode where we wait for a lot of bytes instead
// of just 1 byte), just ignore processing the request.
//
if ( STATUS_CANCELLED != pUsnVolume->IoStatusBlock()->Status )
{
ciDebugOut(( DEB_WARN, "Error 0x%x reading USN Journal\n", pUsnVolume->IoStatusBlock()->Status ));
Win4Assert( STATUS_INVALID_PARAMETER != pUsnVolume->IoStatusBlock()->Status );
}
return;
}
DWORD dwByteCount = (DWORD)pUsnVolume->IoStatusBlock()->Information;
if ( dwByteCount != 0 )
{
usnNextStart = *(USN *)pUsnVolume->GetBuffer();
pUsnRec = (USN_RECORD *)((PCHAR)pUsnVolume->GetBuffer() + sizeof(USN));
dwByteCount -= sizeof(USN);
}
if ( 0 == pUsnRec )
return;
ICiManager *pCiManager = _cicat.CiManager();
Win4Assert( pCiManager );
while ( dwByteCount != 0 )
{
if ( _fAbort || _fUpdatesDisabled )
return;
//
// Check that usn's from journal are greater than the start usn
//
#if CIDBG == 1
if ( pUsnRec->Usn < pUsnVolume->MaxUsnRead() )
{
ciDebugOut(( DEB_ERROR,
"volume %wc pUsnRec = 0x%x, USN = %#I64x, Max USN = %#I64x\n",
pUsnVolume->VolumeId(), pUsnRec, pUsnRec->Usn,
pUsnVolume->MaxUsnRead() ));
Win4Assert( pUsnRec->Usn >= pUsnVolume->MaxUsnRead() );
Sleep( 30 * 1000 );
}
Win4Assert( pUsnRec->Usn >= pUsnVolume->MaxUsnRead() );
if ( pUsnRec->RecordLength > 10000 )
{
ciDebugOut(( DEB_ERROR, "pUsnRec = 0x%x, RecordLength = %u\n", pUsnRec, pUsnRec->RecordLength ));
}
Win4Assert( pUsnRec->RecordLength <= 10000 );
#endif
if ( 0 == (pUsnRec->SourceInfo & (USN_SOURCE_AUXILIARY_DATA | USN_SOURCE_DATA_MANAGEMENT)) &&
( 0 == (pUsnRec->FileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) ||
0 != (pUsnRec->Reason & USN_REASON_INDEXABLE_CHANGE) ) )
{
//
// Indexable bit==0 means index the file, because the bit is set to 0
// by default on an upgrade from Ntfs 4.0 to Ntfs 5.0. We want the
// the default behavior to be index-all-files to ensure backward
// compatibility.
//
if ( pUsnRec->Reason & USN_REASON_RENAME_NEW_NAME )
{
CLowerFunnyPath lcaseFunnyOldPath;
BOOL fOldPathInScope;
_cicat.FileIdToPath( pUsnRec->FileReferenceNumber,
pUsnVolume,
lcaseFunnyOldPath,
fOldPathInScope );
CLowerFunnyPath lcaseFunnyNewPath;
BOOL fNewPathInScope;
WORKID widParent;
#if CIDBG == 1
widParent = widUnused;
#endif
//
// Try hard to find a parent directory, since it may
// not be in the index if it is marked as not-indexed.
//
_cicat.UsnRecordToPathUsingParentId( pUsnRec,
pUsnVolume,
lcaseFunnyNewPath,
fNewPathInScope,
widParent,
TRUE );
Win4Assert( widUnused != widParent );
if ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
//
// Directory rename or add or delete. Directory operations are
// (re)tried a certain number of times, after which a failure
// event is logged.
//
const MAX_RETRIES = 5;
XGrowable<WCHAR> xFileName;
for (unsigned i=0; i<MAX_RETRIES; i++)
{
TRY
{
if ( fOldPathInScope )
{
if ( fNewPathInScope )
{
ciDebugOut(( DEB_USN, "Renaming directory %ws to %ws\n", lcaseFunnyOldPath.GetPath(), lcaseFunnyNewPath.GetPath() ));
CRenameDir( _cicat,
lcaseFunnyOldPath,
lcaseFunnyNewPath,
_fUpdatesDisabled,
pUsnVolume->VolumeId() );
_cicat.Update( lcaseFunnyNewPath,
pUsnRec->FileReferenceNumber,
widParent,
pUsnRec->Usn,
pUsnVolume,
FALSE );
break;
}
else
{
CLowcaseBuf lcaseOldPath( lcaseFunnyOldPath.GetActualPath() );
lcaseOldPath.AppendBackSlash();
ciDebugOut(( DEB_USN, "Removing directory %ws\n", lcaseOldPath.Get() ));
_cicat.RemovePathsFromCiCat( lcaseOldPath.Get(), eUsnsArray );
if ( _fAbort || _fUpdatesDisabled )
{
return;
}
SCODE sc = pCiManager->FlushUpdates();
if ( FAILED(sc) )
THROW( CException( sc ) );
break;
}
}
else
{
if ( fNewPathInScope )
{
lcaseFunnyNewPath.AppendBackSlash();
ciDebugOut(( DEB_USN, "Adding directory %ws\n", lcaseFunnyNewPath.GetPath() ));
_fDoingRenameTraverse = TRUE;
CUsnTreeTraversal usnTreeTrav( _cicat,
*this,
*pCiManager,
lcaseFunnyNewPath,
FALSE, // No deletions
_fUpdatesDisabled,
TRUE, // Process root
pUsnVolume->VolumeId() );
usnTreeTrav.EndProcessing();
_fDoingRenameTraverse = FALSE;
if ( _fAbort || _fUpdatesDisabled )
return;
SCODE sc = pCiManager->FlushUpdates();
if ( FAILED(sc) )
THROW( CException( sc ) );
break;
}
else
{
//
// Both old and new paths are out of scope. We *still* have to
// determine whether this rename caused a top-level indexed scope
// to either exist or cease to exist.
//
CheckTopLevelChange( pUsnVolume, pUsnRec->FileReferenceNumber );
break;
}
}
}
CATCH( CException, e )
{
_fDoingRenameTraverse = FALSE;
#if CIDBG==1
//
// pUsnRec->FileName is not null terminated, so copy
// and null terminate.
//
unsigned cbFileNameLen = pUsnRec->FileNameLength;
xFileName.SetSizeInBytes( cbFileNameLen + 2 );
RtlCopyMemory( xFileName.Get(),
(WCHAR *)(((BYTE *)pUsnRec) + pUsnRec->FileNameOffset),
cbFileNameLen );
xFileName[cbFileNameLen/sizeof(WCHAR)] = 0;
ciDebugOut(( DEB_ERROR,
"Exception 0x%x while doing directory rename/add/delete of %ws\n",
e.GetErrorCode(),
xFileName.Get() ));
#endif
}
END_CATCH
} // for
if ( i == MAX_RETRIES )
{
CEventLog eventLog( NULL, wcsCiEventSource );
CEventItem item( EVENTLOG_ERROR_TYPE,
CI_SERVICE_CATEGORY,
MSG_CI_CONTENTSCAN_FAILED,
1 );
//
// pUsnRec->FileName is not null terminated, so copy
// and null terminate.
//
unsigned cbFileNameLen = pUsnRec->FileNameLength;
xFileName.SetSizeInBytes( cbFileNameLen + 2 );
RtlCopyMemory( xFileName.Get(),
(WCHAR *)(((BYTE *)pUsnRec) + pUsnRec->FileNameOffset),
cbFileNameLen );
xFileName[cbFileNameLen/sizeof(WCHAR)] = 0;
item.AddArg( xFileName.Get() );
eventLog.ReportEvent( item );
}
}
else // if reason & FILE_ATTRIBUTE_DIRECTORY
{
//
// File rename or add or delete
//
if ( fOldPathInScope )
{
if ( fNewPathInScope )
{
ciDebugOut(( DEB_USN, "Renaming file %ws to %ws\n",
lcaseFunnyOldPath.GetActualPath(), lcaseFunnyNewPath.GetActualPath() ));
_cicat.RenameFile( lcaseFunnyOldPath,
lcaseFunnyNewPath,
pUsnRec->FileAttributes,
pUsnVolume->VolumeId(),
pUsnRec->FileReferenceNumber,
widParent );
_cicat.Update( lcaseFunnyNewPath,
pUsnRec->FileReferenceNumber,
widParent,
pUsnRec->Usn,
pUsnVolume,
FALSE );
}
else
{
_cicat.Update( lcaseFunnyOldPath,
pUsnRec->FileReferenceNumber,
widParent,
pUsnRec->Usn,
pUsnVolume,
TRUE );
}
}
else
{
if ( fNewPathInScope )
{
_cicat.Update( lcaseFunnyNewPath,
pUsnRec->FileReferenceNumber,
widParent,
pUsnRec->Usn,
pUsnVolume,
FALSE );
}
else
{
//
// Both old and new paths are out of scope, so do nothing
//
}
}
}
}
else
{
Win4Assert( pUsnRec->Reason & USN_REASON_CLOSE );
if ( 0 != (pUsnRec->Reason & ~(USN_REASON_CLOSE | USN_REASON_COMPRESSION_CHANGE | USN_REASON_RENAME_OLD_NAME ) ) )
{
CLowerFunnyPath lcaseFunnyPath;
BOOL fPathInScope = FALSE;
WORKID widParent = widInvalid;
CReleasableLock lock( _cicat.GetMutex() );
WORKID wid = _cicat.FileIdToWorkId( pUsnRec->FileReferenceNumber,
pUsnVolume->VolumeId() );
if ( 0 != ( pUsnRec->Reason & USN_REASON_FILE_DELETE ) ||
0 != ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ) )
{
//
// File delete. Don't delete directories that go from
// indexed to non-indexed since there may be files
// below them that are indexed.
//
if ( ( widInvalid != wid ) )
{
CDocumentUpdateInfo docInfo( wid,
pUsnVolume->VolumeId(),
pUsnRec->Usn,
TRUE );
pCiManager->UpdateDocument( &docInfo );
if ( ! ( ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) &&
( pUsnRec->Reason & USN_REASON_INDEXABLE_CHANGE ) ) )
{
_cicat.LokMarkForDeletion( pUsnRec->FileReferenceNumber,
wid );
}
else
{
ciDebugOut(( DEB_ITRACE, "usn notify not deleting a ni directory\n" ));
}
}
}
else
{
//
// File create or modify
//
Win4Assert( pUsnRec->Reason & USN_REASON_CLOSE );
if ( widInvalid == wid )
{
_cicat.UsnRecordToPathUsingParentId( pUsnRec,
pUsnVolume,
lcaseFunnyPath,
fPathInScope,
widParent,
( 0 != ( pUsnRec->Reason & USN_REASON_INDEXABLE_CHANGE ) ) );
if ( fPathInScope )
_cicat.Update( lcaseFunnyPath,
pUsnRec->FileReferenceNumber,
widParent,
pUsnRec->Usn,
pUsnVolume,
FALSE, // not a delete
&lock ); // guaranteed new file
else if ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
//
// This might be a top-level directory. No parent id to
// look up, but still the root of the tree.
//
lock.Release();
CheckTopLevelChange( pUsnVolume, pUsnRec->FileReferenceNumber );
}
}
else
{
lock.Release();
CDocumentUpdateInfo docInfo( wid,
pUsnVolume->VolumeId(),
pUsnRec->Usn,
FALSE );
pCiManager->UpdateDocument( &docInfo );
}
} // if fPathInScope
} // if reason is one we care about
} // if-else reason & reason_new_name
} // pUsnRec->fileattr & file_attr_content_indexed
if ( pUsnRec->RecordLength <= dwByteCount )
{
dwByteCount -= pUsnRec->RecordLength;
//#if CIDBG == 1
#if 0
ULONG cb = pUsnRec->RecordLength;
RtlFillMemory( pUsnRec, cb, 0xEE );
pUsnRec = (USN_RECORD *) ((PCHAR) pUsnRec + cb );
#else
pUsnRec = (USN_RECORD *) ((PCHAR) pUsnRec + pUsnRec->RecordLength );
#endif
}
else
{
Win4Assert( !"Bogus dwByteCount" );
ciDebugOut(( DEB_ERROR,
"Usn read fsctl returned bogus dwByteCount, 0x%x\n",
dwByteCount ));
THROW( CException( STATUS_UNSUCCESSFUL ) );
}
}
pUsnVolume->SetMaxUsnRead( usnNextStart );
} //ProcessUsnLogRecords
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::FindCurrentMaxUsn
//
// Synopsis: Find the current max usn for the volume with pwcsScope
//
// Arguments: [pwcsScope] - Scope
//
// History: 07-May-97 SitaramR Created
//
//----------------------------------------------------------------------------
USN CUsnMgr::FindCurrentMaxUsn( WCHAR const * pwcsScope )
{
Win4Assert( pwcsScope );
WCHAR wszVolumePath[] = L"\\\\.\\a:";
wszVolumePath[4] = pwcsScope[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 )
THROW( CException( STATUS_UNSUCCESSFUL ) );
SWin32Handle xHandleVolume( hVolume );
IO_STATUS_BLOCK iosb;
USN_JOURNAL_DATA UsnJournalInfo;
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) )
Status = iosb.Status;
if ( NT_ERROR(Status) )
{
//
// Usn journal should have been created already
//
ciDebugOut(( DEB_ERROR,
"Usn read fsctl returned 0x%x\n",
Status ));
THROW( CException( Status ) );
}
return UsnJournalInfo.NextUsn;
} //FindCurrentMaxUsn
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::SetBatch
//
// Synopsis: Sets the batch flag, which means that the usn thread will not
// be signalled to start processing requests until the batch
// flag is cleared.
//
// History: 27-Jun-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::SetBatch()
{
CLock lock(_mutex);
_fBatch = TRUE;
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::ClearBatch
//
// Synopsis: Clears the batch processing flag and wakes up the usn
// thread and the accumulated scans processed.
//
// History: 27-Jun-97 SitaramR Created
//
//----------------------------------------------------------------------------
void CUsnMgr::ClearBatch()
{
CLock lock(_mutex);
_fBatch = FALSE;
_evtUsn.Set();
}
struct SVolumeUsn
{
VOLUMEID volumeId;
USN usn;
};
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::ProcessUsnLog, public
//
// Synopsis: Processes all records in the USN log for all volumes.
// This is so that we don't have to rescan when a scan
// completes
//
// Arguments: [fAbort] -- Bail if TRUE
// [volScan] -- Volume Id of volume current being scanned (will
// be added to volumes processed).
// [usnScan] -- USN on [volScan] at which monitoring will commence
// when scan is complete.
//
// History: 28-May-98 dlee Created
//
//----------------------------------------------------------------------------
void CUsnMgr::ProcessUsnLog( BOOL & fAbort, VOLUMEID volScan, USN & usnScan )
{
if ( _fDoingRenameTraverse )
return;
CDynArrayInPlace<SVolumeUsn> aVolumeUsns;
//
// Add the volume actively being scanned. No point in monitoring others with
// pending scans, since they will be scanned anyway.
//
aVolumeUsns[0].volumeId = volScan;
aVolumeUsns[0].usn = usnScan;
//
// Add all the volumes being monitored
//
{ // Save stack, by overloading usnVolIter
for ( CUsnVolumeIter usnVolIter(_usnVolumesToMonitor); // with the one farther down the function...
!_usnVolumesToMonitor.AtEnd(usnVolIter);
_usnVolumesToMonitor.Advance(usnVolIter) )
{
Win4Assert( !usnVolIter->FFsctlPending() );
VOLUMEID volumeId = usnVolIter->VolumeId();
USN usn = usnVolIter->MaxUsnRead();
for ( unsigned i = 0; i < aVolumeUsns.Count(); i++ )
{
if ( aVolumeUsns[i].volumeId == volumeId )
{
aVolumeUsns[i].usn = __min( aVolumeUsns[i].usn, usn );
break;
}
}
if ( i == aVolumeUsns.Count() )
{
aVolumeUsns[ i ].volumeId = volumeId;
aVolumeUsns[ i ].usn = usn;
}
}
}
//
// Take this list, and create temporary volume objects.
//
CUsnVolumeList usnVolumesToProcess;
for ( unsigned i = 0; i < aVolumeUsns.Count(); i++ )
{
CUsnVolume * pusnVolume = new CUsnVolume( (WCHAR) aVolumeUsns[i].volumeId, aVolumeUsns[i].volumeId );
pusnVolume->SetMaxUsnRead( aVolumeUsns[i].usn );
usnVolumesToProcess.Push( pusnVolume );
}
//
// Process USNs for the volumes until they are all out of danger
//
BOOL fSomethingChanged = FALSE;
int priOriginal;
unsigned cInDanger = 0;
BOOL fFirst = TRUE;
do
{
cInDanger = 0;
for ( CUsnVolumeIter usnVolIter(usnVolumesToProcess);
!usnVolumesToProcess.AtEnd(usnVolIter);
usnVolumesToProcess.Advance(usnVolIter) )
{
CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
// Process the usn notifications for the volume.
ULONG ulPct = pUsnVolume->PercentRead();
// If the journal is entirely empty, it's not in danger.
if ( !fFirst && ( 0 == ulPct ) && ( 0 == pUsnVolume->MaxUsnRead() ) )
continue;
// If we're in danger of a log overflow, process records.
if ( ulPct < (fSomethingChanged ? USN_LOG_DANGER_THRESHOLD + 5 : USN_LOG_DANGER_THRESHOLD ) )
{
ciDebugOut(( DEB_ITRACE, "Drive %wc: in danger (%u%%)\n",
pUsnVolume->DriveLetter(), pUsnVolume->PercentRead() ));
cInDanger++;
if ( !fSomethingChanged )
{
//
// Boost the priority to try and catch up before we fall farther behind.
// Normally, this thread runs at below normal priority.
//
priOriginal = GetThreadPriority( GetCurrentThread() );
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST );
fSomethingChanged = TRUE;
}
// TRUE == immediate processing
// FALSE == don't wait for notifications
NTSTATUS Status = ProcessUsnNotificationsFromVolume( pUsnVolume, TRUE, FALSE );
Win4Assert( STATUS_PENDING != Status );
//
// Even if this is STATUS_JOURNAL_ENTRY_DELETED, we might as
// well abort the scan. Another will have been scheduled.
//
if ( !NT_SUCCESS(Status) )
{
ciDebugOut(( DEB_ERROR, "Error %#x from ProcessUsnNotificationsFromVolume during scan\n", Status ));
THROW( CException( Status ) );
}
}
fFirst = FALSE;
ciDebugOut(( DEB_ITRACE, "Drive %wc: %u%% read (scan)\n",
pUsnVolume->DriveLetter(), ulPct ));
}
} while ( !fAbort && cInDanger > 0 );
//
// Update the max USNs
//
if ( fSomethingChanged )
{
//
// Reset thread priority.
//
if ( THREAD_PRIORITY_ERROR_RETURN != priOriginal )
SetThreadPriority( GetCurrentThread(), priOriginal );
for ( CUsnVolumeIter usnVolIter(usnVolumesToProcess);
!usnVolumesToProcess.AtEnd(usnVolIter);
usnVolumesToProcess.Advance(usnVolIter) )
{
//
// Patch the USN for the scan
//
if ( volScan == usnVolIter->VolumeId() )
usnScan = usnVolIter->MaxUsnRead();
for ( CFwdScanInfoIter scanInfoIter(_usnScansInProgress);
!_usnScansInProgress.AtEnd(scanInfoIter);
_usnScansInProgress.Advance(scanInfoIter) )
{
CCiScanInfo & scanInfo = * scanInfoIter.GetEntry();
if ( scanInfo.VolumeId() == usnVolIter->VolumeId() )
{
ciDebugOut(( DEB_ITRACE,
"usn updating scan usn on %wc: from %#I64x to %#I64x\n",
(WCHAR)scanInfo.VolumeId(),
scanInfo.UsnStart(),
usnVolIter->MaxUsnRead() ));
scanInfo.SetStartUsn( usnVolIter->MaxUsnRead() );
}
}
// Patch the USN for the monitored volume
for ( CUsnVolumeIter usnVolIter2(_usnVolumesToMonitor);
!_usnVolumesToMonitor.AtEnd(usnVolIter2);
_usnVolumesToMonitor.Advance(usnVolIter2) )
{
if ( usnVolIter->VolumeId() == usnVolIter2->VolumeId() )
{
ciDebugOut(( DEB_ITRACE,
"usn updating monitor volume %wc: from %#I64x to %#I64x\n",
usnVolIter->DriveLetter(),
usnVolIter->MaxUsnRead(),
usnVolIter->MaxUsnRead() ));
usnVolIter2->SetMaxUsnRead( usnVolIter->MaxUsnRead() );
}
}
}
}
} //ProcessUsnLog
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::HandleError, public
//
// Synopsis: Error reporting / handling for USN log read errors.
//
// Arguments: [pUsnVolume] -- Volume which could not be read.
// [Status] -- Failure code.
//
// History: 04-Jun-1998 KyleP Created
//
//----------------------------------------------------------------------------
void CUsnMgr::HandleError( CUsnVolume * pUsnVolume, NTSTATUS Status )
{
HandleError( pUsnVolume->DriveLetter(), Status );
if ( STATUS_TOO_LATE != Status )
pUsnVolume->MarkOffline();
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::HandleError, public
//
// Synopsis: Error reporting / handling for USN log read errors.
//
// Arguments: [wcDrive] -- Volume which could not be read.
// [Status] -- Failure code.
//
// History: 04-Jun-1998 KyleP Created
//
//----------------------------------------------------------------------------
void CUsnMgr::HandleError( WCHAR wcDrive, NTSTATUS Status )
{
Win4Assert( STATUS_SUCCESS != Status );
if ( STATUS_TOO_LATE != Status )
{
CEventLog eventLog( NULL, wcsCiEventSource );
CEventItem item( EVENTLOG_WARNING_TYPE,
CI_SERVICE_CATEGORY,
MSG_CI_USN_LOG_UNREADABLE,
2 );
WCHAR wszDrive[] = L"A:";
wszDrive[0] = wcDrive;
item.AddArg( wszDrive );
item.AddError( Status );
eventLog.ReportEvent( item );
}
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::IsPathIndexed, public
//
// Synopsis: Returns TRUE if the path is being indexed on a USN volume.
//
// Arguments: [pUsnVolume] -- The volume to use when checking
// [lcaseFunnyPath] -- The path to check
//
// Returns: TRUE if the path is in a current scan or in pUsnVolume.
//
// History: 12-Jun-98 dlee Created
//
//----------------------------------------------------------------------------
BOOL CUsnMgr::IsPathIndexed(
CUsnVolume * pUsnVolume,
CLowerFunnyPath & lcaseFunnyPath )
{
//
// First check the volume object given
//
CScanInfoList & usnScopeList = pUsnVolume->GetUsnScopesList();
for ( CFwdScanInfoIter iter( usnScopeList );
!usnScopeList.AtEnd(iter);
usnScopeList.Advance(iter) )
{
CScopeMatch scopeMatch( iter->GetPath(),
wcslen( iter->GetPath() ) );
if ( scopeMatch.IsInScope( lcaseFunnyPath.GetActualPath(), lcaseFunnyPath.GetActualLength() ) )
return TRUE;
}
//
// Check the scans in progress list
//
for ( CFwdScanInfoIter scanInfoIter(_usnScansInProgress);
!_usnScansInProgress.AtEnd(scanInfoIter);
_usnScansInProgress.Advance(scanInfoIter) )
{
CCiScanInfo & scanInfo = * scanInfoIter.GetEntry();
WCHAR const * pwcScanPath = scanInfo.GetPath();
CScopeMatch scopeMatch( pwcScanPath, wcslen( pwcScanPath ) );
if ( scopeMatch.IsInScope( lcaseFunnyPath.GetActualPath(), lcaseFunnyPath.GetActualLength() ) )
return TRUE;
}
return FALSE;
} //IsPathIndexed
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::IsLowResource, private
//
// Synopsis: Determine if either memory or i/o resources are low.
//
// Returns: TRUE if in a low resource condition.
//
// History: 22-Jun-1998 KyleP Created
//
//----------------------------------------------------------------------------
BOOL CUsnMgr::IsLowResource()
{
if ( _cicat.GetRegParams()->DelayUsnReadOnLowResource() )
{
CI_STATE State;
State.cbStruct = sizeof( State );
SCODE sc = _cicat.CiState( State );
if ( SUCCEEDED( sc ) )
return ( 0 != (State.eState & ( CI_STATE_HIGH_IO |
CI_STATE_LOW_MEMORY |
CI_STATE_USER_ACTIVE ) ) );
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::AnyInitialScans
//
// Synopsis: Checks if any scans are the result of a new scope
//
// Returns: TRUE if any scans are for new scopes
//
// History: 3-Aug-98 dlee Created
//
//----------------------------------------------------------------------------
BOOL CUsnMgr::AnyInitialScans()
{
for ( CFwdScanInfoIter iter1( _usnScansToDo );
!_usnScansToDo.AtEnd( iter1 );
_usnScansToDo.Advance( iter1 ) )
{
if ( iter1->IsNewScope() )
return TRUE;
}
for ( CFwdScanInfoIter iter2( _usnScansInProgress );
!_usnScansInProgress.AtEnd( iter2 );
_usnScansInProgress.Advance( iter2 ) )
{
if ( iter2->IsNewScope() )
return TRUE;
}
return FALSE;
} //AnyInitialScans
//+---------------------------------------------------------------------------
//
// Member: CUsnMgr::CheckTopLevelChange, private
//
// Synopsis: Checks if renames or creates *above* top level scopes need
// processing (because they affect top level).
//
// Arguments: [pUsnVolume] -- USN volume change affects
// [FileReferenceNumber] -- File ID of changing file
//
// History: 08-Jan-1999 KyleP Created
//
//----------------------------------------------------------------------------
void CUsnMgr::CheckTopLevelChange( CUsnVolume * pUsnVolume,
ULONGLONG & FileReferenceNumber )
{
//
// Convert file ID to path.
//
CLowerFunnyPath fpRenamed;
if ( 0 != _cicat.FileIdToPath( FileReferenceNumber, pUsnVolume->VolumeId(), fpRenamed ) )
{
CScopeMatch SMatch( fpRenamed.GetActualPath(), fpRenamed.GetActualLength() );
//
// Iterate through the top level scopes for this volume.
//
CScanInfoList & Scopes = pUsnVolume->GetUsnScopesList();
for ( CFwdScanInfoIter ScopesIter( Scopes );
!Scopes.AtEnd( ScopesIter );
Scopes.Advance( ScopesIter ) )
{
if ( _fAbort || _fUpdatesDisabled )
return;
WCHAR const * pwcsPath = ScopesIter->GetPath();
unsigned ccPath = wcslen(pwcsPath);
//
// Any top level scope that is underneath the rename must not have been
// read before, so we need to add all the files.
//
if ( SMatch.IsInScope( pwcsPath, ccPath ) )
{
ICiManager *pCiManager = _cicat.CiManager();
CLowerFunnyPath fpNew( pwcsPath, ccPath, TRUE );
fpNew.AppendBackSlash();
ciDebugOut(( DEB_USN, "Adding directory %ws\n", fpNew.GetActualPath() ));
_fDoingRenameTraverse = TRUE;
CUsnTreeTraversal usnTreeTrav( _cicat,
*this,
*pCiManager,
fpNew,
FALSE, // No deletions
_fUpdatesDisabled,
TRUE, // Process root
pUsnVolume->VolumeId() );
usnTreeTrav.EndProcessing();
_fDoingRenameTraverse = FALSE;
if ( _fAbort || _fUpdatesDisabled )
return;
SCODE sc = pCiManager->FlushUpdates();
if ( FAILED(sc) )
THROW( CException( sc ) );
}
//
// Conversely, if the top level scope no longer exists, files must be
// removed from the catalog. The LokIsInFinalState check is to keep
// us from retrying the same delete > once.
//
WIN32_FIND_DATA ffData;
if ( !GetFileAttributesEx( pwcsPath, GetFileExInfoStandard, &ffData ) &&
ScopesIter->LokIsInFinalState() )
{
ScopesIter->SetStartState();
CLowerFunnyPath fpOld( pwcsPath, ccPath, TRUE );
fpOld.AppendBackSlash();
ICiManager *pCiManager = _cicat.CiManager();
ciDebugOut(( DEB_USN, "Removing directory %ws\n", fpOld.GetActualPath() ));
_cicat.RemovePathsFromCiCat( fpOld.GetActualPath(), eUsnsArray );
if ( _fAbort || _fUpdatesDisabled )
return;
SCODE sc = pCiManager->FlushUpdates();
if ( FAILED(sc) )
THROW( CException( sc ) );
}
}
}
} //CheckTopLevelChange