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.

422 lines
13 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+============================================================================
  3. //
  4. // delmgr.cxx
  5. //
  6. // Implementation of CDeletionsManager, which watches file deletes on
  7. // NTFS5 to determine if a link source delete notification should be
  8. // sent to trksvr.
  9. //
  10. //+============================================================================
  11. #include "pch.cxx"
  12. #pragma hdrstop
  13. #include "trkwks.hxx"
  14. //+----------------------------------------------------------------------------
  15. //
  16. // CDeletionsManager::Initialize
  17. //
  18. // Initialize the delete-notify timer.
  19. //
  20. //+----------------------------------------------------------------------------
  21. void
  22. CDeletionsManager::Initialize( const CTrkWksConfiguration *pconfigWks )
  23. {
  24. _pLatestDeletions = NULL;
  25. _pOldestDeletions = NULL;
  26. _cLatestDeletions = 0;
  27. _pconfigWks = pconfigWks;
  28. _csDeletions.Initialize();
  29. _fInitialized = TRUE;
  30. _timerDeletions.Initialize( this,
  31. NULL, // No name (non-persistent timer)
  32. // Context ID
  33. DELTIMER_DELETE_NOTIFY,
  34. pconfigWks->GetDeleteNotifyTimeout(),
  35. CNewTimer::NO_RETRY,
  36. 0, 0, 0 ); // Ignored for non-retrying timer
  37. }
  38. //+----------------------------------------------------------------------------
  39. //
  40. // CDeleteionsManager::UnInitialize
  41. //
  42. // Free the lists of deletions, and free the timer.
  43. //
  44. //+----------------------------------------------------------------------------
  45. void
  46. CDeletionsManager::UnInitialize()
  47. {
  48. if (_fInitialized)
  49. {
  50. _fInitialized = FALSE;
  51. _timerDeletions.Cancel();
  52. FreeDeletions();
  53. _pOldestDeletions = _pLatestDeletions;
  54. FreeDeletions();
  55. _timerDeletions.UnInitialize();
  56. _csDeletions.UnInitialize();
  57. }
  58. }
  59. //+----------------------------------------------------------------------------
  60. //
  61. // CDeletionsManager::NotifyAddOrDelete
  62. //
  63. // This method is called (by the CObjIdIndexChangeNotifier) when an object
  64. // ID has been added, or been removed by a delete (not removed by a move).
  65. // When an objid is deleted, we add the birth ID to a list, and after a time
  66. // we'll send it up to trksvr. When an objid is added, we see if its birth
  67. // ID is in our list of delete-notifies (this happens after tunnelling), and
  68. // remove it if it is.
  69. //
  70. // Since we hold on to deletes for at least 5 minutes (configurable), and
  71. // the tunnelling windows is 15 seconds (though also configurable), we don't
  72. // worry about a add (tunnel) message coming in after we've already sent
  73. // a delete notification to trksvr.
  74. //
  75. //+----------------------------------------------------------------------------
  76. void
  77. CDeletionsManager::NotifyAddOrDelete( ULONG Action, const CDomainRelativeObjId & droid )
  78. {
  79. // Note: this function must not access _pOldestDeletions
  80. DROID_LIST_ELEMENT * Element;
  81. // Ignore if we're uninitialized (this will happen if the machine is
  82. // in a workgroup).
  83. if( !_fInitialized )
  84. return;
  85. // If bit 0 if the volume id is clear, then the file hasn't moved off the
  86. // volume and there's nothing we need do.
  87. if (! droid.GetVolumeId().GetUserBitState())
  88. {
  89. if( FILE_ACTION_REMOVED_BY_DELETE == Action )
  90. {
  91. TrkLog(( TRKDBG_OBJID_DELETIONS,
  92. TEXT("Ignoring droid=%s because bit 0 is clear\n"),
  93. (const TCHAR*)CDebugString(droid) ));
  94. }
  95. return;
  96. }
  97. // Take the lock and see if we care about the action
  98. _csDeletions.Enter();
  99. __try
  100. {
  101. // Object ID Removed by DeleteFile
  102. if( FILE_ACTION_REMOVED_BY_DELETE == Action )
  103. {
  104. // Add this droid to the list of delete-notifies we need to send to the DC.
  105. if( _pconfigWks->GetParameter( IGNORE_MOVES_AND_DELETES_CONFIG ) )
  106. {
  107. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Ignoring delete due to configuration") ));
  108. }
  109. else if( _cLatestDeletions < _pconfigWks->GetParameter( MAX_DELETE_NOTIFY_QUEUE_CONFIG ))
  110. {
  111. Element = new DROID_LIST_ELEMENT;
  112. if( Element )
  113. {
  114. _cLatestDeletions++;
  115. Element->droid = droid;
  116. Element->droid.GetVolumeId().Normalize();
  117. // Insert the deletion onto the head of the first list.
  118. if (_pLatestDeletions == NULL)
  119. {
  120. _timerDeletions.SetRecurring( );
  121. TrkLog(( TRKDBG_OBJID_DELETIONS,
  122. TEXT("DeleteNotify Timer: %s"),
  123. (const TCHAR*)CDebugString(_timerDeletions) ));
  124. }
  125. Element->pNext = _pLatestDeletions;
  126. _pLatestDeletions = Element;
  127. TrkLog(( TRKDBG_OBJID_DELETIONS,
  128. TEXT("Inserted droid=%s into DROID_LIST_ELEMENT at %08x"),
  129. (const TCHAR*)CDebugString(droid), Element));
  130. }
  131. }
  132. #if DBG
  133. else
  134. {
  135. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Ignoring delete-notify due to max queue size") ));
  136. }
  137. #endif
  138. } // if( FILE_ACTION_REMOVED_BY_DELETE == Action )
  139. else if( FILE_ACTION_ADDED == Action )
  140. {
  141. // See if this droid is in our delete-list. When a document does a safe-save,
  142. // the first thing we see is the file getting deleted, and we add the droid
  143. // to our list of delete-notifies to send to the DC. But when the ID gets
  144. // tunnelled, we need to remove it from that list.
  145. BOOL fDone = FALSE;
  146. DROID_LIST_ELEMENT **ppScan;
  147. CDomainRelativeObjId droidBirth = droid;
  148. droidBirth.GetVolumeId().Normalize();
  149. // Start with the first list
  150. ppScan = &_pLatestDeletions;
  151. for( int i = 0; i < 2; i++ )
  152. {
  153. while( NULL != *ppScan )
  154. {
  155. if( (*ppScan)->droid == droidBirth )
  156. {
  157. // This is the entry we're looking for.
  158. // Remove it.
  159. DROID_LIST_ELEMENT *pDel = *ppScan;
  160. *ppScan = (*ppScan)->pNext;
  161. delete pDel;
  162. fDone = TRUE;
  163. TrkLog((TRKDBG_OBJID_DELETIONS,
  164. TEXT("Removed droid=%s delete-notify list"),
  165. (const TCHAR*)CDebugString(droid) ));
  166. break; // while
  167. }
  168. // Move to the next element in this list.
  169. ppScan = &(*ppScan)->pNext;
  170. }
  171. // If we're done then break out. Otherwise, move on
  172. // to the other linked list.
  173. if( fDone )
  174. break;
  175. else
  176. ppScan = &_pOldestDeletions;
  177. } // for( int i = 0; i < 2; i++ )
  178. } // else if( FILE_ACTION_ADDED == Action )
  179. }
  180. __finally
  181. {
  182. _csDeletions.Leave();
  183. }
  184. }
  185. //+----------------------------------------------------------------------------
  186. //
  187. // CDeletionsManager::OnDeleteNotifyTimeout
  188. //
  189. // Process the delete notifications in the linked list at _pOldestDeletions.
  190. // The notifications from this list are sent to trksvr, then the entries
  191. // in that list are freed, and the entries from _pLatestDeletions are
  192. // moved to _pOldestDeletions.
  193. //
  194. // Note: This method assumes it is non-reentrant, since it is only
  195. // called when the single delete notify timer fires.
  196. //
  197. //+----------------------------------------------------------------------------
  198. PTimerCallback::TimerContinuation
  199. CDeletionsManager::OnDeleteNotifyTimeout()
  200. {
  201. CAvailableDc adc;
  202. PTimerCallback::TimerContinuation continuation = CONTINUE_TIMER;
  203. // Keep track of the number of attempts to send a single
  204. // batch.
  205. static cAttempts = 0;
  206. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Delete notify timeout") ));
  207. __try // __finally
  208. {
  209. // Are there deletions that need to be sent to trksvr?
  210. if( _pOldestDeletions != NULL && !_pconfigWks->_fIsWorkgroup )
  211. {
  212. TRKSVR_MESSAGE_UNION Msg;
  213. CDomainRelativeObjId adroidBirth[ 32 ];
  214. DROID_LIST_ELEMENT * pScan;
  215. ULONG cdroidBirth;
  216. pScan = _pOldestDeletions;
  217. // Send the delete notifications in batches.
  218. do
  219. {
  220. g_ptrkwks->RaiseIfStopped();
  221. //
  222. // Count the number of list elements and put into the
  223. // array format for the RPC call.
  224. //
  225. for ( cdroidBirth = 0;
  226. pScan != NULL && cdroidBirth < ELEMENTS(adroidBirth);
  227. cdroidBirth++, pScan = pScan->pNext )
  228. {
  229. adroidBirth[cdroidBirth] = pScan->droid;
  230. TrkAssert( pScan != pScan->pNext && pScan->pNext != _pOldestDeletions );
  231. }
  232. if( cdroidBirth != 0 )
  233. {
  234. // As a sanity check, make sure we don't stick on a single
  235. // batch forever.
  236. if( cAttempts >= _pconfigWks->GetParameter( MAX_DELETE_NOTIFY_ATTEMPTS_CONFIG ) )
  237. {
  238. TrkLog(( TRKDBG_WARNING,
  239. TEXT("Aborting delete-notify list") ));
  240. break;
  241. }
  242. // Send the delete notifications to trksvr
  243. Msg.MessageType = DELETE_NOTIFY;
  244. Msg.Priority = PRI_5;
  245. Msg.Delete.cVolumes = 0;
  246. Msg.Delete.pVolumes = NULL;
  247. Msg.Delete.adroidBirth = adroidBirth;
  248. Msg.Delete.cdroidBirth = cdroidBirth;
  249. // if the DC is down -> exception (really can't do anything about it)
  250. cAttempts++;
  251. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Sending %d delete notifications"), cdroidBirth ));
  252. adc.CallAvailableDc(&Msg);
  253. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Sent %d delete notifications"), cdroidBirth ));
  254. // Free the entries that we've sent so far.
  255. FreeDeletions( pScan );
  256. }
  257. // Reset the per-batch retry count.
  258. cAttempts = 0;
  259. } while (pScan != NULL);
  260. }
  261. }
  262. __finally
  263. {
  264. if( AbnormalTermination() )
  265. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't send deletions") ));
  266. else
  267. {
  268. // Free the old deletions, and swap the list so that the
  269. // "new" deletions become "old" (to be sent the next time
  270. // this timer fires).
  271. FreeDeletions();
  272. cAttempts = 0;
  273. _csDeletions.Enter();
  274. if (_pLatestDeletions == NULL)
  275. {
  276. //
  277. // If there is nothing to notify, cancel the timer
  278. //
  279. continuation = BREAK_TIMER;
  280. }
  281. _pOldestDeletions = _pLatestDeletions;
  282. _pLatestDeletions = NULL;
  283. _cLatestDeletions = 0;
  284. _csDeletions.Leave();
  285. }
  286. adc.UnInitialize();
  287. }
  288. return( continuation );
  289. }
  290. //+----------------------------------------------------------------------------
  291. //
  292. // CDeletionsManager::FreeDeletions
  293. //
  294. // Free the delete notifications from the "old" list
  295. // up to (but not including) pStop or the end.
  296. //
  297. //+----------------------------------------------------------------------------
  298. void
  299. CDeletionsManager::FreeDeletions( DROID_LIST_ELEMENT *pStop )
  300. {
  301. DROID_LIST_ELEMENT * pScan = _pOldestDeletions;
  302. while (pScan && pStop != pScan)
  303. {
  304. DROID_LIST_ELEMENT * pNext = pScan->pNext;
  305. delete pScan;
  306. pScan = pNext;
  307. }
  308. _pOldestDeletions = pStop;
  309. }
  310. //+----------------------------------------------------------------------------
  311. //
  312. // CDeletionsManager::Timer
  313. //
  314. // Called when the delete timer fires. This triggers us to send the
  315. // notifications from the old list, and then to move the "new" items to
  316. // the old list.
  317. //
  318. //+----------------------------------------------------------------------------
  319. PTimerCallback::TimerContinuation
  320. CDeletionsManager::Timer( ULONG ulTimerId )
  321. {
  322. TimerContinuation continuation = CONTINUE_TIMER;
  323. TrkAssert( ulTimerId == DELTIMER_DELETE_NOTIFY );
  324. TrkAssert( _timerDeletions.IsRecurring() );
  325. __try
  326. {
  327. // This will raise if service is stopping
  328. continuation = OnDeleteNotifyTimeout();
  329. }
  330. __except( EXCEPTION_EXECUTE_HANDLER )
  331. {
  332. }
  333. return( continuation );
  334. }