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.

2127 lines
67 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+============================================================================
  3. //
  4. // volmgr.cxx
  5. //
  6. // This file implements the CVolumeManager class. That class maintains
  7. // a list of CVolume objects.
  8. //
  9. //+============================================================================
  10. #include <pch.cxx>
  11. #pragma hdrstop
  12. #include "trkwks.hxx"
  13. #include <dbt.h>
  14. #define THIS_FILE_NUMBER VOLMGR_CXX_FILE_NO
  15. void
  16. CVolumeManager::Initialize(CTrkWksSvc * pTrkWks,
  17. const CTrkWksConfiguration *pTrkWksConfiguration,
  18. PLogCallback * pLogCallback,
  19. SERVICE_STATUS_HANDLE ssh
  20. #if DBG
  21. , CTestSync * pTunnelTest
  22. #endif
  23. )
  24. {
  25. BOOL fReg = FALSE;
  26. HKEY hKey;
  27. OBJECT_ATTRIBUTES oa;
  28. UNICODE_STRING name;
  29. CSystemSD ssd;
  30. NTSTATUS Status;
  31. TrkLog(( TRKDBG_VOLUME, TEXT("Initializing the volume list") ));
  32. TrkAssert( !_fInitialized );
  33. _csVolumeNodeList.Initialize();
  34. _fInitialized = TRUE;
  35. _pTrkWks = pTrkWks;
  36. _pTrkWksConfiguration = pTrkWksConfiguration;
  37. _pVolumeNodeListHead = NULL;
  38. __try
  39. {
  40. // This timer is started when we unexpectedly lose our volume handles.
  41. // When it fires, we try to reopen them. After a number of such retries,
  42. // we give up and stop the timer.
  43. _timerObjIdIndexReopen.Initialize(
  44. this,
  45. NULL, // No name (non-persistent timer)
  46. VOLTIMER_OBJID_INDEX_REOPEN, // Context ID
  47. pTrkWksConfiguration->GetObjIdIndexReopen(),
  48. CNewTimer::RETRY_WITH_BACKOFF,
  49. pTrkWksConfiguration->GetObjIdIndexReopenRetryMin(),
  50. pTrkWksConfiguration->GetObjIdIndexReopenRetryMax(),
  51. pTrkWksConfiguration->GetObjIdIndexReopenLifetime()
  52. );
  53. // Create and register an event that we'll signal when a volume has been unlocked.
  54. // We have to run this on an IO thread so that the logfile oplock and
  55. // ReadDirectoryChanges on the objid index will work.
  56. _heventVolumeToBeReopened = CreateEvent( NULL, FALSE, FALSE, NULL );
  57. if( NULL == _heventVolumeToBeReopened )
  58. {
  59. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create _hVolumeUnlockEvent") ));
  60. TrkReportInternalError( THIS_FILE_NUMBER, __LINE__, HRESULT_FROM_WIN32(GetLastError()),
  61. NULL );
  62. TrkRaiseWin32Error( GetLastError() );
  63. }
  64. _hRegisterWaitForSingleObjectEx
  65. = TrkRegisterWaitForSingleObjectEx( _heventVolumeToBeReopened, ThreadPoolCallbackFunction,
  66. static_cast<PWorkItem*>(this), INFINITE,
  67. WT_EXECUTEINIOTHREAD | WT_EXECUTELONGFUNCTION );
  68. if( NULL == _hRegisterWaitForSingleObjectEx )
  69. {
  70. TrkLog(( TRKDBG_ERROR, TEXT("Failed RegisterWaitForSingleObjectEx in CVolumeManager::Initialize (%lu)"),
  71. GetLastError() ));
  72. TrkRaiseLastError();
  73. }
  74. // Create a list of CVolume objects, one for each local NTFS5 volume.
  75. InitializeVolumeList( pTrkWksConfiguration, pLogCallback, ssh
  76. #if DBG
  77. , pTunnelTest
  78. #endif
  79. );
  80. if( !pTrkWksConfiguration->_fIsWorkgroup )
  81. {
  82. InitializeDomainObjects();
  83. StartDomainTimers();
  84. }
  85. // Set the event that will get us to open the volume handles on an
  86. // IO thread.
  87. OnVolumeToBeReopened();
  88. }
  89. __finally
  90. {
  91. ssd.UnInitialize();
  92. }
  93. }
  94. void
  95. CVolumeManager::UnInitialize()
  96. {
  97. if( _fInitialized )
  98. {
  99. if( NULL != _hRegisterWaitForSingleObjectEx )
  100. {
  101. if( !TrkUnregisterWait( _hRegisterWaitForSingleObjectEx ))
  102. {
  103. TrkLog(( TRKDBG_ERROR, TEXT("Failed UnregisterWait for CVolumeManager (%lu)"),
  104. GetLastError() ));
  105. }
  106. else
  107. TrkLog(( TRKDBG_VOLUME, TEXT("Unregistered wait CVolumeManager") ));
  108. _hRegisterWaitForSingleObjectEx = NULL;
  109. }
  110. if( NULL != _heventVolumeToBeReopened )
  111. {
  112. CloseHandle( _heventVolumeToBeReopened );
  113. _heventVolumeToBeReopened = NULL;
  114. }
  115. UnInitializeDomainObjects();
  116. _timerObjIdIndexReopen.UnInitialize();
  117. CVolumeNode * pVolumeNode = _pVolumeNodeListHead;
  118. _pVolumeNodeListHead = NULL;
  119. while (pVolumeNode)
  120. {
  121. CVolumeNode * pNext = pVolumeNode->_pNext;
  122. // By this time, all timers and the LPC port are stopped and have unregistered
  123. // with the thread pool. Therefore, there are no other threads running,
  124. // and each of the volume should have a ref count of only 1. For robustness,
  125. // if any volumes have leaked, we release the extra refs here.
  126. while( 0 != pVolumeNode->_pVolume->Release() );
  127. delete pVolumeNode;
  128. pVolumeNode = pNext;
  129. }
  130. _fFrequentTaskHesitation = _fInfrequentTaskHesitation = FALSE;
  131. _csVolumeNodeList.UnInitialize();
  132. _fInitialized = FALSE;
  133. }
  134. }
  135. void
  136. CVolumeManager::DoWork()
  137. {
  138. // One of the volumes has been unlocked. Just try to reopen them all
  139. // (those that aren't in need of opening will noop).
  140. __try
  141. {
  142. // These raise if the service is stopping.
  143. ReopenVolumeHandles();
  144. // If a volume has just been re-created, it may be necessary
  145. // to clean up some object IDs.
  146. CleanUpOids();
  147. }
  148. __except( BreakOnDebuggableException() )
  149. {
  150. TrkLog(( TRKDBG_VOLUME, TEXT("Ignoring exception %08x in CVolumeManager::DoWork"),
  151. GetExceptionCode() ));
  152. }
  153. }
  154. //+----------------------------------------------------------------------------
  155. //
  156. // CVolumeManager::InitializeDomainObjects
  157. //
  158. // Initialize member objects that we don't use in a workgroup. These
  159. // objects can come and go without restarting the service (thus we can
  160. // go between domains or move to/from domain without requiring a reboot).
  161. //
  162. //+----------------------------------------------------------------------------
  163. void
  164. CVolumeManager::InitializeDomainObjects()
  165. {
  166. __try
  167. {
  168. TrkAssert( !_pTrkWksConfiguration->_fIsWorkgroup );
  169. // When this timer fires, we send all our IDs to trksvr so that the entries may
  170. // be touched (entries that are not touched will be GC-ed by trksvr).
  171. // Before actuallying doing the refresh, we sleep a random period, in order to
  172. // avoid too many workstations refreshing at the same time.
  173. _timerRefresh.Initialize( this,
  174. TEXT("NextRefreshTime"), // Name (for persistent timer)
  175. VOLTIMER_REFRESH, // Context ID
  176. // Timer period
  177. _pTrkWksConfiguration->GetRefreshPeriod(),
  178. CNewTimer::RETRY_RANDOMLY, //Retry values
  179. _pTrkWksConfiguration->GetRefreshRetryMin(),
  180. _pTrkWksConfiguration->GetRefreshRetryMax(),
  181. 0 ); // No max lifetime
  182. _timerRefresh.SetRecurring();
  183. TrkLog(( TRKDBG_VOLUME, TEXT("Refresh timer: %s"),
  184. (const TCHAR*)CDebugString(_timerRefresh) ));
  185. // The Notify timer is set when we receive a move notification from ntos.
  186. // When it expires we send all unsent notifications up to trksvr.
  187. _timerNotify.Initialize( this,
  188. NULL, // No name (non-persistent timer)
  189. VOLTIMER_NOTIFY, // Context ID
  190. _pTrkWksConfiguration->GetParameter( MOVE_NOTIFY_TIMEOUT_CONFIG ),
  191. CNewTimer::RETRY_WITH_BACKOFF,
  192. _pTrkWksConfiguration->GetParameter( MIN_MOVE_NOTIFY_RETRY_CONFIG ),
  193. _pTrkWksConfiguration->GetParameter( MAX_MOVE_NOTIFY_RETRY_CONFIG ),
  194. _pTrkWksConfiguration->GetParameter( MAX_MOVE_NOTIFY_LIFETIME_CONFIG ) );
  195. // The deletions manager watches for files with object IDs to get
  196. // deleted. When they are, and they're not subsequently tunnelled back,
  197. // a notification is sent to trksvr so that it can remove that birth ID
  198. // from the object move table.
  199. _deletions.Initialize( _pTrkWksConfiguration );
  200. // When this timer fires, we do our ~daily tasks
  201. _timerFrequentTasks.Initialize( this,
  202. TEXT("NextVolFrequentTask"), // Persistent timer
  203. VOLTIMER_FREQUENT, // Context ID
  204. _pTrkWksConfiguration->GetVolFrequentTasksPeriod(),
  205. CNewTimer::NO_RETRY,
  206. 0, 0, 0 ); // Ignored for non-retrying timer
  207. // When this timer fires, we do our ~weekly tasks
  208. _timerInfrequentTasks.Initialize( this,
  209. TEXT("NextVolInfrequentTask"),//Persistent timer
  210. VOLTIMER_INFREQUENT, // Context ID
  211. _pTrkWksConfiguration->GetVolInfrequentTasksPeriod(),
  212. CNewTimer::NO_RETRY,
  213. 0, 0, 0 ); // Ignored for non-retrying timer
  214. // When this timer fires, we do the initial volume synchronizations.
  215. // This timer is also used to do slow retries. E.g, if we try to send
  216. // a move notification and get a busy error, we retry at this slow rate.
  217. _timerVolumeInit.Initialize( this,
  218. NULL, // No name (non-persistent)
  219. VOLTIMER_INIT, // Context ID
  220. _pTrkWksConfiguration->GetVolInitInitialDelay(),
  221. CNewTimer::RETRY_RANDOMLY,
  222. // Initial retry period
  223. _pTrkWksConfiguration->GetVolInitRetryDelay1(),
  224. // Max retry period
  225. _pTrkWksConfiguration->GetVolInitRetryDelay2(),
  226. _pTrkWksConfiguration->GetVolInitLifetime() );
  227. }
  228. __except( EXCEPTION_EXECUTE_HANDLER )
  229. {
  230. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception %08x in CVolumeManager::InitializeDomainObjects"),
  231. GetExceptionCode() ));
  232. }
  233. }
  234. //+----------------------------------------------------------------------------
  235. //
  236. // CVolumeManager::UnInitializeObjects
  237. //
  238. // Free the objects that we don't use in a workgroup. This doesn't require
  239. // stopping the service.
  240. //
  241. //+----------------------------------------------------------------------------
  242. void
  243. CVolumeManager::UnInitializeDomainObjects()
  244. {
  245. __try
  246. {
  247. _timerRefresh.UnInitialize();
  248. _timerNotify.UnInitialize();
  249. _deletions.UnInitialize( );
  250. _timerInfrequentTasks.UnInitialize();
  251. _timerFrequentTasks.UnInitialize();
  252. _timerVolumeInit.UnInitialize();
  253. }
  254. __except( EXCEPTION_EXECUTE_HANDLER )
  255. {
  256. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception %08x in CVolumeManager::UnInitializeDomainObjects"),
  257. GetExceptionCode() ));
  258. }
  259. }
  260. //+----------------------------------------------------------------------------
  261. //
  262. // CVolumeManager::StartDomainTimers
  263. //
  264. // Start the timers that we don't use in a workgroup.
  265. //
  266. //+----------------------------------------------------------------------------
  267. void
  268. CVolumeManager::StartDomainTimers()
  269. {
  270. if( !_pTrkWksConfiguration->_fIsWorkgroup )
  271. {
  272. _timerFrequentTasks.SetSingleShot();
  273. TrkLog(( TRKDBG_VOLUME, TEXT("Frequent timer: %s"),
  274. (const TCHAR*)CDebugString(_timerFrequentTasks) ));
  275. _timerInfrequentTasks.SetSingleShot();
  276. TrkLog(( TRKDBG_VOLUME, TEXT("Infrequent timer: %s"),
  277. (const TCHAR*)CDebugString(_timerInfrequentTasks) ));
  278. _timerVolumeInit.SetSingleShot();
  279. TrkLog(( TRKDBG_VOLUME, TEXT("VolInit timer: %s"),
  280. (const TCHAR*)CDebugString(_timerVolumeInit) ));
  281. }
  282. }
  283. //+----------------------------------------------------------------------------
  284. //
  285. // Method: CVolumeManager::RefreshVolumes
  286. //
  287. // Refresh the CVolume objects. This gives them the chance to
  288. // get an updated drive letter, and to delete themselves if the volume
  289. // they represent is now gone.
  290. //
  291. //+----------------------------------------------------------------------------
  292. void
  293. CVolumeManager::RefreshVolumes( PLogCallback *pLogCallback,
  294. SERVICE_STATUS_HANDLE ssh
  295. #if DBG
  296. , CTestSync *pTunnelTest
  297. #endif
  298. )
  299. {
  300. InitializeVolumeList( _pTrkWksConfiguration, pLogCallback, ssh
  301. #if DBG
  302. , pTunnelTest
  303. #endif
  304. );
  305. OnVolumeToBeReopened();
  306. }
  307. //+----------------------------------------------------------------------------
  308. //
  309. // Method: CVolumeManager::InitializeVolumeList
  310. //
  311. // This method initializes the linked list of CVolume objects.
  312. //
  313. //+----------------------------------------------------------------------------
  314. void
  315. CVolumeManager::InitializeVolumeList( const CTrkWksConfiguration *pTrkWksConfiguration,
  316. PLogCallback *pLogCallback,
  317. SERVICE_STATUS_HANDLE ssh
  318. #if DBG
  319. , CTestSync *pTunnelTest
  320. #endif
  321. )
  322. {
  323. ULONG cVolumes = 0;
  324. TCHAR tszVolumeName[ CCH_MAX_VOLUME_NAME + 1 ];
  325. HANDLE hFindVolume = INVALID_HANDLE_VALUE;
  326. CVolumeNode *pVolNode = NULL;
  327. __try
  328. {
  329. // Begin a physical volume enumeration using the mount manager.
  330. // Volume names represent the root in Win32 format, e.g.
  331. // \\?\Volume{8baec120-078b-11d2-824b-000000000000}\
  332. hFindVolume = FindFirstVolume( tszVolumeName, sizeof(tszVolumeName) );
  333. if( INVALID_HANDLE_VALUE == hFindVolume )
  334. {
  335. TrkLog(( TRKDBG_ERROR, TEXT("FindFirstVolume failed") ));
  336. TrkRaiseLastError();
  337. }
  338. // For each local NTFS5 volume, create a CVolume, add it to the linked-list,
  339. // and initialize.
  340. cVolumes = 1;
  341. while( NUM_VOLUMES >= cVolumes )
  342. {
  343. CVolume *pvolOpen = NULL;
  344. TCHAR tszVolumeDeviceName[ MAX_PATH + 1 ];
  345. ULONG cchVolumeName;
  346. TrkLog(( TRKDBG_VOLUME, TEXT("Initializing volume %s"), tszVolumeName ));
  347. // If we already have this volume open, continue on.
  348. cchVolumeName = wcslen(tszVolumeName);
  349. memcpy( tszVolumeDeviceName, tszVolumeName, cchVolumeName*sizeof(TCHAR) );
  350. tszVolumeDeviceName[ cchVolumeName - 1 ] = TEXT('\0');
  351. if( pvolOpen = FindVolume( tszVolumeDeviceName ))
  352. {
  353. TrkLog(( TRKDBG_VOLUME, TEXT("Volume already open" ) ));
  354. pvolOpen->Release();
  355. cVolumes++;
  356. }
  357. // If this isn't an NTFS5 volume, move on.
  358. else if( IsLocalObjectVolume(tszVolumeName) )
  359. {
  360. // Alloc a new node for the volume list
  361. pVolNode = new CVolumeNode;
  362. if (pVolNode == NULL)
  363. {
  364. TrkRaiseException(E_OUTOFMEMORY);
  365. }
  366. // Put a volume into the node
  367. pVolNode->_pVolume = new CVolume();
  368. if (pVolNode->_pVolume == NULL)
  369. {
  370. TrkRaiseException(E_OUTOFMEMORY);
  371. }
  372. // Initialize the volume. Returns true if successful, raises on error.
  373. __try
  374. {
  375. if( pVolNode->_pVolume->Initialize(tszVolumeName, _pTrkWksConfiguration, this,
  376. pLogCallback, &_deletions, ssh
  377. #if DBG
  378. ,pTunnelTest
  379. #endif
  380. ))
  381. {
  382. cVolumes++;
  383. // Add this volume node (and its associated CVolume) to the linked list.
  384. AddNodeToLinkedList( pVolNode );
  385. pVolNode = NULL;
  386. } // if( pVolNode->_pVolume->Initialize(v, _pTrkWksConfiguration, ...
  387. }
  388. __except( BreakOnDebuggableException() )
  389. {
  390. }
  391. if( NULL != pVolNode )
  392. {
  393. TrkLog(( TRKDBG_VOLUME, TEXT("Volume initialization failed, deleting node") ));
  394. pVolNode->_pVolume->Release();
  395. delete pVolNode;
  396. pVolNode = NULL;
  397. }
  398. } // else if( IsLocalObjectVolume(tszVolumeName) )
  399. #if DBG
  400. else
  401. {
  402. TrkLog(( TRKDBG_VOLUME, TEXT("Skipping volume %s"), tszVolumeName ));
  403. }
  404. #endif
  405. // Move on to the next volume in the system.
  406. if( !FindNextVolume( hFindVolume, tszVolumeName, sizeof(tszVolumeName) ))
  407. {
  408. if( ERROR_NO_MORE_FILES == GetLastError() )
  409. // We've enumerated all of the volumes.
  410. break;
  411. else
  412. {
  413. TrkLog(( TRKDBG_ERROR, TEXT("FindNextVolume failed") ));
  414. TrkRaiseLastError();
  415. }
  416. }
  417. } // while( NUM_VOLUMES >= cVolumes )
  418. }
  419. __finally
  420. {
  421. if( INVALID_HANDLE_VALUE != hFindVolume )
  422. FindVolumeClose( hFindVolume );
  423. if( NULL != pVolNode )
  424. delete pVolNode;
  425. }
  426. }
  427. //+----------------------------------------------------------------------------
  428. //
  429. // Method: CVolumeManager::AddNodeToLinkedList
  430. //
  431. // Adds a CVolumeNode (and its embedded CVolume) to the volume manager's
  432. // linked list.
  433. //
  434. // The CVolumeNode elements in the linked list are kept sorted in
  435. // increasing address order. This is so a CVolumeEnumerator can handle a
  436. // node being deleted while such an enumeration is active.
  437. //
  438. //+----------------------------------------------------------------------------
  439. void
  440. CVolumeManager::AddNodeToLinkedList( CVolumeNode *pVolNode )
  441. {
  442. TrkAssert( _fInitialized );
  443. _csVolumeNodeList.Enter();
  444. __try
  445. {
  446. if( NULL == _pVolumeNodeListHead )
  447. {
  448. pVolNode->_pNext = NULL;
  449. _pVolumeNodeListHead = pVolNode;
  450. }
  451. else if( pVolNode < _pVolumeNodeListHead )
  452. {
  453. pVolNode->_pNext = _pVolumeNodeListHead;
  454. _pVolumeNodeListHead = pVolNode;
  455. }
  456. else
  457. {
  458. CVolumeNode *pNode = _pVolumeNodeListHead;
  459. while( NULL != pNode->_pNext && pNode->_pNext < pVolNode )
  460. pNode = pNode->_pNext;
  461. if( NULL == pNode->_pNext )
  462. {
  463. TrkAssert( pNode < pVolNode );
  464. pNode->_pNext = pVolNode;
  465. pVolNode->_pNext = NULL;
  466. }
  467. else
  468. {
  469. TrkAssert( pNode < pVolNode );
  470. TrkAssert( pNode->_pNext > pVolNode );
  471. pVolNode->_pNext = pNode->_pNext;
  472. pNode->_pNext = pVolNode;
  473. }
  474. }
  475. }
  476. __except( EXCEPTION_EXECUTE_HANDLER )
  477. {
  478. TrkAssert( !TEXT("Unexpected exception in CVolumeManager::AddNodeToLinkedList") );
  479. }
  480. _csVolumeNodeList.Leave();
  481. }
  482. //+----------------------------------------------------------------------------
  483. //
  484. // Method: CVolumeManager::RemoveVolumeFromLinkedList
  485. //
  486. // Removes a volume from the volume manager's linked list.
  487. // The CVolume is Released (which may or may not make it go away, depending
  488. // on ref-counts), and the CVolumeNode is deleted.
  489. //
  490. //+----------------------------------------------------------------------------
  491. void
  492. CVolumeManager::RemoveVolumeFromLinkedList( const CVolume *pvol )
  493. {
  494. CVolumeNode **ppvolnodePrev = NULL;
  495. CVolumeNode *pvolnode = NULL;
  496. TrkAssert( _fInitialized );
  497. _csVolumeNodeList.Enter();
  498. __try
  499. {
  500. pvolnode = _pVolumeNodeListHead;
  501. ppvolnodePrev = &_pVolumeNodeListHead;
  502. while( NULL != pvolnode )
  503. {
  504. if( pvol == pvolnode->_pVolume )
  505. {
  506. TrkLog(( TRKDBG_VOLUME, TEXT("Removing volume %p from the list"), pvolnode->_pVolume ));
  507. CVolumeNode *pvolnodeDel = pvolnode;
  508. *ppvolnodePrev = pvolnode->_pNext;
  509. // Releasing the volume will usually cause it to delete itself, unless
  510. // someone else is holding a ref on it.
  511. pvolnodeDel->_pVolume->Release();
  512. delete pvolnodeDel;
  513. break;
  514. }
  515. else
  516. {
  517. ppvolnodePrev = &pvolnode->_pNext;
  518. pvolnode = pvolnode->_pNext;
  519. }
  520. }
  521. }
  522. __except( BreakOnDebuggableException() )
  523. {
  524. }
  525. _csVolumeNodeList.Leave();
  526. }
  527. void
  528. CVolumeManager::CloseVolumeHandles( HDEVNOTIFY hdnVolume )
  529. {
  530. CVolumeEnumerator volEnum = Enum();
  531. CVolume* vol = volEnum.GetNextVolume();
  532. while (vol != NULL)
  533. {
  534. __try
  535. {
  536. vol->CloseVolumeHandles( hdnVolume );
  537. }
  538. __except( BreakOnDebuggableException() )
  539. {
  540. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CloseVolumeHandles (%08x)"),
  541. GetExceptionCode() ));
  542. }
  543. vol->Release();
  544. vol = volEnum.GetNextVolume();
  545. }
  546. }
  547. void
  548. CVolumeManager::CleanUpOids()
  549. {
  550. CVolumeEnumerator volEnum = Enum();
  551. CVolume* vol = volEnum.GetNextVolume();
  552. while(vol != NULL)
  553. {
  554. if(vol->IsMarkedForMakeAllOidsReborn())
  555. {
  556. __try
  557. {
  558. if( vol->MakeAllOidsReborn() )
  559. vol->ClearMarkForMakeAllOidsReborn();
  560. }
  561. __except( BreakOnDebuggableException() )
  562. {
  563. TrkLog(( TRKDBG_VOLUME, TEXT("Ignoring exception in CVolumeManager::CleanUpOids") ));
  564. }
  565. }
  566. vol->Release();
  567. vol = volEnum.GetNextVolume();
  568. }
  569. }
  570. //+----------------------------------------------------------------------------
  571. //
  572. // CVolumeManager::SyncVolumes
  573. //
  574. // Give each active volume an opportunity to synchronize with trksvr.
  575. // This allows the volumes to create a new ID, claim an existing ID,
  576. // etc.
  577. //
  578. // Note:: This routine is guaranteed not to raise.
  579. //
  580. //+----------------------------------------------------------------------------
  581. BOOL
  582. CVolumeManager::SyncVolumes( EAggressiveness eAggressiveness,
  583. CFILETIME cftLastDue,
  584. ULONG ulPeriodInSeconds )
  585. {
  586. BOOL fSuccess = FALSE;
  587. BOOL fDoRetry = FALSE;
  588. CAvailableDc adc;
  589. CVolumeEnumerator volEnum;
  590. CVolume* pvol;
  591. BOOL fIsOnlyInstance = FALSE;
  592. __try
  593. {
  594. TRKSVR_SYNC_VOLUME rgSyncVolumes[ NUM_VOLUMES ];
  595. TRKSVR_MESSAGE_UNION Msg;
  596. ULONG cVolumes = 0;
  597. const CVolumeId volNULL;
  598. BOOL fSyncNeeded;
  599. // If there's already a thread doing a SyncVolumes, we don't need to
  600. // do it again simultaneously.
  601. fIsOnlyInstance = BeginSingleInstanceTask( &_cSyncVolumesInProgress );
  602. if( !fIsOnlyInstance )
  603. {
  604. TrkLog(( TRKDBG_VOLUME, TEXT("Skipping SyncVolumes, another instance already in progress") ));
  605. fSuccess = TRUE;
  606. goto Exit;
  607. }
  608. // If we haven't fully initialized the volumes yet, do so now.
  609. ReopenVolumeHandles();
  610. // Start a CVolume enumeration
  611. volEnum = Enum();
  612. pvol = volEnum.GetNextVolume();
  613. // Loop through the enumerated volumes.
  614. // When we're done, cVolumes will show the count of volumes
  615. // that requested to sync with trksvr (could be zero).
  616. while ( NULL != pvol )
  617. {
  618. BOOL fFound = FALSE;
  619. // Add this volume to the array of volumes needing update with trksvr.
  620. // If this particular volume turns out not to need an update, we won't
  621. // increment cVolumes, consequently it will get overwritten on the
  622. // next pass. If this volume does need an update, we'll addref it.
  623. _rgVolumesToUpdate[cVolumes] = pvol;
  624. // Call the volume to load the sync-volume request.
  625. if(TRUE == pvol->LoadSyncVolume(&rgSyncVolumes[cVolumes], eAggressiveness, &fSyncNeeded))
  626. {
  627. // The LoadSyncVolumes request succeeded.
  628. // Does the volume need a sync with trksvr?
  629. if(fSyncNeeded == FALSE)
  630. {
  631. // No, the volume doesn't need a sync. It's
  632. // apparantly neither new nor newly attached.
  633. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  634. TEXT("Volume %c is properly ID-ed already"),
  635. 'A'+pvol->GetIndex() ));
  636. }
  637. else if(rgSyncVolumes[cVolumes].SyncType == CREATE_VOLUME)
  638. {
  639. // This is a newly-formatted volume.
  640. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  641. TEXT("Having a new ID created for volume %c"),
  642. TEXT('A')+pvol->GetIndex() ));
  643. _rgVolumesToUpdate[cVolumes]->AddRef();
  644. cVolumes++;
  645. }
  646. else
  647. {
  648. // The volume is new to this machine
  649. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  650. TEXT("Claiming volume %c"),
  651. TEXT('A')+pvol->GetIndex() ));
  652. _rgVolumesToUpdate[cVolumes]->AddRef();
  653. cVolumes++;
  654. }
  655. }
  656. else
  657. {
  658. // The volume needs to be synced but failed for some reason
  659. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  660. TEXT("Volume %c can not be synced"),
  661. 'A'+pvol->GetIndex() ));
  662. }
  663. // Move on to the next item in the enumeration.
  664. // If this volumes needs an update, it's addref-ed in
  665. // _rgVolumesToUpdate.
  666. pvol->Release();
  667. pvol = volEnum.GetNextVolume();
  668. } // while ( vol != NULL )
  669. // Were there any volumes in need of a sync with trksvr?
  670. if( 0 != cVolumes )
  671. {
  672. // Yes, send the sync_volumes request.
  673. __try
  674. {
  675. HRESULT hr;
  676. // Construct the Msg union
  677. Msg.MessageType = SYNC_VOLUMES;
  678. Msg.Priority = (0 == ulPeriodInSeconds)
  679. ? PRI_6
  680. : GetSvrMessagePriority( cftLastDue, ulPeriodInSeconds );
  681. Msg.SyncVolumes.cVolumes = cVolumes;
  682. Msg.SyncVolumes.pVolumes = rgSyncVolumes;
  683. #ifdef VOL_REPL
  684. Msg.SyncVolumes.cChanges = 0;
  685. Msg.SyncVolumes.ppVolumeChanges = NULL;
  686. #endif
  687. // Send the request to trksvr. We pass it under privacy encryption
  688. // because there could be a volume secret in it.
  689. // This will raise if there's an error.
  690. hr = adc.CallAvailableDc(&Msg, PRIVACY_AUTHENTICATION );
  691. // now we've successfully told the DC we should update the
  692. // DcInformed flags
  693. TrkLog((TRKDBG_VOLUME, TEXT("CallAvailableDc returned %d volumes (%08X)"),
  694. Msg.SyncVolumes.cVolumes, hr ));
  695. // See if there were problems and we should do a retry
  696. // (This gets set in the DcCallback method).
  697. if( TRK_S_VOLUME_NOT_SYNCED == hr )
  698. fDoRetry = TRUE;
  699. // Process the responses
  700. for( ULONG v = 0; v < Msg.SyncVolumes.cVolumes; v++ )
  701. {
  702. // If the problem is server-too-busy, have the caller do
  703. // a retry. Otherwise (e.g. quota error) ignore the error.
  704. // E.g. for a quota error, we'll ignore the error, but the
  705. // volume will still be in a not-created state, and we'll
  706. // retry the next time the infrequent timer fires.
  707. if( TRK_E_SERVER_TOO_BUSY == rgSyncVolumes[v].hr )
  708. fDoRetry = TRUE;
  709. if( _rgVolumesToUpdate[v]->UnloadSyncVolume( &rgSyncVolumes[v] )
  710. &&
  711. rgSyncVolumes[v].hr == S_OK
  712. )
  713. {
  714. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  715. TEXT("Volume %c successfully synced with server"),
  716. 'A'+_rgVolumesToUpdate[v]->GetIndex() ));
  717. }
  718. else
  719. {
  720. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  721. TEXT("Couldn't sync vol %c with server (%08x, %s)"),
  722. 'A'+_rgVolumesToUpdate[v]->GetIndex(),
  723. rgSyncVolumes[v].hr, GetErrorString(rgSyncVolumes[v].hr) ));
  724. }
  725. } // for( v = 0; v < Msg.SyncVolumes.cVolumes; v++ )
  726. }
  727. __finally
  728. {
  729. for( ULONG v = 0; v < cVolumes; v++ )
  730. {
  731. _rgVolumesToUpdate[v]->Release();
  732. }
  733. }
  734. } // if( 0 != cVolumes )
  735. // If any volids have been changed, make all the existing object IDs
  736. // reborn.
  737. CleanUpOids();
  738. fSuccess = TRUE;
  739. }
  740. __except( BreakOnDebuggableException() )
  741. {
  742. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't sync with server (0x%08x)"), GetExceptionCode() ));
  743. }
  744. if( pvol != NULL )
  745. {
  746. pvol->Release();
  747. }
  748. Exit:
  749. if( fIsOnlyInstance )
  750. EndSingleInstanceTask( &_cSyncVolumesInProgress );
  751. return fSuccess && !fDoRetry;
  752. }
  753. void
  754. CVolumeManager::Append( const CDomainRelativeObjId &droidCurrent,
  755. const CDomainRelativeObjId &droidNew,
  756. const CMachineId &mcidNew,
  757. const CDomainRelativeObjId &droidBirth)
  758. {
  759. CVolumeEnumerator enumerator = Enum();
  760. CVolume* cvolCur = enumerator.GetNextVolume();
  761. BOOL fVolumeFound = FALSE;
  762. __try // __finally
  763. {
  764. while ( cvolCur != NULL )
  765. {
  766. if( cvolCur->GetVolumeId() == droidCurrent.GetVolumeId())
  767. {
  768. fVolumeFound = TRUE;
  769. cvolCur->Append(droidCurrent, droidNew, mcidNew, droidBirth);
  770. break;
  771. }
  772. cvolCur->Release();
  773. cvolCur = enumerator.GetNextVolume();
  774. }
  775. }
  776. __finally
  777. {
  778. if (cvolCur != NULL)
  779. {
  780. cvolCur->Release();
  781. }
  782. }
  783. if( !fVolumeFound )
  784. TrkRaiseNtStatus( STATUS_NO_TRACKING_SERVICE );
  785. }
  786. // search all volumes (and each log) or just the given volume
  787. //
  788. // S_OK || TRK_E_NOT_FOUND || TRK_E_REFERRAL
  789. HRESULT
  790. CVolumeManager::Search( DWORD Restrictions,
  791. const CDomainRelativeObjId & droidBirthLast,
  792. const CDomainRelativeObjId & droidLast,
  793. CDomainRelativeObjId * pdroidBirthNext,
  794. CDomainRelativeObjId * pdroidNext,
  795. CMachineId * pmcidNext,
  796. TCHAR * ptszLocalPath )
  797. {
  798. NTSTATUS status;
  799. HRESULT hr = TRK_E_NOT_FOUND;
  800. CVolumeEnumerator enumerator;
  801. CVolume* pvol = NULL;
  802. const CMachineId mcidLocal( MCID_LOCAL );
  803. BOOL fPotentialFile = FALSE;
  804. CDomainRelativeObjId droidZero;
  805. // Local working stores for what we'll return in pdroidNew & pmcidNew
  806. // if we have a referral.
  807. CDomainRelativeObjId droidBirthNext = droidBirthLast;
  808. CDomainRelativeObjId droidNext = droidLast;
  809. CMachineId mcidNext = mcidLocal;
  810. __try // __finally
  811. {
  812. g_ptrkwks->RaiseIfStopped();
  813. // -------------------------------
  814. // Search the volumes for the file
  815. // -------------------------------
  816. // Search the last volume first, then search all the volumes.
  817. // Thus we'll end up searching droidLast.GetVolumeId() twice.
  818. // This is necessary, though, because there could be multiple
  819. // local volumes with this volid.
  820. if( !(Restrictions & TRK_MEND_DONT_SEARCH_ALL_VOLUMES) )
  821. enumerator = Enum();
  822. if( !(Restrictions & TRK_MEND_DONT_USE_VOLIDS) )
  823. pvol = FindVolume( droidLast.GetVolumeId() );
  824. if( NULL == pvol )
  825. pvol = enumerator.GetNextVolume();
  826. for( ; NULL != pvol; pvol = enumerator.GetNextVolume() )
  827. {
  828. status = FindLocalPath( pvol->GetIndex(), droidLast.GetObjId(),
  829. &droidBirthNext, &ptszLocalPath[2] );
  830. if( NT_SUCCESS(status) )
  831. {
  832. // Is this in the SystemVolumeInformation directory? If so, we'll
  833. // pretend we didn't find it (it's probably in the System Recovery
  834. // directory).
  835. if( IsSystemVolumeInformation( &ptszLocalPath[2] ))
  836. {
  837. TrkLog(( TRKDBG_VOLUME | TRKDBG_MEND,
  838. TEXT("CVolumeManager::Search ignoring %c:%s"),
  839. VolChar(pvol->GetIndex()), &ptszLocalPath[2] ));
  840. }
  841. // Or, is this the correct birth ID (or the caller doesn't want us to check)?
  842. else if( droidBirthLast == droidBirthNext
  843. ||
  844. droidBirthLast == droidZero )
  845. {
  846. // Yes. We've found our file and we're done.
  847. // Give the path a drive letter
  848. TrkAssert( -1 != pvol->GetIndex() );
  849. ptszLocalPath[0] = VolChar(pvol->GetIndex());
  850. ptszLocalPath[1] = TEXT(':');
  851. // droidBirthNext is already set by FindLocalPath
  852. droidNext = CDomainRelativeObjId( pvol->GetVolumeId(), droidLast.GetObjId() );
  853. mcidNext = mcidLocal;
  854. TrkLog(( TRKDBG_VOLUME | TRKDBG_MEND, TEXT("CVolumeManager::Search found %s"),
  855. ptszLocalPath ));
  856. hr = S_OK;
  857. fPotentialFile = FALSE;
  858. __leave;
  859. }
  860. // Otherwise, is it the first potential hit?
  861. else if( !fPotentialFile )
  862. {
  863. // We found a file with the right object ID, but the wrong birth ID.
  864. // By rule of law, it's therefore not the right file. However, it could be
  865. // the right file, but was re-born due to a volid change. So we'll keep
  866. // it and let the caller (eventually, the user) decide.
  867. // Give the path a drive letter
  868. ptszLocalPath[0] = VolChar(pvol->GetIndex());
  869. ptszLocalPath[1] = TEXT(':');
  870. // droidBirthNext is already set by FindLocalPath
  871. droidNext = CDomainRelativeObjId( pvol->GetVolumeId(), droidLast.GetObjId() );
  872. mcidNext = mcidLocal;
  873. TrkLog(( TRKDBG_VOLUME | TRKDBG_MEND,
  874. TEXT("CVolumeManager::Search found *potential* %s"),
  875. ptszLocalPath ));
  876. fPotentialFile = TRUE;
  877. }
  878. }
  879. pvol->Release(); pvol = NULL;
  880. } // for( ; NULL != pvol, pvol = enumerator.GetNextVolume() );
  881. enumerator.UnInitialize();
  882. // We didn't find the file on any of the volumes, let's search the logs
  883. // to see if they know where it went.
  884. // ---------------------
  885. // Search the local logs
  886. // ---------------------
  887. if( Restrictions & TRK_MEND_DONT_USE_LOG )
  888. __leave;
  889. // Start by searching the log of the last volume, then enumerate through all the
  890. // volume logs. Again we'll end up searching the last volume twice, because
  891. // there could be dup volids.
  892. if( !(Restrictions & TRK_MEND_DONT_SEARCH_ALL_VOLUMES) )
  893. enumerator = Enum();
  894. if( !(Restrictions & TRK_MEND_DONT_USE_VOLIDS) )
  895. pvol = FindVolume( droidLast.GetVolumeId() );
  896. if( NULL == pvol )
  897. pvol = enumerator.GetNextVolume();
  898. for( ; NULL != pvol; pvol = enumerator.GetNextVolume() )
  899. {
  900. CDomainRelativeObjId droidNextT, droidBirthT;
  901. CMachineId mcidNextT;
  902. if (pvol->Search( droidLast, &droidNextT, &mcidNextT, &droidBirthT ))
  903. {
  904. // We found a match in the log.
  905. TrkLog(( TRKDBG_VOLUME | TRKDBG_MEND, TEXT("Referral on vol %c: %s\n => %s:%s"),
  906. VolChar(pvol->GetVolIndex()),
  907. (const TCHAR*)CDebugString(droidLast.GetObjId()),
  908. (const TCHAR*)CDebugString(mcidNextT),
  909. (const TCHAR*)CDebugString(droidNextT) ));
  910. // If the volid is useful (it's non-NULL and non-local), keep these IDs
  911. // as the best to return.
  912. if( CVolumeId() != droidNextT.GetVolumeId()
  913. &&
  914. ( !IsLocal( droidNextT.GetVolumeId() )
  915. ||
  916. droidLast.GetObjId() != droidNextT.GetObjId()
  917. ||
  918. (Restrictions & TRK_MEND_DONT_SEARCH_ALL_VOLUMES)
  919. )
  920. )
  921. {
  922. hr = TRK_E_REFERRAL;
  923. droidBirthNext = droidBirthLast;
  924. droidNext = droidNextT;
  925. mcidNext = mcidNextT;
  926. fPotentialFile = FALSE;
  927. break;
  928. }
  929. // Or, if the mcid is useful (not this machine), keep the IDs as the
  930. // best to return
  931. else if( CMachineId() != mcidNextT
  932. &&
  933. ( mcidLocal != mcidNextT
  934. ||
  935. (Restrictions & TRK_MEND_DONT_SEARCH_ALL_VOLUMES)
  936. )
  937. )
  938. {
  939. hr = TRK_E_REFERRAL;
  940. droidBirthNext = droidBirthLast;
  941. mcidNext = mcidNextT;
  942. droidNext = droidNextT;
  943. fPotentialFile = FALSE;
  944. break;
  945. }
  946. }
  947. pvol->Release(); pvol = NULL;
  948. } // for( ; NULL != pvol, pvol = enumerator.GetNextVolume() );
  949. } // __try
  950. _finally
  951. {
  952. if( NULL != pvol )
  953. {
  954. pvol->Release();
  955. pvol = NULL;
  956. }
  957. enumerator.UnInitialize();
  958. }
  959. // If we didn't find the file or a referral, but did find a potential hit,
  960. // return that potential hit.
  961. if( TRK_E_NOT_FOUND == hr && fPotentialFile )
  962. hr = TRK_E_POTENTIAL_FILE_FOUND;
  963. if( SUCCEEDED(hr) || TRK_E_REFERRAL == hr || TRK_E_POTENTIAL_FILE_FOUND == hr )
  964. {
  965. *pdroidBirthNext = droidBirthNext;
  966. *pmcidNext = mcidNext;
  967. *pdroidNext = droidNext;
  968. }
  969. return( hr );
  970. }
  971. PTimerCallback::TimerContinuation
  972. CVolumeManager::Timer( DWORD dwTimerId )
  973. {
  974. PTimerCallback::TimerContinuation continuation = CONTINUE_TIMER;
  975. __try
  976. {
  977. switch ( dwTimerId )
  978. {
  979. case VOLTIMER_OBJID_INDEX_REOPEN:
  980. TrkAssert( _timerObjIdIndexReopen.IsRecurring() );
  981. if( ReopenVolumeHandles() ) // Raises if service is stopped
  982. continuation = BREAK_TIMER;
  983. else
  984. continuation = RETRY_TIMER;
  985. break;
  986. case VOLTIMER_FREQUENT:
  987. case VOLTIMER_INFREQUENT:
  988. case VOLTIMER_INIT:
  989. TrkAssert( !_timerFrequentTasks.IsRecurring() );
  990. TrkAssert( !_timerInfrequentTasks.IsRecurring() );
  991. TrkAssert( !_timerVolumeInit.IsRecurring() );
  992. continuation = OnVolumeTimer( dwTimerId );
  993. break;
  994. // Time to send MoveNotifies to the DC
  995. case VOLTIMER_NOTIFY:
  996. TrkAssert( !_timerNotify.IsRecurring() );
  997. TrkAssert( CNewTimer::RETRY_WITH_BACKOFF == _timerNotify.GetRetryType() );
  998. __try
  999. {
  1000. continuation = _pTrkWks->OnMoveBatchTimeout();
  1001. }
  1002. __except( BreakOnDebuggableException() )
  1003. {
  1004. // If there was an unexpected error, instead of retrying the
  1005. // MoveNotify timer, use the slower (thus DC-friendlier) VolInit
  1006. // timer.
  1007. TrkLog(( TRKDBG_ERROR, TEXT("Server to busy to receive move notifications, starting VolInit timer") ));
  1008. continuation = BREAK_TIMER;
  1009. SetVolInitTimer();
  1010. }
  1011. break;
  1012. // Time to refresh the DC with all our active IDs
  1013. case VOLTIMER_REFRESH:
  1014. // The first time we're called, we hesitate for a random amount of time.
  1015. // This hesitation is implemented by setting a flag, then resetting the timer.
  1016. // When the timer fires again a little later, we'll do the real work.
  1017. if( !_fRefreshHesitation )
  1018. {
  1019. // This is the first time we've been called.
  1020. ULONG ulHesitation = 0;
  1021. // Delay a random number of seconds within an interval
  1022. _fRefreshHesitation = TRUE;
  1023. ulHesitation = QuasiRandomDword()
  1024. %
  1025. ( 1 + _pTrkWksConfiguration->GetRefreshHesitation() );
  1026. TrkLog(( TRKDBG_LOG, TEXT("Hesitating %d seconds before executing refresh"), ulHesitation ));
  1027. _timerRefresh.ReInitialize( ulHesitation );
  1028. continuation = CONTINUE_TIMER;
  1029. }
  1030. else
  1031. {
  1032. TrkAssert( _timerRefresh.IsRecurring() );
  1033. continuation = _pTrkWks->OnRefreshTimeout(
  1034. _timerRefresh.QueryOriginalDueTime(),
  1035. _pTrkWksConfiguration->GetRefreshPeriod() );
  1036. if( CONTINUE_TIMER == continuation )
  1037. {
  1038. _timerRefresh.ReInitialize( _pTrkWksConfiguration->GetRefreshPeriod() );
  1039. _fRefreshHesitation = FALSE;
  1040. }
  1041. }
  1042. break;
  1043. default:
  1044. TrkAssert( 0 && "invalid timer id" );
  1045. break;
  1046. }
  1047. }
  1048. __except( BreakOnDebuggableException() )
  1049. {
  1050. TrkLog(( TRKDBG_VOLUME, TEXT("Ignoring exception %08x in CVolumeManager::Timer"),
  1051. GetExceptionCode() ));
  1052. }
  1053. return( continuation );
  1054. }
  1055. BOOL
  1056. CVolumeManager::ReopenVolumeHandles()
  1057. {
  1058. BOOL fAllOk = TRUE;
  1059. // If there's another thread already executing this routine, we'll just skip out.
  1060. // This is a reasonable idea, but really shouldn't be necessary; the volumes
  1061. // can protect themselves, and one thread should basically noop. However,
  1062. // there was an iostress break where these two threads got each other into
  1063. // a deadly embrace. This was due to the fact that the win32 thread pool
  1064. // has the tendency to put multiple IO work items on the same thread (since
  1065. // it queues to IO threads using APCs). So as a workaround, don't run
  1066. // this method more than once at a time.
  1067. if( !BeginSingleInstanceTask( &_cReopenVolumeHandles ))
  1068. {
  1069. TrkLog(( TRKDBG_VOLUME, TEXT("Skipping ReopenVolumeHandles, another instance is already in progress") ));
  1070. return FALSE;
  1071. }
  1072. // Get a volume enumerator. After initialization, none of the volumes
  1073. // have yet been opened. Ordinarily the Enum method gives us nothing until
  1074. // they've been opened once. But this routine is the one that originally does
  1075. // the opens, so we need the enumerator to give us everything.
  1076. CVolumeEnumerator enumerator = Enum( ENUM_UNOPENED_VOLUMES );
  1077. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("\nAttempting to reopen all volume handles") ));
  1078. for( CVolume* pVol = enumerator.GetNextVolume();
  1079. pVol != NULL;
  1080. pVol = enumerator.GetNextVolume())
  1081. {
  1082. __try
  1083. {
  1084. if( !pVol->ReopenVolumeHandles() )
  1085. fAllOk = FALSE;
  1086. }
  1087. __except(BreakOnDebuggableException())
  1088. {
  1089. fAllOk = FALSE;
  1090. }
  1091. pVol->Release();
  1092. }
  1093. // Show that at least an attempt has been made to open all
  1094. // volume handles. This is checked in Enum().
  1095. _fVolumesHaveBeenOpenedOnce = TRUE;
  1096. // Show that we're done with this method.
  1097. EndSingleInstanceTask( &_cReopenVolumeHandles );
  1098. TrkLog(( TRKDBG_OBJID_DELETIONS,
  1099. fAllOk ? TEXT("All volume handles are open") : TEXT("Not all volume handles are open") ));
  1100. return( fAllOk );
  1101. }
  1102. void
  1103. CVolumeManager::VolumeDeviceEvent( HDEVNOTIFY hdnVolume, EVolumeDeviceEvent eVolumeDeviceEvent )
  1104. {
  1105. CVolumeEnumerator enumerator = Enum();
  1106. BOOL fAllOk = TRUE;
  1107. for( CVolume* pVol = enumerator.GetNextVolume();
  1108. pVol != NULL;
  1109. pVol = enumerator.GetNextVolume() )
  1110. {
  1111. __try
  1112. {
  1113. switch( eVolumeDeviceEvent )
  1114. {
  1115. case ON_VOLUME_LOCK:
  1116. pVol->OnVolumeLock( hdnVolume );
  1117. break;
  1118. case ON_VOLUME_UNLOCK:
  1119. pVol->OnVolumeUnlock( hdnVolume );
  1120. break;
  1121. case ON_VOLUME_LOCK_FAILED:
  1122. pVol->OnVolumeLockFailed( hdnVolume );
  1123. break;
  1124. case ON_VOLUME_MOUNT:
  1125. pVol->OnVolumeMount( hdnVolume );
  1126. break;
  1127. case ON_VOLUME_DISMOUNT:
  1128. pVol->OnVolumeDismount( hdnVolume );
  1129. break;
  1130. case ON_VOLUME_DISMOUNT_FAILED:
  1131. pVol->OnVolumeDismountFailed( hdnVolume );
  1132. break;
  1133. default:
  1134. TrkLog(( TRKDBG_ERROR, TEXT("Invalid event to OnVolumeDeviceEvent (%d)"),
  1135. eVolumeDeviceEvent ));
  1136. TrkAssert( !TEXT("Invalid event to OnVolumeDeviceEvent") );
  1137. break;
  1138. } // switch( eVolumeDeviceEvent )
  1139. }
  1140. __except(BreakOnDebuggableException())
  1141. {
  1142. }
  1143. pVol->Release();
  1144. }
  1145. return;
  1146. }
  1147. //+----------------------------------------------------------------------------
  1148. //
  1149. // CVolumeManager::OnEntriesAvailable
  1150. //
  1151. // The CLog calls this routine when it has new data available
  1152. // for us to read. We don't read it right away, but start the Notify timer.
  1153. // When it goes off, we'll upload all notifications to the DC that haven't
  1154. // yet been sent.
  1155. //
  1156. //+----------------------------------------------------------------------------
  1157. void
  1158. CVolumeManager::OnEntriesAvailable()
  1159. {
  1160. if( !_pTrkWksConfiguration->_fIsWorkgroup )
  1161. {
  1162. _timerNotify.SetSingleShot();
  1163. TrkLog(( TRKDBG_MOVE | TRKDBG_WKS,
  1164. TEXT("log called CVolumeManager::OnEntriesAvailable(), %s"),
  1165. (const TCHAR*)CDebugString(_timerNotify) ));
  1166. }
  1167. }
  1168. //+----------------------------------------------------------------------------
  1169. //
  1170. // CVolumeManager::ForceVolumeClaims
  1171. //
  1172. // Put all of the volumes in the not-owned state so that they will
  1173. // try to do a claim.
  1174. //
  1175. //+----------------------------------------------------------------------------
  1176. void
  1177. CVolumeManager::ForceVolumeClaims()
  1178. {
  1179. CVolumeEnumerator enumerator = Enum();
  1180. TrkLog(( TRKDBG_OBJID_DELETIONS, TEXT("Force volume claims") ));
  1181. for( CVolume* pVol = enumerator.GetNextVolume();
  1182. pVol != NULL;
  1183. pVol = enumerator.GetNextVolume())
  1184. {
  1185. __try
  1186. {
  1187. pVol->SetState(CVolume::VOL_STATE_NOTOWNED);
  1188. }
  1189. __except(BreakOnDebuggableException())
  1190. {
  1191. }
  1192. pVol->Release();
  1193. }
  1194. // Do a SyncVolumes so that the volumes can all send up a
  1195. // volume-claim request. If it fails, just start the volinit
  1196. // timer to have it called again later.
  1197. if( !SyncVolumes( AGGRESSIVE )) // Doesn't raise
  1198. SetVolInitTimer();
  1199. }
  1200. PTimerCallback::TimerContinuation
  1201. CVolumeManager::OnVolumeTimer( DWORD dwTimerId )
  1202. {
  1203. ULONG ulTimerResetPeriod = 0;
  1204. BOOL fTimerRetry = FALSE;
  1205. CNewTimer * ptimer;
  1206. PTimerCallback::TimerContinuation continuation = CONTINUE_TIMER;
  1207. __try
  1208. {
  1209. switch( static_cast<VOLTIMERID>(dwTimerId) )
  1210. {
  1211. // Initialization tasks
  1212. // This timer is started during initialization, and then usually stops after
  1213. // one iteration. It can be restarted, however, if a MoveNotify gets a
  1214. // TRK_E_SERVER_TOO_BUSY error.
  1215. case VOLTIMER_INIT:
  1216. TrkLog(( TRKDBG_VOLUME, TEXT("VolInit timer has fired") ));
  1217. ptimer = &_timerVolumeInit;
  1218. TrkAssert( CNewTimer::RETRY_RANDOMLY == ptimer->GetRetryType() );
  1219. TrkAssert( !ptimer->IsRecurring() );
  1220. // Create/claim volumes as necessary.
  1221. fTimerRetry = TRUE;
  1222. if( SyncVolumes( PASSIVE,
  1223. _timerVolumeInit.QueryOriginalDueTime(),
  1224. _timerVolumeInit.QueryPeriodInSeconds() )) // Doesn't raise
  1225. fTimerRetry = FALSE;
  1226. // If we have a new volid, make the existing object IDs reborn.
  1227. CleanUpOids();
  1228. // Give each of the volumes an opportunity to upload any pending
  1229. // MoveNotifies.
  1230. // BUGBUG: Move the MoveBatchTimeout controlling code into CVolumeManager,
  1231. // so that all such control is in one place.
  1232. __try
  1233. {
  1234. continuation = g_ptrkwks->OnMoveBatchTimeout();
  1235. }
  1236. __except( BreakOnDebuggableException() )
  1237. {
  1238. TrkAssert( TRK_E_SERVER_TOO_BUSY == GetExceptionCode() );
  1239. TrkLog(( TRKDBG_ERROR, TEXT("VolInit timer caught %08x during MoveNotify"), GetExceptionCode() ));
  1240. fTimerRetry = TRUE;
  1241. }
  1242. break;
  1243. // Frequent tasks (i.e. ~daily)
  1244. case VOLTIMER_FREQUENT:
  1245. ptimer = &_timerFrequentTasks;
  1246. if( _fFrequentTaskHesitation )
  1247. {
  1248. TrkLog(( TRKDBG_VOLUME, TEXT("Executing frequent tasks") ));
  1249. _fFrequentTaskHesitation = FALSE;
  1250. ulTimerResetPeriod = _pTrkWksConfiguration->GetVolFrequentTasksPeriod() + 1;
  1251. SyncVolumes( PASSIVE ); // Doesn't raise
  1252. ReopenVolumeHandles();
  1253. }
  1254. else
  1255. {
  1256. // Delay a random number of seconds within an interval
  1257. _fFrequentTaskHesitation = TRUE;
  1258. ulTimerResetPeriod = QuasiRandomDword()
  1259. %
  1260. (1+_pTrkWksConfiguration->GetVolPeriodicTasksHesitation());
  1261. ulTimerResetPeriod++;
  1262. TrkLog(( TRKDBG_VOLUME, TEXT("Hesitating %d seconds before executing frequent tasks"), ulTimerResetPeriod ));
  1263. }
  1264. break;
  1265. // Infrequent tasks (i.e. ~weekly)
  1266. case VOLTIMER_INFREQUENT:
  1267. ptimer = &_timerInfrequentTasks;
  1268. if( _fInfrequentTaskHesitation )
  1269. {
  1270. TrkLog(( TRKDBG_VOLUME, TEXT("Executing infrequent tasks") ));
  1271. _fInfrequentTaskHesitation = FALSE;
  1272. ulTimerResetPeriod = _pTrkWksConfiguration->GetVolInfrequentTasksPeriod() + 1;
  1273. CheckSequenceNumbers();
  1274. // Be aggressive about the sync; if we have not-created volumes, try
  1275. // to create them again even if the last time we got a vol-quota-exceeded
  1276. // error.
  1277. SyncVolumes( AGGRESSIVE ); // Doesn't raise
  1278. // Give each of the volumes an opportunity to upload any pending
  1279. // MoveNotifies. Again, be aggressive about it in the face of previous
  1280. // quota errors.
  1281. g_ptrkwks->OnMoveBatchTimeout( AGGRESSIVE );
  1282. }
  1283. else
  1284. {
  1285. // Delay a random number of seconds within an interval
  1286. _fInfrequentTaskHesitation = TRUE;
  1287. ulTimerResetPeriod = QuasiRandomDword()
  1288. %
  1289. (1+_pTrkWksConfiguration->GetVolPeriodicTasksHesitation());
  1290. ulTimerResetPeriod++;
  1291. TrkLog(( TRKDBG_VOLUME, TEXT("Hesitating %d seconds before executing infrequent tasks"), ulTimerResetPeriod ));
  1292. }
  1293. break;
  1294. default:
  1295. TrkAssert( FALSE && TEXT("Invalid timerID in CVolumeManager::Timer") );
  1296. break;
  1297. } // switch
  1298. }
  1299. __except( BreakOnDebuggableException() )
  1300. {
  1301. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CVolumeManager::OnVolumeTimer %08x"), GetExceptionCode() ));
  1302. }
  1303. if( fTimerRetry )
  1304. continuation = RETRY_TIMER;
  1305. else if( 0 != ulTimerResetPeriod )
  1306. {
  1307. ptimer->ReInitialize( ulTimerResetPeriod );
  1308. ptimer->SetSingleShot();
  1309. continuation = CONTINUE_TIMER;
  1310. }
  1311. return( continuation );
  1312. }
  1313. ULONG
  1314. CVolumeManager::GetVolumeIds( CVolumeId * pVolumeIds, ULONG cMax )
  1315. {
  1316. CVolumeEnumerator enumerator = Enum();
  1317. CVolume* pvol = enumerator.GetNextVolume();
  1318. ULONG cVolumes;
  1319. CVolumeId volidZero;
  1320. __try
  1321. {
  1322. for( cVolumes = 0;
  1323. cVolumes < cMax && NULL != pvol;
  1324. pvol = enumerator.GetNextVolume()
  1325. )
  1326. {
  1327. *pVolumeIds = pvol->GetVolumeId();
  1328. if (*pVolumeIds != volidZero)
  1329. {
  1330. pVolumeIds ++;
  1331. cVolumes ++;
  1332. }
  1333. pvol->Release();
  1334. pvol = NULL;
  1335. }
  1336. }
  1337. __finally
  1338. {
  1339. enumerator.UnInitialize();
  1340. if(pvol)
  1341. {
  1342. pvol->Release();
  1343. pvol = NULL;
  1344. }
  1345. }
  1346. return(cVolumes);
  1347. }
  1348. HRESULT
  1349. CVolumeManager::OnRestore()
  1350. {
  1351. return( E_NOTIMPL );
  1352. #if 0
  1353. CVolumeEnumerator enumerator = Enum();
  1354. CVolume* pvol = enumerator.GetNextVolume();
  1355. HRESULT hr = S_OK;
  1356. HRESULT hrRet = S_OK;
  1357. __try
  1358. {
  1359. for(; NULL != pvol; pvol = enumerator.GetNextVolume())
  1360. {
  1361. hr = pvol->OnRestore();
  1362. if(hr != S_OK && hrRet == S_OK)
  1363. // Return the hr from the first failed volume.
  1364. {
  1365. hrRet = hr;
  1366. }
  1367. pvol->Release();
  1368. pvol = NULL;
  1369. }
  1370. }
  1371. __finally
  1372. {
  1373. enumerator.UnInitialize();
  1374. if(pvol)
  1375. {
  1376. pvol->Release();
  1377. pvol = NULL;
  1378. }
  1379. }
  1380. return hr;
  1381. #endif // #if 0
  1382. }
  1383. BOOL
  1384. CVolumeManager::CheckSequenceNumbers()
  1385. {
  1386. BOOL fSuccess = FALSE;
  1387. HRESULT hr = S_OK;
  1388. CAvailableDc adc;
  1389. CVolume* rgVolsToCheck[ NUM_VOLUMES ];
  1390. ULONG cVolumes = 0;
  1391. __try
  1392. {
  1393. TRKSVR_SYNC_VOLUME rgQueryVolumes[ NUM_VOLUMES ];
  1394. ULONG v;
  1395. const CVolumeId volNULL;
  1396. CVolumeEnumerator enumerator = Enum();
  1397. CVolume* cvolCur = enumerator.GetNextVolume();
  1398. for(; cvolCur != NULL; cvolCur = enumerator.GetNextVolume())
  1399. {
  1400. rgVolsToCheck[cVolumes] = cvolCur;
  1401. if(cvolCur->LoadQueryVolume(&rgQueryVolumes[cVolumes]))
  1402. {
  1403. cVolumes++;
  1404. }
  1405. else
  1406. {
  1407. cvolCur->Release();
  1408. }
  1409. }
  1410. if( 0 != cVolumes )
  1411. {
  1412. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE, TEXT("Verifying sequence numbers on %d volumes"), cVolumes ));
  1413. _pTrkWks->CallDcSyncVolumes(cVolumes, rgQueryVolumes);
  1414. for( v = 0; v < cVolumes; v++ )
  1415. {
  1416. if( rgVolsToCheck[v]->UnloadQueryVolume( &rgQueryVolumes[v] )
  1417. &&
  1418. S_OK == rgQueryVolumes[ v ].hr )
  1419. {
  1420. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  1421. TEXT("Seq number for volume %c should be %d"),
  1422. 'A'+rgVolsToCheck[v]->GetIndex(),
  1423. rgQueryVolumes[v].seq ));
  1424. }
  1425. else
  1426. {
  1427. TrkLog(( TRKDBG_ERROR,
  1428. TEXT("Couldn't verify the seq number on vol %c"),
  1429. 'A'+rgVolsToCheck[v]->GetIndex() ));
  1430. }
  1431. } // for( v = 0; v < cVolumes; v++ )
  1432. fSuccess = TRUE;
  1433. } // if( cVolumes != 0 )
  1434. }
  1435. __except( BreakOnDebuggableException() )
  1436. {
  1437. TrkLog(( TRKDBG_VOLUME, TEXT("Couldn't check sequence numbers against server"), GetExceptionCode() ));
  1438. }
  1439. for( ULONG v = 0; v < cVolumes; v++ )
  1440. {
  1441. rgVolsToCheck[v]->Release();
  1442. } // for( v = 0; v < cVolumes; v++ )
  1443. return( fSuccess );
  1444. }
  1445. void
  1446. CVolumeManager::SetReopenVolumeHandlesTimer()
  1447. {
  1448. CFILETIME ft;
  1449. // Start the timer (if it's not already running).
  1450. _timerObjIdIndexReopen.SetRecurring();
  1451. TrkLog(( TRKDBG_VOLUME, TEXT("ReOpen timer: %s"),
  1452. (const TCHAR*)CDebugString(_timerObjIdIndexReopen) ));
  1453. }
  1454. CVolumeEnumerator
  1455. CVolumeManager::Enum( EEnumType eEnumType )
  1456. {
  1457. CVolumeEnumerator volenum;
  1458. if( _fVolumesHaveBeenOpenedOnce
  1459. ||
  1460. _fInitialized
  1461. &&
  1462. ENUM_UNOPENED_VOLUMES == eEnumType )
  1463. {
  1464. volenum = CVolumeEnumerator( &_pVolumeNodeListHead, &_csVolumeNodeList );
  1465. }
  1466. return( volenum );
  1467. }
  1468. CVolume *
  1469. CVolumeManager::FindVolume( const CVolumeId &vol )
  1470. {
  1471. CVolumeEnumerator enumerator = Enum();
  1472. CVolume *pvol = enumerator.GetNextVolume();
  1473. for(; pvol != NULL; pvol = enumerator.GetNextVolume())
  1474. {
  1475. if( pvol->GetVolumeId() == vol )
  1476. {
  1477. break;
  1478. }
  1479. pvol->Release();
  1480. } // for(; pvol != NULL; pvol = enumerator.GetNextVolume())
  1481. return( pvol );
  1482. }
  1483. //+----------------------------------------------------------------------------
  1484. //
  1485. // CVolumeManager::FlushAllVolumes
  1486. //
  1487. // Flush all of the volumes.
  1488. //
  1489. //+----------------------------------------------------------------------------
  1490. void
  1491. CVolumeManager::FlushAllVolumes( BOOL fServiceShutdown )
  1492. {
  1493. CVolumeEnumerator enumerator = Enum();
  1494. CVolume *pvol = enumerator.GetNextVolume();
  1495. for(; pvol != NULL; pvol = enumerator.GetNextVolume())
  1496. {
  1497. __try
  1498. {
  1499. pvol->Flush( fServiceShutdown );
  1500. }
  1501. __except( BreakOnDebuggableException() )
  1502. {
  1503. }
  1504. pvol->Release();
  1505. }
  1506. }
  1507. //+----------------------------------------------------------------------------
  1508. //
  1509. // CVolumeManager::FindVolume
  1510. //
  1511. // Find a volume in the linked-list, given it's device name,
  1512. // and return its CVolume*.
  1513. //
  1514. //+----------------------------------------------------------------------------
  1515. CVolume *
  1516. CVolumeManager::FindVolume( const TCHAR *ptszVolumeDeviceName )
  1517. {
  1518. CVolumeEnumerator enumerator = Enum();
  1519. CVolume *pvol = enumerator.GetNextVolume();
  1520. for(; pvol != NULL; pvol = enumerator.GetNextVolume())
  1521. {
  1522. if( 0 == _tcscmp( pvol->GetVolumeDeviceName(), ptszVolumeDeviceName ))
  1523. {
  1524. break;
  1525. }
  1526. pvol->Release();
  1527. } // for(; pvol != NULL; pvol = enumerator.GetNextVolume())
  1528. return( pvol );
  1529. }
  1530. //+----------------------------------------------------------------------------
  1531. //
  1532. // CVolumeManager::IsDuplicatevolId
  1533. //
  1534. // Check to see if there is a volume, aside from the caller, that
  1535. // has a particular volume ID. This should never happen, so this
  1536. // method allows a volume to check for it and respond appropriately.
  1537. //
  1538. // When checking another volume's volid, we can't take its lock.
  1539. // See the description in CVolume::GetVolumeId.
  1540. //
  1541. //+----------------------------------------------------------------------------
  1542. CVolume *
  1543. CVolumeManager::IsDuplicateVolId( CVolume *pvolCheck, const CVolumeId &volid )
  1544. {
  1545. CVolumeNode *pVolNode = NULL;
  1546. CVolume *pvol = NULL;
  1547. _csVolumeNodeList.Enter();
  1548. __try
  1549. {
  1550. pVolNode = _pVolumeNodeListHead;
  1551. while( NULL != pVolNode )
  1552. {
  1553. if( NULL != pVolNode->_pVolume
  1554. &&
  1555. volid == pVolNode->_pVolume->GetVolumeId() // Doesn't take lock.
  1556. &&
  1557. pvolCheck != pVolNode->_pVolume )
  1558. {
  1559. pvol = pVolNode->_pVolume;
  1560. pvol->AddRef();
  1561. __leave;
  1562. }
  1563. pVolNode = pVolNode->_pNext;
  1564. TrkAssert( pVolNode != _pVolumeNodeListHead );
  1565. }
  1566. }
  1567. __except( BreakOnDebuggableException() )
  1568. {
  1569. TrkLog(( TRKDBG_WARNING, TEXT("Ignoring exception in IsDuplicateVolId") ));
  1570. }
  1571. _csVolumeNodeList.Leave();
  1572. return pvol;
  1573. }
  1574. //+----------------------------------------------------------------------------
  1575. //
  1576. // CVolumeManager::IsLocal
  1577. //
  1578. // Determine if a given volume ID represents a local volume. Note that
  1579. // if it's not in the linked list of volumes, we'll return false, though
  1580. // the volume may in fact exist on the system (since we don't respond
  1581. // to new volumes after service start).
  1582. //
  1583. //+----------------------------------------------------------------------------
  1584. BOOL
  1585. CVolumeManager::IsLocal( const CVolumeId &vol )
  1586. {
  1587. CVolume *pvol = FindVolume( vol );
  1588. if( NULL == pvol )
  1589. return FALSE;
  1590. else
  1591. {
  1592. pvol->Release();
  1593. return( TRUE );
  1594. }
  1595. }
  1596. //+----------------------------------------------------------------------------
  1597. //
  1598. // CVolumeManager::Seek
  1599. //
  1600. // Seek the specified volume's log to the specified sequence number.
  1601. //
  1602. //+----------------------------------------------------------------------------
  1603. void
  1604. CVolumeManager::Seek( CVolumeId vol, SequenceNumber seq )
  1605. {
  1606. CVolumeEnumerator enumerator = Enum();
  1607. CVolume* cvolCur = enumerator.GetNextVolume();
  1608. for(; cvolCur != NULL; cvolCur = enumerator.GetNextVolume())
  1609. {
  1610. if( cvolCur->GetVolumeId() == vol )
  1611. {
  1612. cvolCur->Seek( seq );
  1613. cvolCur->Release();
  1614. return;
  1615. }
  1616. cvolCur->Release();
  1617. } // for( ULONG i = 0; i < 26; i++ )
  1618. }
  1619. //+----------------------------------------------------------------------------
  1620. //
  1621. // CVolumeEnumerator::GetNextVolume
  1622. //
  1623. // Get the next CVolume* in the enumeration.
  1624. //
  1625. //+----------------------------------------------------------------------------
  1626. CVolume *
  1627. CVolumeEnumerator::GetNextVolume()
  1628. {
  1629. CVolume *pVol = NULL;
  1630. if( NULL == _ppVolumeNodeListHead )
  1631. return( NULL );
  1632. TrkAssert( NULL != _pcs );
  1633. _pcs->Enter();
  1634. __try
  1635. {
  1636. if( NULL == *_ppVolumeNodeListHead )
  1637. {
  1638. // There are no volumes in the list
  1639. pVol = NULL;
  1640. }
  1641. else if( NULL == _pVolNodeLast )
  1642. {
  1643. // This is a new enumeration. Pass back the first volume
  1644. pVol = (*_ppVolumeNodeListHead)->_pVolume;
  1645. _pVolNodeLast = *_ppVolumeNodeListHead;
  1646. }
  1647. else
  1648. {
  1649. // Find the next volume in the list, the one that's
  1650. // just beyond _pVolNodeLast.
  1651. // If we terminate this while loop because pVolNode goes to
  1652. // NULL, it means that there are no more volumes left to
  1653. // enumerate.
  1654. CVolumeNode *pVolNode = *_ppVolumeNodeListHead;
  1655. while( NULL != pVolNode )
  1656. {
  1657. if( pVolNode > _pVolNodeLast )
  1658. {
  1659. pVol = pVolNode->_pVolume;
  1660. _pVolNodeLast = pVolNode;
  1661. break;
  1662. }
  1663. pVolNode = pVolNode->_pNext;
  1664. }
  1665. }
  1666. if( NULL != pVol )
  1667. pVol->AddRef();
  1668. }
  1669. __except( EXCEPTION_EXECUTE_HANDLER )
  1670. {
  1671. TrkAssert( !TEXT("Unexpected exception in GetNextVolume") );
  1672. }
  1673. _pcs->Leave();
  1674. return( pVol );
  1675. }
  1676. //+----------------------------------------------------------------------------
  1677. //
  1678. // CVolumeManager::DcCallback
  1679. // StubLnkSvrMessageCallback
  1680. //
  1681. // When we RPC to trksvr to do a create volume (in the SyncVolumes method),
  1682. // trksvr does an RPC callback on that connection to StubLnkSvrMessageCallback,
  1683. // which in turn calls the DcCallback method. This was done so that
  1684. // we can verify that the volid actually gets to the volume before taking
  1685. // the hit of writing it into the DS. (At one point, the request to trksvr
  1686. // was being received, the entry was being put into the DS, but then the
  1687. // response back to trkwks was getting an RPC error, so trkwks would retry
  1688. // the create, etc.
  1689. //
  1690. //+----------------------------------------------------------------------------
  1691. HRESULT CVolumeManager::DcCallback(ULONG cVolumes, TRKSVR_SYNC_VOLUME* rgVolumes)
  1692. {
  1693. HRESULT hr = S_OK;
  1694. BOOL fSuccess = TRUE;
  1695. TrkLog((TRKDBG_VOLUME, TEXT("Dc Callback with %d volumes"), cVolumes ));
  1696. for( ULONG v = 0; v < cVolumes; v++ )
  1697. {
  1698. if( _rgVolumesToUpdate[v]->UnloadSyncVolume( &rgVolumes[v] )
  1699. &&
  1700. rgVolumes[v].hr == S_OK )
  1701. {
  1702. TrkLog(( TRKDBG_VOLUME | TRKDBG_MOVE,
  1703. TEXT("Volume %c successfully synced with server"),
  1704. 'A'+_rgVolumesToUpdate[v]->GetIndex() ));
  1705. }
  1706. else
  1707. {
  1708. fSuccess = FALSE;
  1709. TrkLog(( TRKDBG_ERROR | TRKDBG_MOVE,
  1710. TEXT("Couldn't sync vol %c with server (%08x, %s)"),
  1711. 'A'+_rgVolumesToUpdate[v]->GetIndex(),
  1712. rgVolumes[v].hr, GetErrorString(rgVolumes[v].hr) ));
  1713. }
  1714. } // for( v = 0; v < cVolumes; v++ )
  1715. if( S_OK == hr )
  1716. return fSuccess ? S_OK : TRK_S_VOLUME_NOT_SYNCED;
  1717. else
  1718. return hr;
  1719. }
  1720. // DC callback function. When calling CAvailableDc::CallAvailableDc, DC will callback to the
  1721. // trkwks service to set the volume ids on the volumes.
  1722. HRESULT StubLnkSvrMessageCallback(TRKSVR_MESSAGE_UNION* pMsg)
  1723. {
  1724. return g_ptrkwks->_volumes.DcCallback(pMsg->SyncVolumes.cVolumes, pMsg->SyncVolumes.pVolumes);
  1725. }