|
|
// Copyright (c) 1996-1999 Microsoft Corporation
//+============================================================================
//
// delmgr.cxx
//
// Implementation of CDeletionsManager, which watches file deletes on
// NTFS5 to determine if a link source delete notification should be
// sent to trksvr.
//
//+============================================================================
#include "pch.cxx"
#pragma hdrstop
#include "trkwks.hxx"
//+----------------------------------------------------------------------------
//
// CDeletionsManager::Initialize
//
// Initialize the delete-notify timer.
//
//+----------------------------------------------------------------------------
void CDeletionsManager::Initialize( const CTrkWksConfiguration *pconfigWks ) { _pLatestDeletions = NULL; _pOldestDeletions = NULL; _cLatestDeletions = 0; _pconfigWks = pconfigWks;
_csDeletions.Initialize();
_fInitialized = TRUE;
_timerDeletions.Initialize( this, NULL, // No name (non-persistent timer)
// Context ID
DELTIMER_DELETE_NOTIFY, pconfigWks->GetDeleteNotifyTimeout(), CNewTimer::NO_RETRY, 0, 0, 0 ); // Ignored for non-retrying timer
}
//+----------------------------------------------------------------------------
//
// CDeleteionsManager::UnInitialize
//
// Free the lists of deletions, and free the timer.
//
//+----------------------------------------------------------------------------
void CDeletionsManager::UnInitialize() { if (_fInitialized) { _fInitialized = FALSE; _timerDeletions.Cancel();
FreeDeletions(); _pOldestDeletions = _pLatestDeletions; FreeDeletions();
_timerDeletions.UnInitialize();
_csDeletions.UnInitialize(); } }
//+----------------------------------------------------------------------------
//
// CDeletionsManager::NotifyAddOrDelete
//
// This method is called (by the CObjIdIndexChangeNotifier) when an object
// ID has been added, or been removed by a delete (not removed by a move).
// When an objid is deleted, we add the birth ID to a list, and after a time
// we'll send it up to trksvr. When an objid is added, we see if its birth
// ID is in our list of delete-notifies (this happens after tunnelling), and
// remove it if it is.
//
// Since we hold on to deletes for at least 5 minutes (configurable), and
// the tunnelling windows is 15 seconds (though also configurable), we don't
// worry about a add (tunnel) message coming in after we've already sent
// a delete notification to trksvr.
//
//+----------------------------------------------------------------------------
void CDeletionsManager::NotifyAddOrDelete( ULONG Action, const CDomainRelativeObjId & droid ) { // Note: this function must not access _pOldestDeletions
DROID_LIST_ELEMENT * Element;
// Ignore if we're uninitialized (this will happen if the machine is
// in a workgroup).
if( !_fInitialized ) return;
// If bit 0 if the volume id is clear, then the file hasn't moved off the
// volume and there's nothing we need do.
if (! droid.GetVolumeId().GetUserBitState()) { if( FILE_ACTION_REMOVED_BY_DELETE == Action ) { TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Ignoring droid=%s because bit 0 is clear\n"), (const TCHAR*)CDebugString(droid) )); } return; }
// Take the lock and see if we care about the action
_csDeletions.Enter();
__try { // Object ID Removed by DeleteFile
if( FILE_ACTION_REMOVED_BY_DELETE == Action ) { // Add this droid to the list of delete-notifies we need to send to the DC.
if( _pconfigWks->GetParameter( IGNORE_MOVES_AND_DELETES_CONFIG ) ) { TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Ignoring delete due to configuration") )); } else if( _cLatestDeletions < _pconfigWks->GetParameter( MAX_DELETE_NOTIFY_QUEUE_CONFIG )) { Element = new DROID_LIST_ELEMENT; if( Element ) { _cLatestDeletions++;
Element->droid = droid; Element->droid.GetVolumeId().Normalize();
// Insert the deletion onto the head of the first list.
if (_pLatestDeletions == NULL) { _timerDeletions.SetRecurring( ); TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("DeleteNotify Timer: %s"), (const TCHAR*)CDebugString(_timerDeletions) )); }
Element->pNext = _pLatestDeletions; _pLatestDeletions = Element;
TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Inserted droid=%s into DROID_LIST_ELEMENT at %08x"), (const TCHAR*)CDebugString(droid), Element)); } } #if DBG
else { TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Ignoring delete-notify due to max queue size") )); } #endif
} // if( FILE_ACTION_REMOVED_BY_DELETE == Action )
else if( FILE_ACTION_ADDED == Action ) { // See if this droid is in our delete-list. When a document does a safe-save,
// the first thing we see is the file getting deleted, and we add the droid
// to our list of delete-notifies to send to the DC. But when the ID gets
// tunnelled, we need to remove it from that list.
BOOL fDone = FALSE; DROID_LIST_ELEMENT **ppScan; CDomainRelativeObjId droidBirth = droid; droidBirth.GetVolumeId().Normalize();
// Start with the first list
ppScan = &_pLatestDeletions;
for( int i = 0; i < 2; i++ ) { while( NULL != *ppScan ) { if( (*ppScan)->droid == droidBirth ) { // This is the entry we're looking for.
// Remove it.
DROID_LIST_ELEMENT *pDel = *ppScan; *ppScan = (*ppScan)->pNext; delete pDel; fDone = TRUE;
TrkLog((TRKDBG_OBJID_DELETIONS, TEXT("Removed droid=%s delete-notify list"), (const TCHAR*)CDebugString(droid) ));
break; // while
}
// Move to the next element in this list.
ppScan = &(*ppScan)->pNext; }
// If we're done then break out. Otherwise, move on
// to the other linked list.
if( fDone ) break; else ppScan = &_pOldestDeletions;
} // for( int i = 0; i < 2; i++ )
} // else if( FILE_ACTION_ADDED == Action )
} __finally { _csDeletions.Leave(); } }
//+----------------------------------------------------------------------------
//
// CDeletionsManager::OnDeleteNotifyTimeout
//
// Process the delete notifications in the linked list at _pOldestDeletions.
// The notifications from this list are sent to trksvr, then the entries
// in that list are freed, and the entries from _pLatestDeletions are
// moved to _pOldestDeletions.
//
// Note: This method assumes it is non-reentrant, since it is only
// called when the single delete notify timer fires.
//
//+----------------------------------------------------------------------------
PTimerCallback::TimerContinuation CDeletionsManager::OnDeleteNotifyTimeout() {
CAvailableDc adc; PTimerCallback::TimerContinuation continuation = CONTINUE_TIMER;
// Keep track of the number of attempts to send a single
// batch.
static cAttempts = 0;
TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Delete notify timeout") ));
__try // __finally
{
// Are there deletions that need to be sent to trksvr?
if( _pOldestDeletions != NULL && !_pconfigWks->_fIsWorkgroup ) { TRKSVR_MESSAGE_UNION Msg; CDomainRelativeObjId adroidBirth[ 32 ]; DROID_LIST_ELEMENT * pScan; ULONG cdroidBirth;
pScan = _pOldestDeletions;
// Send the delete notifications in batches.
do { g_ptrkwks->RaiseIfStopped();
//
// Count the number of list elements and put into the
// array format for the RPC call.
//
for ( cdroidBirth = 0; pScan != NULL && cdroidBirth < ELEMENTS(adroidBirth); cdroidBirth++, pScan = pScan->pNext ) { adroidBirth[cdroidBirth] = pScan->droid; TrkAssert( pScan != pScan->pNext && pScan->pNext != _pOldestDeletions ); }
if( cdroidBirth != 0 ) { // As a sanity check, make sure we don't stick on a single
// batch forever.
if( cAttempts >= _pconfigWks->GetParameter( MAX_DELETE_NOTIFY_ATTEMPTS_CONFIG ) ) { TrkLog(( TRKDBG_WARNING, TEXT("Aborting delete-notify list") )); break; }
// Send the delete notifications to trksvr
Msg.MessageType = DELETE_NOTIFY; Msg.Priority = PRI_5; Msg.Delete.cVolumes = 0; Msg.Delete.pVolumes = NULL;
Msg.Delete.adroidBirth = adroidBirth; Msg.Delete.cdroidBirth = cdroidBirth;
// if the DC is down -> exception (really can't do anything about it)
cAttempts++; TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Sending %d delete notifications"), cdroidBirth )); adc.CallAvailableDc(&Msg); TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Sent %d delete notifications"), cdroidBirth ));
// Free the entries that we've sent so far.
FreeDeletions( pScan ); }
// Reset the per-batch retry count.
cAttempts = 0;
} while (pScan != NULL); } } __finally { if( AbnormalTermination() ) TrkLog(( TRKDBG_ERROR, TEXT("Couldn't send deletions") )); else { // Free the old deletions, and swap the list so that the
// "new" deletions become "old" (to be sent the next time
// this timer fires).
FreeDeletions(); cAttempts = 0;
_csDeletions.Enter();
if (_pLatestDeletions == NULL) { //
// If there is nothing to notify, cancel the timer
//
continuation = BREAK_TIMER; }
_pOldestDeletions = _pLatestDeletions; _pLatestDeletions = NULL; _cLatestDeletions = 0;
_csDeletions.Leave(); }
adc.UnInitialize(); }
return( continuation ); }
//+----------------------------------------------------------------------------
//
// CDeletionsManager::FreeDeletions
//
// Free the delete notifications from the "old" list
// up to (but not including) pStop or the end.
//
//+----------------------------------------------------------------------------
void CDeletionsManager::FreeDeletions( DROID_LIST_ELEMENT *pStop ) { DROID_LIST_ELEMENT * pScan = _pOldestDeletions; while (pScan && pStop != pScan) { DROID_LIST_ELEMENT * pNext = pScan->pNext; delete pScan; pScan = pNext; }
_pOldestDeletions = pStop; }
//+----------------------------------------------------------------------------
//
// CDeletionsManager::Timer
//
// Called when the delete timer fires. This triggers us to send the
// notifications from the old list, and then to move the "new" items to
// the old list.
//
//+----------------------------------------------------------------------------
PTimerCallback::TimerContinuation CDeletionsManager::Timer( ULONG ulTimerId ) { TimerContinuation continuation = CONTINUE_TIMER;
TrkAssert( ulTimerId == DELTIMER_DELETE_NOTIFY ); TrkAssert( _timerDeletions.IsRecurring() );
__try { // This will raise if service is stopping
continuation = OnDeleteNotifyTimeout(); } __except( EXCEPTION_EXECUTE_HANDLER ) { }
return( continuation );
}
|