windows-nt/Source/XPSP1/NT/com/svcdlls/trksvcs/trkwks/delmgr.cxx
2020-09-26 16:20:57 +08:00

423 lines
13 KiB
C++

// 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 );
}